Skip to content

Commit bc994a6

Browse files
committed
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 e0b8084 commit bc994a6

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-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

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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 names(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; depends on RFC 5801
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; depends on RFC 5587
102+
103+
:requires-ext:`rfc5587`
104+
"""
105+
return self._attrs.known_mech_attrs
106+
107+
@property
108+
def attrs(self):
109+
"""
110+
Get the attributes of the mechanism; depends on RFC 5587
111+
112+
:requires-ext:`rfc5587`
113+
"""
114+
return self._attrs.mech_attrs
115+
116+
@classmethod
117+
def all_mechs(cls):
118+
"""
119+
all_mechs()
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+
from_name(name)
128+
Get a generator of mechanisms that may be able to process the name
129+
130+
Args:
131+
name (Name): a name to inquire about
132+
133+
Returns:
134+
[Mechanism]: a set of mechanisms which support this name
135+
136+
Raises:
137+
GSSError
138+
"""
139+
return (cls(mech) for mech in rmisc.inquire_mechs_for_name(name))
140+
141+
@classmethod
142+
def from_sasl_name(cls, name=None):
143+
"""
144+
from_sasl_name(name)
145+
Create a Mechanism from its SASL name
146+
147+
Args:
148+
name (str): SASL name of the desired mechanism
149+
150+
Returns:
151+
Mechanism: the desired mechanism
152+
153+
Raises:
154+
GSSError
155+
156+
:requires-ext:`rfc5801`
157+
"""
158+
if rfc5801 is None:
159+
raise NotImplementedError("Your GSSAPI implementation does not "
160+
"have support for RFC 5801")
161+
if isinstance(name, six.text_type):
162+
name = name.encode(_utils._get_encoding())
163+
164+
m = rfc5801.inquire_mech_for_saslname(name)
165+
166+
return cls(m)
167+
168+
@classmethod
169+
def from_attrs(cls, m_desired=None, m_except=None, m_critical=None):
170+
"""
171+
from_attrs
172+
Get a generator of mechanisms supporting the specified attributes. See
173+
RFC 5587's indicate_mechs_by_attrs for more information.
174+
175+
Args:
176+
m_desired ([OID]): Desired attributes
177+
m_except ([OID]): Except attributes
178+
m_critical ([OID]): Critical attributes
179+
180+
Returns:
181+
[Mechanism]: A set of mechanisms having the desired features.
182+
183+
Raises:
184+
GSSError
185+
186+
:requires-ext:`rfc5587`
187+
"""
188+
if isinstance(m_desired, roids.OID):
189+
m_desired = set([m_desired])
190+
if isinstance(m_except, roids.OID):
191+
m_except = set([m_except])
192+
if isinstance(m_critical, roids.OID):
193+
m_critical = set([m_critical])
194+
195+
if rfc5587 is None:
196+
raise NotImplementedError("Your GSSAPI implementation does not "
197+
"have support for RFC 5587")
198+
199+
mechs = rfc5587.indicate_mechs_by_attrs(m_desired,
200+
m_except,
201+
m_critical)
202+
return (cls(mech) for mech in mechs)

gssapi/tests/test_high_level.py

+55
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,60 @@ 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+
for mech in mechs:
384+
s = str(mech)
385+
s.shouldnt_be_empty()
386+
s.should_be_a(str)
387+
s.should_be(mech.name)
388+
389+
mech.sasl_name.shouldnt_be_empty()
390+
mech.sasl_name.should_be_a(six.text_type)
391+
392+
mech.description.shouldnt_be_empty()
393+
mech.description.should_be_a(six.text_type)
394+
395+
cmp_mech = gssmechs.Mechanism.from_sasl_name(mech.sasl_name)
396+
str(cmp_mech).should_be(str(mech))
397+
398+
@ktu.gssapi_extension_test('rfc5587', 'RFC 5587: Mech Inquiry')
399+
def test_mech_inquiry(self):
400+
mechs = list(gssmechs.Mechanism.all_mechs())
401+
c = len(mechs)
402+
for mech in mechs:
403+
attrs = mech.attrs
404+
known_attrs = mech.known_attrs
405+
406+
for attr in attrs:
407+
from_desired = gssmechs.Mechanism.from_attrs(m_desired=[attr])
408+
from_except = gssmechs.Mechanism.from_attrs(m_except=[attr])
409+
410+
from_desired = list(from_desired)
411+
from_except = list(from_except)
412+
413+
(len(from_desired) + len(from_except)).should_be(c)
414+
from_desired.should_include(mech)
415+
from_except.shouldnt_include(mech)
416+
417+
for attr in known_attrs:
418+
from_desired = gssmechs.Mechanism.from_attrs(m_desired=[attr])
419+
from_except = gssmechs.Mechanism.from_attrs(m_except=[attr])
420+
421+
from_desired = list(from_desired)
422+
from_except = list(from_except)
423+
424+
(len(from_desired) + len(from_except)).should_be(c)
425+
426+
372427
class NamesTestCase(_GSSAPIKerberosTestCase):
373428
def test_create_from_other(self):
374429
raw_name = gb.import_name(SERVICE_PRINCIPAL)

0 commit comments

Comments
 (0)