Skip to content

Commit fe6d5cc

Browse files
cipherboyDirectXMan12
authored andcommitted
Expose mechanisms in the high-level API
This creates a new class, Mechanism, for inquiring information about a mechanism. This includes support for RFCs 5587 and 5801. As Mechanism derives from OID, it is compatible with all places that accept a mech by OID. Signed-off-by: Alexander Scheel <[email protected]> Signed-off-by: Alexander Scheel <[email protected]>
1 parent 96fb80b commit fe6d5cc

File tree

3 files changed

+256
-0
lines changed

3 files changed

+256
-0
lines changed

gssapi/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@
3333
from gssapi.creds import Credentials # noqa
3434
from gssapi.names import Name # noqa
3535
from gssapi.sec_contexts import SecurityContext # noqa
36+
from gssapi.mechs import Mechanism # noqa
3637

3738
from gssapi._utils import set_encoding # noqa

gssapi/mechs.py

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import six
2+
3+
from gssapi.raw import oids as roids
4+
from gssapi._utils import import_gssapi_extension
5+
from gssapi.raw import misc as rmisc
6+
from gssapi import _utils
7+
8+
rfc5587 = import_gssapi_extension('rfc5587')
9+
rfc5801 = import_gssapi_extension('rfc5801')
10+
11+
12+
class Mechanism(roids.OID):
13+
"""
14+
A GSSAPI Mechanism
15+
16+
This class represents a mechanism and centralizes functions dealing with
17+
mechanisms and can be used with any calls.
18+
19+
It inherits from the low-level GSSAPI :class:`~gssapi.raw.oids.OID` class,
20+
and thus can be used with both low-level and high-level API calls.
21+
"""
22+
def __new__(cls, cpy=None, elements=None):
23+
return super(Mechanism, cls).__new__(cls, cpy, elements)
24+
25+
@property
26+
def name_types(self):
27+
"""
28+
Get the set of name types supported by this mechanism.
29+
"""
30+
return rmisc.inquire_names_for_mech(self)
31+
32+
@property
33+
def _saslname(self):
34+
if rfc5801 is None:
35+
raise NotImplementedError("Your GSSAPI implementation does not "
36+
"have support for RFC 5801")
37+
return rfc5801.inquire_saslname_for_mech(self)
38+
39+
@property
40+
def _attrs(self):
41+
if rfc5587 is None:
42+
raise NotImplementedError("Your GSSAPI implementation does not "
43+
"have support for RFC 5587")
44+
45+
return rfc5587.inquire_attrs_for_mech(self)
46+
47+
def __str__(self):
48+
if issubclass(str, six.text_type):
49+
# Python 3 -- we should return unicode
50+
return self._bytes_desc().decode(_utils._get_encoding())
51+
else:
52+
return self._bytes_desc()
53+
54+
def __unicode__(self):
55+
return self._bytes_desc().decode(_utils._get_encoding())
56+
57+
def _bytes_desc(self):
58+
base = self.dotted_form
59+
if rfc5801 is not None:
60+
base = self._saslname.mech_name
61+
62+
if isinstance(base, six.text_type):
63+
base = base.encode(_utils._get_encoding())
64+
65+
return base
66+
67+
def __repr__(self):
68+
"""
69+
Get a name representing the mechanism; always safe to call
70+
"""
71+
base = "<Mechanism (%s)>" % self.dotted_form
72+
if rfc5801 is not None:
73+
base = "<Mechanism %s (%s)>" % (
74+
self._saslname.mech_name.decode('UTF-8'),
75+
self.dotted_form
76+
)
77+
78+
return base
79+
80+
@property
81+
def sasl_name(self):
82+
"""
83+
Get the SASL name for the mechanism
84+
85+
:requires-ext:`rfc5801`
86+
"""
87+
return self._saslname.sasl_mech_name.decode('UTF-8')
88+
89+
@property
90+
def description(self):
91+
"""
92+
Get the description of the mechanism
93+
94+
:requires-ext:`rfc5801`
95+
"""
96+
return self._saslname.mech_description.decode('UTF-8')
97+
98+
@property
99+
def known_attrs(self):
100+
"""
101+
Get the known attributes of the mechanism; returns a set of OIDs
102+
([OID])
103+
104+
:requires-ext:`rfc5587`
105+
"""
106+
return self._attrs.known_mech_attrs
107+
108+
@property
109+
def attrs(self):
110+
"""
111+
Get the attributes of the mechanism; returns a set of OIDs ([OID])
112+
113+
:requires-ext:`rfc5587`
114+
"""
115+
return self._attrs.mech_attrs
116+
117+
@classmethod
118+
def all_mechs(cls):
119+
"""
120+
Get a generator of all mechanisms supported by GSSAPI
121+
"""
122+
return (cls(mech) for mech in rmisc.indicate_mechs())
123+
124+
@classmethod
125+
def from_name(cls, name=None):
126+
"""
127+
Get a generator of mechanisms that may be able to process the name
128+
129+
Args:
130+
name (Name): a name to inquire about
131+
132+
Returns:
133+
[Mechanism]: a set of mechanisms which support this name
134+
135+
Raises:
136+
GSSError
137+
"""
138+
return (cls(mech) for mech in rmisc.inquire_mechs_for_name(name))
139+
140+
@classmethod
141+
def from_sasl_name(cls, name=None):
142+
"""
143+
Create a Mechanism from its SASL name
144+
145+
Args:
146+
name (str): SASL name of the desired mechanism
147+
148+
Returns:
149+
Mechanism: the desired mechanism
150+
151+
Raises:
152+
GSSError
153+
154+
:requires-ext:`rfc5801`
155+
"""
156+
if rfc5801 is None:
157+
raise NotImplementedError("Your GSSAPI implementation does not "
158+
"have support for RFC 5801")
159+
if isinstance(name, six.text_type):
160+
name = name.encode(_utils._get_encoding())
161+
162+
m = rfc5801.inquire_mech_for_saslname(name)
163+
164+
return cls(m)
165+
166+
@classmethod
167+
def from_attrs(cls, m_desired=None, m_except=None, m_critical=None):
168+
"""
169+
Get a generator of mechanisms supporting the specified attributes. See
170+
RFC 5587's :func:`indicate_mechs_by_attrs` for more information.
171+
172+
Args:
173+
m_desired ([OID]): Desired attributes
174+
m_except ([OID]): Except attributes
175+
m_critical ([OID]): Critical attributes
176+
177+
Returns:
178+
[Mechanism]: A set of mechanisms having the desired features.
179+
180+
Raises:
181+
GSSError
182+
183+
:requires-ext:`rfc5587`
184+
"""
185+
if isinstance(m_desired, roids.OID):
186+
m_desired = set([m_desired])
187+
if isinstance(m_except, roids.OID):
188+
m_except = set([m_except])
189+
if isinstance(m_critical, roids.OID):
190+
m_critical = set([m_critical])
191+
192+
if rfc5587 is None:
193+
raise NotImplementedError("Your GSSAPI implementation does not "
194+
"have support for RFC 5587")
195+
196+
mechs = rfc5587.indicate_mechs_by_attrs(m_desired,
197+
m_except,
198+
m_critical)
199+
return (cls(mech) for mech in mechs)

gssapi/tests/test_high_level.py

+56
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from nose_parameterized import parameterized
1010

1111
from gssapi import creds as gsscreds
12+
from gssapi import mechs as gssmechs
1213
from gssapi import names as gssnames
1314
from gssapi import sec_contexts as gssctx
1415
from gssapi import raw as gb
@@ -369,6 +370,61 @@ def test_add_with_impersonate(self):
369370
new_creds.should_be_a(gsscreds.Credentials)
370371

371372

373+
class MechsTestCase(_GSSAPIKerberosTestCase):
374+
def test_indicate_mechs(self):
375+
mechs = gssmechs.Mechanism.all_mechs()
376+
for mech in mechs:
377+
s = str(mech)
378+
s.shouldnt_be_empty()
379+
380+
@ktu.gssapi_extension_test('rfc5801', 'RFC 5801: SASL Names')
381+
def test_sasl_properties(self):
382+
mechs = gssmechs.Mechanism.all_mechs()
383+
encoding = gssutils._get_encoding()
384+
for mech in mechs:
385+
s = str(mech)
386+
s.shouldnt_be_empty()
387+
s.should_be_a(str)
388+
s.should_be(mech._saslname.mech_name.decode(encoding))
389+
390+
mech.sasl_name.shouldnt_be_empty()
391+
mech.sasl_name.should_be_a(six.text_type)
392+
393+
mech.description.shouldnt_be_empty()
394+
mech.description.should_be_a(six.text_type)
395+
396+
cmp_mech = gssmechs.Mechanism.from_sasl_name(mech.sasl_name)
397+
str(cmp_mech).should_be(str(mech))
398+
399+
@ktu.gssapi_extension_test('rfc5587', 'RFC 5587: Mech Inquiry')
400+
def test_mech_inquiry(self):
401+
mechs = list(gssmechs.Mechanism.all_mechs())
402+
c = len(mechs)
403+
for mech in mechs:
404+
attrs = mech.attrs
405+
known_attrs = mech.known_attrs
406+
407+
for attr in attrs:
408+
from_desired = gssmechs.Mechanism.from_attrs(m_desired=[attr])
409+
from_except = gssmechs.Mechanism.from_attrs(m_except=[attr])
410+
411+
from_desired = list(from_desired)
412+
from_except = list(from_except)
413+
414+
(len(from_desired) + len(from_except)).should_be(c)
415+
from_desired.should_include(mech)
416+
from_except.shouldnt_include(mech)
417+
418+
for attr in known_attrs:
419+
from_desired = gssmechs.Mechanism.from_attrs(m_desired=[attr])
420+
from_except = gssmechs.Mechanism.from_attrs(m_except=[attr])
421+
422+
from_desired = list(from_desired)
423+
from_except = list(from_except)
424+
425+
(len(from_desired) + len(from_except)).should_be(c)
426+
427+
372428
class NamesTestCase(_GSSAPIKerberosTestCase):
373429
def test_create_from_other(self):
374430
raw_name = gb.import_name(SERVICE_PRINCIPAL)

0 commit comments

Comments
 (0)