codekingpro/portable-devtools
114k
1 2import typing as t3 4from gssapi.raw import names as rname5from gssapi.raw import NameType6from gssapi.raw import named_tuples as tuples7from gssapi.raw import oids as roids8from gssapi import _utils9 10from collections.abc import MutableMapping, Iterable11 12rname_rfc6680 = _utils.import_gssapi_extension('rfc6680')13rname_rfc6680_comp_oid = _utils.import_gssapi_extension('rfc6680_comp_oid')14 15 16class Name(rname.Name):17 """A GSSAPI Name18 19 This class represents a GSSAPI name which may be used with and/or returned20 by other GSSAPI methods.21 22 It inherits from the low-level GSSAPI :class:`~gssapi.raw.names.Name`23 class, and thus may used with both low-level and high-level API methods.24 25 This class may be pickled and unpickled, as well as copied.26 27 The :class:`str` and :class:`bytes` methods may be used to retrieve the28 text of the name.29 30 Note:31 Name strings will be automatically converted to and from unicode32 strings as appropriate. If a method is listed as returning a33 :class:`str` object, it will return a unicode string.34 35 The encoding used will be python-gssapi's current encoding, which36 defaults to UTF-8.37 """38 39 __slots__ = ('_attr_obj')40 41 def __new__(42 cls,43 base: t.Optional[t.Union[rname.Name, bytes, str]] = None,44 name_type: t.Optional[roids.OID] = None,45 token: t.Optional[bytes] = None,46 composite: bool = False,47 ) -> "Name":48 if token is not None:49 if composite:50 if rname_rfc6680 is None:51 raise NotImplementedError(52 "Your GSSAPI implementation does not support RFC 6680 "53 "(the GSSAPI naming extensions)")54 55 if rname_rfc6680_comp_oid is not None:56 base_name = rname.import_name(token,57 NameType.composite_export)58 displ_name = rname.display_name(base_name, name_type=True)59 if displ_name.name_type == NameType.composite_export:60 # NB(directxman12): there's a bug in MIT krb5 <= 1.1361 # where GSS_C_NT_COMPOSITE_EXPORT doesn't trigger62 # immediate import logic. However, we can just use63 # the normal GSS_C_NT_EXPORT_NAME in this case.64 base_name = rname.import_name(token, NameType.export)65 else:66 # NB(directxman12): some older versions of MIT krb5 don't67 # have support for the GSS_C_NT_COMPOSITE_EXPORT, but do68 # support composite tokens via GSS_C_NT_EXPORT_NAME.69 base_name = rname.import_name(token, NameType.export)70 else:71 base_name = rname.import_name(token, NameType.export)72 elif isinstance(base, rname.Name):73 base_name = base74 else:75 if isinstance(base, str):76 base = base.encode(_utils._get_encoding())77 78 base_name = rname.import_name(79 base, # type: ignore[arg-type]80 name_type)81 82 return t.cast("Name", super(Name, cls).__new__(cls, base_name))83 84 def __init__(85 self,86 base: t.Optional[t.Union[rname.Name, bytes, str]] = None,87 name_type: t.Optional[roids.OID] = None,88 token: t.Optional[bytes] = None,89 composite: bool = False,90 ) -> None:91 """92 The constructor can be used to "import" a name from a human readable93 representation, or from a token, and can also be used to convert a94 low-level :class:`gssapi.raw.names.Name` object into a high-level95 object.96 97 If a :class:`~gssapi.raw.names.Name` object from the low-level API98 is passed as the `base` argument, it will be converted into a99 high-level object.100 101 If the `token` argument is used, the name will be imported using102 the token. If the token was exported as a composite token,103 pass `composite=True`.104 105 Otherwise, a new name will be created, using the `base` argument as106 the human-readable string and the `name_type` argument to denote the107 name type.108 109 Raises:110 ~gssapi.exceptions.BadNameTypeError111 ~gssapi.exceptions.BadNameError112 ~gssapi.exceptions.BadMechanismError113 """114 115 self._attr_obj: t.Optional[_NameAttributeMapping]116 117 if rname_rfc6680 is not None:118 self._attr_obj = _NameAttributeMapping(self)119 else:120 self._attr_obj = None121 122 def __str__(self) -> str:123 return bytes(self).decode(_utils._get_encoding())124 125 def __unicode__(self) -> str:126 # Python 2 -- someone asked for unicode127 return self.__bytes__().decode(_utils._get_encoding())128 129 def __bytes__(self) -> bytes:130 # Python 3 -- someone asked for bytes131 return rname.display_name(self, name_type=False).name132 133 def display_as(134 self,135 name_type: roids.OID,136 ) -> str:137 """138 Display this name as the given name type.139 140 This method attempts to display the current :class:`Name`141 using the syntax of the given :class:`~gssapi.raw.types.NameType`, if142 possible.143 144 Warning:145 146 In MIT krb5 versions below 1.13.3, this method can segfault if147 the name was not *originally* created with a `name_type` that was148 not ``None`` (even in cases when a ``name_type``149 is later "added", such as via :meth:`canonicalize`).150 **Do not use this method unless you are sure the above151 conditions can never happen in your code.**152 153 Warning:154 155 In addition to the above warning, current versions of MIT krb5 do156 not actually fully implement this method, and it may return157 incorrect results in the case of canonicalized names.158 159 :requires-ext:`rfc6680`160 161 Args:162 name_type (~gssapi.OID): the :class:`~gssapi.raw.types.NameType` to163 use to display the given name164 165 Returns:166 str: the displayed name167 168 Raises:169 ~gssapi.exceptions.OperationUnavailableError170 """171 172 if rname_rfc6680 is None:173 raise NotImplementedError("Your GSSAPI implementation does not "174 "support RFC 6680 (the GSSAPI naming "175 "extensions)")176 return rname_rfc6680.display_name_ext(self, name_type).decode(177 _utils._get_encoding())178 179 @property180 def name_type(self) -> t.Optional[roids.OID]:181 """The :class:`~gssapi.raw.types.NameType` of this name"""182 return rname.display_name(self, name_type=True).name_type183 184 def __eq__(185 self,186 other: object,187 ) -> bool:188 if not isinstance(other, rname.Name):189 # maybe something else can compare this190 # to other classes, but we certainly can't191 return NotImplemented192 else:193 return rname.compare_name(self, other)194 195 def __ne__(196 self,197 other: object,198 ) -> bool:199 return not self.__eq__(other)200 201 def __repr__(self) -> str:202 disp_res = rname.display_name(self, name_type=True)203 return "Name({name!r}, {name_type})".format(204 name=disp_res.name, name_type=disp_res.name_type)205 206 def export(207 self,208 composite: bool = False,209 ) -> bytes:210 """Export this name as a token.211 212 This method exports the name into a byte string which can then be213 imported by using the `token` argument of the constructor.214 215 Args:216 composite (bool): whether or not use to a composite token --217 :requires-ext:`rfc6680`218 219 Returns:220 bytes: the exported name in token form221 222 Raises:223 ~gssapi.exceptions.MechanismNameRequiredError224 ~gssapi.exceptions.BadNameTypeError225 ~gssapi.exceptions.BadNameError226 """227 228 if composite:229 if rname_rfc6680 is None:230 raise NotImplementedError("Your GSSAPI implementation does "231 "not support RFC 6680 (the GSSAPI "232 "naming extensions)")233 234 return rname_rfc6680.export_name_composite(self)235 else:236 return rname.export_name(self)237 238 def canonicalize(239 self,240 mech: roids.OID241 ) -> "Name":242 """Canonicalize a name with respect to a mechanism.243 244 This method returns a new :class:`Name` that is canonicalized according245 to the given mechanism.246 247 Args:248 mech (~gssapi.OID): the :class:`MechType` to use249 250 Returns:251 Name: the canonicalized name252 253 Raises:254 ~gssapi.exceptions.BadMechanismError255 ~gssapi.exceptions.BadNameTypeError256 ~gssapi.exceptions.BadNameError257 """258 259 return type(self)(rname.canonicalize_name(self, mech))260 261 def __copy__(self) -> "Name":262 return type(self)(rname.duplicate_name(self))263 264 def __deepcopy__(265 self,266 memo: t.Dict,267 ) -> "Name":268 return type(self)(rname.duplicate_name(self))269 270 def _inquire(271 self,272 **kwargs: t.Any,273 ) -> tuples.InquireNameResult:274 """Inspect this name for information.275 276 This method inspects the name for information.277 278 If no keyword arguments are passed, all available information279 is returned. Otherwise, only the keyword arguments that280 are passed and set to `True` are returned.281 282 Args:283 mech_name (bool): get whether this is a mechanism name,284 and, if so, the associated mechanism285 attrs (bool): get the attributes names for this name286 287 Returns:288 InquireNameResult: the results of the inquiry, with unused289 fields set to None290 291 Raises:292 ~gssapi.exceptions.GSSError293 """294 295 if rname_rfc6680 is None:296 raise NotImplementedError("Your GSSAPI implementation does not "297 "support RFC 6680 (the GSSAPI naming "298 "extensions)")299 300 if not kwargs:301 default_val = True302 else:303 default_val = False304 305 attrs = kwargs.get('attrs', default_val)306 mech_name = kwargs.get('mech_name', default_val)307 308 return rname_rfc6680.inquire_name(self, mech_name=mech_name,309 attrs=attrs)310 311 @property312 def is_mech_name(self) -> bool:313 """Whether or not this name is a mechanism name314 (:requires-ext:`rfc6680`)315 """316 return self._inquire(mech_name=True).is_mech_name317 318 @property319 def mech(self) -> roids.OID:320 """The mechanism associated with this name (:requires-ext:`rfc6680`)321 """322 return self._inquire(mech_name=True).mech323 324 @property325 def attributes(self) -> t.Optional[MutableMapping]:326 """The attributes of this name (:requires-ext:`rfc6680`)327 328 The attributes are presenting in the form of a329 :class:`~collections.abc.MutableMapping` (a dict-like object).330 331 Retrieved values will always be in the form of :class:`frozenset`.332 333 When assigning values, if iterables are used, they be considered to be334 the set of values for the given attribute. If a non-iterable is used,335 it will be considered a single value, and automatically wrapped in an336 iterable.337 338 Note:339 String types (includes :class:`bytes`) are not considered to340 be iterables in this case.341 """342 if self._attr_obj is None:343 raise NotImplementedError("Your GSSAPI implementation does not "344 "support RFC 6680 (the GSSAPI naming "345 "extensions)")346 347 return self._attr_obj348 349 350class _NameAttributeMapping(MutableMapping):351 352 """Provides dict-like access to RFC 6680 Name attributes."""353 def __init__(354 self,355 name: Name,356 ) -> None:357 self._name = name358 359 def __getitem__(360 self,361 key: t.Union[bytes, str],362 ) -> tuples.GetNameAttributeResult:363 if isinstance(key, str):364 key = key.encode(_utils._get_encoding())365 366 res = rname_rfc6680.get_name_attribute( # type: ignore[union-attr]367 self._name, key)368 res = t.cast(tuples.GetNameAttributeResult, res)369 370 return tuples.GetNameAttributeResult(list(res.values),371 list(res.display_values),372 res.authenticated,373 res.complete)374 375 def __setitem__(376 self,377 key: t.Union[bytes, str],378 value: t.Union[379 tuples.GetNameAttributeResult, t.Tuple[bytes, bool], bytes380 ],381 ) -> None:382 if isinstance(key, str):383 key = key.encode(_utils._get_encoding())384 385 rname_rfc6680.delete_name_attribute( # type: ignore[union-attr]386 self._name, key)387 388 attr_value: t.List[bytes]389 if isinstance(value, tuples.GetNameAttributeResult):390 complete = value.complete391 attr_value = value.values392 elif isinstance(value, tuple) and len(value) == 2:393 complete = t.cast(bool, value[1])394 attr_value = [t.cast(bytes, value[0])]395 else:396 complete = False397 398 if (isinstance(value, (str, bytes)) or399 not isinstance(value, Iterable)):400 # NB(directxman12): this allows us to easily assign a single401 # value, since that's a common case402 attr_value = [value]403 404 rname_rfc6680.set_name_attribute( # type: ignore[union-attr]405 self._name, key, attr_value, complete=complete)406 407 def __delitem__(408 self,409 key: t.Union[bytes, str],410 ) -> None:411 if isinstance(key, str):412 key = key.encode(_utils._get_encoding())413 414 rname_rfc6680.delete_name_attribute( # type: ignore[union-attr]415 self._name, key)416 417 def __iter__(self) -> t.Iterator[bytes]:418 return iter(self._name._inquire(attrs=True).attrs)419 420 def __len__(self) -> int:421 return len(self._name._inquire(attrs=True).attrs)422 