Skip to content

Commit acc28ca

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 51817f5 commit acc28ca

File tree

4 files changed

+250
-0
lines changed

4 files changed

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

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

gssapi/raw/oids.pyx

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

+52
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)