Skip to content

Commit ac7776e

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 ac7776e

File tree

4 files changed

+261
-0
lines changed

4 files changed

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

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