Skip to content

Commit afd68bf

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]>
1 parent b1cb22b commit afd68bf

File tree

4 files changed

+278
-0
lines changed

4 files changed

+278
-0
lines changed

gssapi/__init__.py

Lines changed: 1 addition & 0 deletions
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

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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+
_sasl_name = None
23+
_mech_attrs = None
24+
25+
def __new__(cls, cpy=None, elements=None):
26+
return super(Mechanism, cls).__new__(cls, cpy, elements)
27+
28+
@property
29+
def names(self):
30+
"""
31+
Get the set of name types supported by the mechanism.
32+
"""
33+
return rmisc.inquire_names_for_mech(self)
34+
35+
@property
36+
def _query_saslname(self):
37+
if rfc5801 is None:
38+
raise NotImplementedError("Your GSSAPI implementation does not "
39+
"have support for RFC 5801")
40+
return rfc5801.inquire_saslname_for_mech(self)
41+
42+
@property
43+
def _query_attrs(self):
44+
if rfc5587 is None:
45+
raise NotImplementedError("Your GSSAPI implementation does not "
46+
"have support for RFC 5587")
47+
48+
return rfc5587.inquire_attrs_for_mech(self)
49+
50+
def __str__(self):
51+
if issubclass(str, six.text_type):
52+
# Python 3 -- we should return unicode
53+
return self.__bytes__().decode(_utils._get_encoding())
54+
else:
55+
return self.__bytes__()
56+
57+
def __unicode__(self):
58+
return self.__bytes__().decode(_utils._get_encoding())
59+
60+
def __bytes__(self):
61+
"""
62+
Get a name representing the mechanism; always safe to call
63+
"""
64+
base = self.dotted_form
65+
if rfc5801 is not None:
66+
base = self._query_saslname.mech_name
67+
68+
if issubclass(str, six.text_type):
69+
base = bytes(base, _utils._get_encoding())
70+
else:
71+
base = bytes(base)
72+
73+
return base
74+
75+
def __repr__(self):
76+
"""
77+
Get a name representing the mechanism; always safe to call
78+
"""
79+
base = self.dotted_form
80+
if rfc5801 is not None:
81+
base = "%s %s" % (self._query_saslname.mech_name.decode('UTF-8'),
82+
base)
83+
84+
return base
85+
86+
@property
87+
def sasl_name(self):
88+
"""
89+
Get the SASL name for the mechanism; depends on RFC 5801
90+
91+
:requires-ext:`rfc5801`
92+
"""
93+
return self._query_saslname.sasl_mech_name.decode('UTF-8')
94+
95+
@property
96+
def name(self):
97+
"""
98+
Get the name for the mechanism; depends on RFC 5801
99+
100+
:requires-ext:`rfc5801`
101+
"""
102+
self._query_saslname()
103+
return self._query_saslname.mech_name.decode('UTF-8')
104+
105+
@property
106+
def description(self):
107+
"""
108+
Get the description of the mechanism; depends on RFC 5801
109+
110+
:requires-ext:`rfc5801`
111+
"""
112+
self._query_saslname()
113+
return self._query_saslname.mech_description.decode('UTF-8')
114+
115+
@property
116+
def known_attrs(self):
117+
"""
118+
Get the known attributes of the mechanism; depends on RFC 5587
119+
120+
:requires-ext:`rfc5587`
121+
"""
122+
return self._query_attrs.known_mech_attrs
123+
124+
@property
125+
def attrs(self):
126+
"""
127+
Get the attributes of the mechanism; depends on RFC 5587
128+
129+
:requires-ext:`rfc5587`
130+
"""
131+
return self._query_attrs.mech_attrs
132+
133+
@classmethod
134+
def all_mechs(cls):
135+
"""
136+
all_mechs()
137+
Get a list of all mechanisms supported by GSSAPI
138+
"""
139+
mechs = rmisc.indicate_mechs()
140+
return [cls(mech) for mech in mechs]
141+
142+
@classmethod
143+
def from_name(cls, name=None):
144+
"""
145+
from_name(name)
146+
Get a set of mechanisms that may be able to process the name
147+
148+
Args:
149+
name (Name): a name to inquire about
150+
151+
Returns:
152+
[Mechanism]: a set of mechanisms which support this name
153+
154+
Raises:
155+
GSSError
156+
"""
157+
mechs = rmisc.inquire_mechs_for_name(name)
158+
return [cls(mech) for mech in mechs]
159+
160+
@classmethod
161+
def from_sasl_name(cls, name=None):
162+
"""
163+
from_sasl_name(name)
164+
Create a Mechanism from its SASL name
165+
166+
Args:
167+
name (str): SASL name of the desired mechanism
168+
169+
Returns:
170+
Mechanism: the desired mechanism
171+
172+
Raises:
173+
GSSError
174+
175+
:requires-ext:`rfc5801`
176+
"""
177+
if rfc5801 is None:
178+
raise NotImplementedError("Your GSSAPI implementation does not "
179+
"have support for RFC 5801")
180+
n = name
181+
if type(n) is not bytes:
182+
n = str(n).encode()
183+
184+
m = rfc5801.inquire_mech_for_saslname(n)
185+
186+
return cls(m)
187+
188+
@classmethod
189+
def from_attrs(cls, m_desired=None, m_except=None, m_critical=None):
190+
"""
191+
from_attrs
192+
Get the set of mechanisms supporting the specified attributes. See
193+
RFC 5587's indicate_mechs_by_attrs for more information.
194+
195+
Args:
196+
m_desired ([OID]): Desired attributes
197+
m_except ([OID]): Except attributes
198+
m_critical ([OID]): Critical attributes
199+
200+
Returns:
201+
[Mechanism]: A set of mechanisms having the desired features.
202+
203+
Raises:
204+
GSSError
205+
206+
:requires-ext:`rfc5587`
207+
"""
208+
if type(m_desired) == roids.OID:
209+
m_desired = set([m_desired])
210+
if type(m_except) == roids.OID:
211+
m_except = set([m_except])
212+
if type(m_critical) == roids.OID:
213+
m_critical = set([m_critical])
214+
215+
if rfc5587 is None:
216+
raise NotImplementedError("Your GSSAPI implementation does not "
217+
"have support for RFC 5587")
218+
219+
mechs = rfc5587.indicate_mechs_by_attrs(m_desired,
220+
m_except,
221+
m_critical)
222+
return [cls(mech) for mech in mechs]

gssapi/raw/oids.pyx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ cdef class OID:
6161
self._free_on_dealloc = True
6262
return 0
6363

64+
def _copy_oid(self, OID other):
65+
self._copy_from(other.raw_oid)
66+
6467
cdef int _from_bytes(OID self, object base) except -1:
6568
base_bytes = bytes(base)
6669
cdef char* byte_str = base_bytes

gssapi/tests/test_high_level.py

Lines changed: 52 additions & 0 deletions
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,57 @@ 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[0].shouldnt_be('<')
388+
s.should_be(mech.name)
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 = 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+
i = gssmechs.Mechanism.from_attrs(m_desired=[attr])
409+
e = gssmechs.Mechanism.from_attrs(m_except=[attr])
410+
411+
count = len(i) + len(e)
412+
count.should_be(c)
413+
i.should_include(mech)
414+
e.shouldnt_include(mech)
415+
416+
for attr in known_attrs:
417+
i = gssmechs.Mechanism.from_attrs(m_desired=[attr])
418+
e = gssmechs.Mechanism.from_attrs(m_except=[attr])
419+
420+
count = len(i) + len(e)
421+
count.should_be(c)
422+
423+
372424
class NamesTestCase(_GSSAPIKerberosTestCase):
373425
def test_create_from_other(self):
374426
raw_name = gb.import_name(SERVICE_PRINCIPAL)

0 commit comments

Comments
 (0)