Skip to content

Commit 9dc24ac

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 9dc24ac

File tree

4 files changed

+273
-0
lines changed

4 files changed

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