1
+ import collections
2
+
1
3
import six
2
4
3
5
from gssapi .raw import names as rname
4
6
from gssapi .raw import NameType
7
+ from gssapi .raw import named_tuples as tuples
5
8
from gssapi import _utils
6
9
10
+ rname_rfc6680 = _utils .import_gssapi_extension ('rfc6680' )
11
+ rname_rfc6680_comp_oid = _utils .import_gssapi_extension ('rfc6680_comp_oid' )
12
+
7
13
8
14
class Name (rname .Name ):
9
15
"""GSSAPI Name
@@ -20,9 +26,45 @@ class Name(rname.Name):
20
26
text of the name.
21
27
"""
22
28
23
- __slots__ = ()
29
+ __slots__ = ('_attr_obj' )
30
+
31
+ def __new__ (cls , base = None , name_type = None , token = None ,
32
+ composite = False ):
33
+ if token is not None :
34
+ if composite :
35
+ if rname_rfc6680 is None :
36
+ raise NotImplementedError (
37
+ "Your GSSAPI implementation does not support RFC 6680 "
38
+ "(the GSSAPI naming extensions)" )
39
+
40
+ if rname_rfc6680_comp_oid is not None :
41
+ base_name = rname .import_name (token ,
42
+ NameType .composite_export )
43
+ displ_name = rname .display_name (base_name , name_type = True )
44
+ if displ_name .name_type == NameType .composite_export :
45
+ # NB(directxman12): there's a bug in MIT krb5 <= 1.13
46
+ # where GSS_C_NT_COMPOSITE_EXPORT doesn't trigger
47
+ # immediate import logic. However, we can just use
48
+ # the normal GSS_C_NT_EXPORT_NAME in this case.
49
+ base_name = rname .import_name (token , NameType .export )
50
+ else :
51
+ # NB(directxman12): some older versions of MIT krb5 don't
52
+ # have support for the GSS_C_NT_COMPOSITE_EXPORT, but do
53
+ # support composite tokens via GSS_C_NT_EXPORT_NAME.
54
+ base_name = rname .import_name (token , NameType .export )
55
+ else :
56
+ base_name = rname .import_name (token , NameType .export )
57
+ elif isinstance (base , rname .Name ):
58
+ base_name = base
59
+ else :
60
+ if isinstance (base , six .text_type ):
61
+ base = base .encode (_utils ._get_encoding ())
62
+
63
+ base_name = rname .import_name (base , name_type )
24
64
25
- def __new__ (cls , base = None , name_type = None , token = None ):
65
+ return super (Name , cls ).__new__ (cls , base_name )
66
+
67
+ def __init__ (self , base = None , name_type = None , token = None , composite = False ):
26
68
"""Create or import a GSSAPI name
27
69
28
70
The constructor either creates or imports a GSSAPI name.
@@ -32,7 +74,8 @@ def __new__(cls, base=None, name_type=None, token=None):
32
74
high-level object.
33
75
34
76
If the `token` argument is used, the name will be imported using
35
- the token.
77
+ the token. If the token was exported as a composite token,
78
+ pass `composite=True`.
36
79
37
80
Otherwise, a new name will be created, using the `base` argument as
38
81
the string and the `name_type` argument to denote the name type.
@@ -43,17 +86,10 @@ def __new__(cls, base=None, name_type=None, token=None):
43
86
BadMechanismError
44
87
"""
45
88
46
- if token is not None :
47
- base_name = rname .import_name (token , NameType .export )
48
- elif isinstance (base , rname .Name ):
49
- base_name = base
89
+ if rname_rfc6680 is not None :
90
+ self ._attr_obj = _NameAttributeMapping (self )
50
91
else :
51
- if isinstance (base , six .text_type ):
52
- base = base .encode (_utils ._get_encoding ())
53
-
54
- base_name = rname .import_name (base , name_type )
55
-
56
- return super (Name , cls ).__new__ (cls , base_name )
92
+ self ._attr_obj = None
57
93
58
94
def __str__ (self ):
59
95
if issubclass (str , six .text_type ):
@@ -71,6 +107,30 @@ def __bytes__(self):
71
107
# Python 3 -- someone asked for bytes
72
108
return rname .display_name (self , name_type = False ).name
73
109
110
+ def display_as (self , name_type ):
111
+ """
112
+ Display the current name as the given name type.
113
+
114
+ This method attempts to display the current Name using
115
+ the syntax of the given NameType, if possible.
116
+
117
+ Args:
118
+ name_type (OID): the NameType to use to display the given name
119
+
120
+ Returns:
121
+ str: the displayed name
122
+
123
+ Raises:
124
+ OperationUnavailableError
125
+ """
126
+
127
+ if rname_rfc6680 is None :
128
+ raise NotImplementedError ("Your GSSAPI implementation does not "
129
+ "support RFC 6680 (the GSSAPI naming "
130
+ "extensions)" )
131
+ return rname_rfc6680 .display_name_ext (self , name_type ).encode (
132
+ _utils .get_encoding ())
133
+
74
134
@property
75
135
def name_type (self ):
76
136
"""Get the name type of this name"""
@@ -92,7 +152,7 @@ def __repr__(self):
92
152
return "Name({name}, {name_type})" .format (name = disp_res .name ,
93
153
name_type = disp_res .name_type )
94
154
95
- def export (self ):
155
+ def export (self , composite = False ):
96
156
"""Export the name
97
157
98
158
This method exports the name into a byte string which can then be
@@ -107,7 +167,15 @@ def export(self):
107
167
BadNameError
108
168
"""
109
169
110
- return rname .export_name (self )
170
+ if composite :
171
+ if rname_rfc6680 is None :
172
+ raise NotImplementedError ("Your GSSAPI implementation does "
173
+ "not support RFC 6680 (the GSSAPI "
174
+ "naming extensions)" )
175
+
176
+ return rname_rfc6680 .export_name_composite (self )
177
+ else :
178
+ return rname .export_name (self )
111
179
112
180
def canonicalize (self , mech ):
113
181
"""Canonicalize a name with respect to a mechanism
@@ -134,3 +202,111 @@ def __copy__(self):
134
202
135
203
def __deepcopy__ (self , memo ):
136
204
return type (self )(rname .duplicate_name (self ))
205
+
206
+ def _inquire (self , ** kwargs ):
207
+ """Inspect the name for information
208
+
209
+ This method inspects the name for information.
210
+
211
+ If no keyword arguments are passed, all available information
212
+ is returned. Otherwise, only the keyword arguments that
213
+ are passed and set to `True` are returned.
214
+
215
+ Args:
216
+ mech_name (bool): get whether this is a mechanism name,
217
+ and, if so, the associated mechanism
218
+ attrs (bool): get the attributes names for this name
219
+
220
+ Returns:
221
+ InquireNameResult: the results of the inquiry, with unused
222
+ fields set to None
223
+
224
+ Raises:
225
+ GSSError
226
+ """
227
+
228
+ if rname_rfc6680 is None :
229
+ raise NotImplementedError ("Your GSSAPI implementation does not "
230
+ "support RFC 6680 (the GSSAPI naming "
231
+ "extensions)" )
232
+
233
+ if not kwargs :
234
+ default_val = True
235
+ else :
236
+ default_val = False
237
+
238
+ attrs = kwargs .get ('attrs' , default_val )
239
+ mech_name = kwargs .get ('mech_name' , default_val )
240
+
241
+ return rname_rfc6680 .inquire_name (self , mech_name = mech_name ,
242
+ attrs = attrs )
243
+
244
+ @property
245
+ def is_mech_name (self ):
246
+ return self ._inquire (mech_name = True ).is_mech_name
247
+
248
+ @property
249
+ def mech (self ):
250
+ return self ._inquire (mech_name = True ).mech
251
+
252
+ @property
253
+ def attributes (self ):
254
+ if self ._attr_obj is None :
255
+ raise NotImplementedError ("Your GSSAPI implementation does not "
256
+ "support RFC 6680 (the GSSAPI naming "
257
+ "extensions)" )
258
+
259
+ return self ._attr_obj
260
+
261
+
262
+ class _NameAttributeMapping (collections .MutableMapping ):
263
+
264
+ """Provides dict-like access to RFC 6680 Name attributes."""
265
+ def __init__ (self , name ):
266
+ self ._name = name
267
+
268
+ def __getitem__ (self , key ):
269
+ if isinstance (key , six .text_type ):
270
+ key = key .encode (_utils ._get_encoding ())
271
+
272
+ res = rname_rfc6680 .get_name_attribute (self ._name , key )
273
+ return tuples .GetNameAttributeResult (frozenset (res .values ),
274
+ frozenset (res .display_values ),
275
+ res .authenticated ,
276
+ res .complete )
277
+
278
+ def __setitem__ (self , key , value ):
279
+ if isinstance (key , six .text_type ):
280
+ key = key .encode (_utils ._get_encoding ())
281
+
282
+ rname_rfc6680 .delete_name_attribute (self ._name , key )
283
+
284
+ if isinstance (value , tuples .GetNameAttributeResult ):
285
+ complete = value .complete
286
+ value = value .values
287
+ elif isinstance (value , tuple ) and len (value ) == 2 :
288
+ complete = value [1 ]
289
+ value = value [0 ]
290
+ else :
291
+ complete = False
292
+
293
+ if (isinstance (value , (six .string_types , bytes )) or
294
+ not isinstance (value , collections .Iterable )):
295
+ # NB(directxman12): this allows us to easily assign a single
296
+ # value, since that's a common case
297
+ value = [value ]
298
+
299
+ rname_rfc6680 .set_name_attribute (self ._name , key , value ,
300
+ complete = complete )
301
+
302
+ def __delitem__ (self , key ):
303
+ if isinstance (key , six .text_type ):
304
+ key = key .encode (_utils ._get_encoding ())
305
+
306
+ rname_rfc6680 .delete_name_attribute (self ._name , key )
307
+
308
+ def __iter__ (self ):
309
+ return iter (self ._name ._inquire (attrs = True ).attrs )
310
+
311
+ def __len__ (self ):
312
+ return len (self ._name ._inquire (attrs = True ).attrs )
0 commit comments