1+ import collections
2+
13import six
24
35from gssapi .raw import names as rname
46from gssapi .raw import NameType
7+ from gssapi .raw import named_tuples as tuples
58from gssapi import _utils
69
10+ rname_rfc6680 = _utils .import_gssapi_extension ('rfc6680' )
11+ rname_rfc6680_comp_oid = _utils .import_gssapi_extension ('rfc6680_comp_oid' )
12+
713
814class Name (rname .Name ):
915 """GSSAPI Name
@@ -20,9 +26,45 @@ class Name(rname.Name):
2026 text of the name.
2127 """
2228
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 )
2464
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 ):
2668 """Create or import a GSSAPI name
2769
2870 The constructor either creates or imports a GSSAPI name.
@@ -32,7 +74,8 @@ def __new__(cls, base=None, name_type=None, token=None):
3274 high-level object.
3375
3476 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`.
3679
3780 Otherwise, a new name will be created, using the `base` argument as
3881 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):
4386 BadMechanismError
4487 """
4588
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 )
5091 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
5793
5894 def __str__ (self ):
5995 if issubclass (str , six .text_type ):
@@ -71,6 +107,30 @@ def __bytes__(self):
71107 # Python 3 -- someone asked for bytes
72108 return rname .display_name (self , name_type = False ).name
73109
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+
74134 @property
75135 def name_type (self ):
76136 """Get the name type of this name"""
@@ -92,7 +152,7 @@ def __repr__(self):
92152 return "Name({name}, {name_type})" .format (name = disp_res .name ,
93153 name_type = disp_res .name_type )
94154
95- def export (self ):
155+ def export (self , composite = False ):
96156 """Export the name
97157
98158 This method exports the name into a byte string which can then be
@@ -107,7 +167,15 @@ def export(self):
107167 BadNameError
108168 """
109169
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 )
111179
112180 def canonicalize (self , mech ):
113181 """Canonicalize a name with respect to a mechanism
@@ -134,3 +202,111 @@ def __copy__(self):
134202
135203 def __deepcopy__ (self , memo ):
136204 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