Skip to content

Commit

Permalink
Expose mechanisms in the high-level API
Browse files Browse the repository at this point in the history
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 <ascheel@redhat.com>
  • Loading branch information
cipherboy committed Aug 11, 2017
1 parent 61ec201 commit e40a67f
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 0 deletions.
1 change: 1 addition & 0 deletions gssapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
from gssapi.creds import Credentials # noqa
from gssapi.names import Name # noqa
from gssapi.sec_contexts import SecurityContext # noqa
from gssapi.mechs import Mechanism # noqa

from gssapi._utils import set_encoding # noqa
205 changes: 205 additions & 0 deletions gssapi/mechs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import six

from gssapi.raw import oids as roids
from gssapi._utils import import_gssapi_extension
from gssapi.raw import misc as rmisc
from gssapi import _utils

rfc5587 = import_gssapi_extension('rfc5587')
rfc5801 = import_gssapi_extension('rfc5801')


class Mechanism(roids.OID):
"""
A GSSAPI Mechanism
This class represents a mechanism and centralizes functions dealing with
mechanisms and can be used with any calls.
It inherits from the low-level GSSAPI :class:`~gssapi.raw.oids.OID` class,
and thus can be used with both low-level and high-level API calls.
"""
def __new__(cls, cpy=None, elements=None):
return super(Mechanism, cls).__new__(cls, cpy, elements)

@property
def names(self):
"""
Get the set of name types supported by this mechanism.
"""
return rmisc.inquire_names_for_mech(self)

@property
def _saslname(self):
if rfc5801 is None:
raise NotImplementedError("Your GSSAPI implementation does not "
"have support for RFC 5801")
return rfc5801.inquire_saslname_for_mech(self)

@property
def _attrs(self):
if rfc5587 is None:
raise NotImplementedError("Your GSSAPI implementation does not "
"have support for RFC 5587")

return rfc5587.inquire_attrs_for_mech(self)

def __str__(self):
if issubclass(str, six.text_type):
# Python 3 -- we should return unicode
return self._bytes_desc().decode(_utils._get_encoding())
else:
return self._bytes_desc()

def __unicode__(self):
return self._bytes_desc().decode(_utils._get_encoding())

def _bytes_desc(self):
base = self.dotted_form
if rfc5801 is not None:
base = self._saslname.mech_name

if issubclass(str, six.text_type):
base = bytes(base, _utils._get_encoding())
else:
base = bytes(base)

return base

def __repr__(self):
"""
Get a name representing the mechanism; always safe to call
"""
base = "<Mechanism (%s)>" % self.dotted_form
if rfc5801 is not None:
base = "<Mechanism %s (%s)>" % (
self._saslname.mech_name.decode('UTF-8'),
self.dotted_form
)

return base

@property
def sasl_name(self):
"""
Get the SASL name for the mechanism; depends on RFC 5801
:requires-ext:`rfc5801`
"""
return self._saslname.sasl_mech_name.decode('UTF-8')

@property
def description(self):
"""
Get the description of the mechanism; depends on RFC 5801
:requires-ext:`rfc5801`
"""
return self._saslname.mech_description.decode('UTF-8')

@property
def known_attrs(self):
"""
Get the known attributes of the mechanism; depends on RFC 5587
:requires-ext:`rfc5587`
"""
return self._attrs.known_mech_attrs

@property
def attrs(self):
"""
Get the attributes of the mechanism; depends on RFC 5587
:requires-ext:`rfc5587`
"""
return self._attrs.mech_attrs

@classmethod
def all_mechs(cls):
"""
all_mechs()
Get a generator of all mechanisms supported by GSSAPI
"""
return (cls(mech) for mech in rmisc.indicate_mechs())

@classmethod
def from_name(cls, name=None):
"""
from_name(name)
Get a generator of mechanisms that may be able to process the name
Args:
name (Name): a name to inquire about
Returns:
[Mechanism]: a set of mechanisms which support this name
Raises:
GSSError
"""
return (cls(mech) for mech in rmisc.inquire_mechs_for_name(name))

@classmethod
def from_sasl_name(cls, name=None):
"""
from_sasl_name(name)
Create a Mechanism from its SASL name
Args:
name (str): SASL name of the desired mechanism
Returns:
Mechanism: the desired mechanism
Raises:
GSSError
:requires-ext:`rfc5801`
"""
if rfc5801 is None:
raise NotImplementedError("Your GSSAPI implementation does not "
"have support for RFC 5801")
n = name
if not isinstance(n, bytes):
n = str(n).encode()

m = rfc5801.inquire_mech_for_saslname(n)

return cls(m)

@classmethod
def from_attrs(cls, m_desired=None, m_except=None, m_critical=None):
"""
from_attrs
Get a generator of mechanisms supporting the specified attributes. See
RFC 5587's indicate_mechs_by_attrs for more information.
Args:
m_desired ([OID]): Desired attributes
m_except ([OID]): Except attributes
m_critical ([OID]): Critical attributes
Returns:
[Mechanism]: A set of mechanisms having the desired features.
Raises:
GSSError
:requires-ext:`rfc5587`
"""
if isinstance(m_desired, roids.OID):
m_desired = set([m_desired])
if isinstance(m_except, roids.OID):
m_except = set([m_except])
if isinstance(m_critical, roids.OID):
m_critical = set([m_critical])

if rfc5587 is None:
raise NotImplementedError("Your GSSAPI implementation does not "
"have support for RFC 5587")

mechs = rfc5587.indicate_mechs_by_attrs(m_desired,
m_except,
m_critical)
return (cls(mech) for mech in mechs)
3 changes: 3 additions & 0 deletions gssapi/raw/oids.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ cdef class OID:
self._free_on_dealloc = True
return 0

def _copy_oid(self, OID other):
self._copy_from(other.raw_oid)

cdef int _from_bytes(OID self, object base) except -1:
base_bytes = bytes(base)
cdef char* byte_str = base_bytes
Expand Down
52 changes: 52 additions & 0 deletions gssapi/tests/test_high_level.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from nose_parameterized import parameterized

from gssapi import creds as gsscreds
from gssapi import mechs as gssmechs
from gssapi import names as gssnames
from gssapi import sec_contexts as gssctx
from gssapi import raw as gb
Expand Down Expand Up @@ -369,6 +370,57 @@ def test_add_with_impersonate(self):
new_creds.should_be_a(gsscreds.Credentials)


class MechsTestCase(_GSSAPIKerberosTestCase):
def test_indicate_mechs(self):
mechs = gssmechs.Mechanism.all_mechs()
for mech in mechs:
s = str(mech)
s.shouldnt_be_empty()

@ktu.gssapi_extension_test('rfc5801', 'RFC 5801: SASL Names')
def test_sasl_properties(self):
mechs = gssmechs.Mechanism.all_mechs()
for mech in mechs:
s = str(mech)
s.shouldnt_be_empty()
s.should_be_a(str)
s[0].shouldnt_be('<')
s.should_be(mech.name)

mech.sasl_name.shouldnt_be_empty()
mech.sasl_name.should_be_a(six.text_type)

mech.description.shouldnt_be_empty()
mech.description.should_be_a(six.text_type)

cmp_mech = gssmechs.Mechanism.from_sasl_name(mech.sasl_name)
str(cmp_mech).should_be(str(mech))

@ktu.gssapi_extension_test('rfc5587', 'RFC 5587: Mech Inquiry')
def test_mech_inquiry(self):
mechs = list(gssmechs.Mechanism.all_mechs())
c = len(mechs)
for mech in mechs:
attrs = mech.attrs
known_attrs = mech.known_attrs

for attr in attrs:
i = list(gssmechs.Mechanism.from_attrs(m_desired=[attr]))
e = list(gssmechs.Mechanism.from_attrs(m_except=[attr]))

count = len(i) + len(e)
count.should_be(c)
i.should_include(mech)
e.shouldnt_include(mech)

for attr in known_attrs:
i = list(gssmechs.Mechanism.from_attrs(m_desired=[attr]))
e = list(gssmechs.Mechanism.from_attrs(m_except=[attr]))

count = len(i) + len(e)
count.should_be(c)


class NamesTestCase(_GSSAPIKerberosTestCase):
def test_create_from_other(self):
raw_name = gb.import_name(SERVICE_PRINCIPAL)
Expand Down

0 comments on commit e40a67f

Please sign in to comment.