Source code for flask_open_directory.model.model_abc
# -*- coding: utf-8 -*-
import abc
from typing import Dict, Union, Tuple, Any
import ldap3
[docs]class ModelABC(metaclass=abc.ABCMeta):
"""An abstract class with some default implementations for a model, which
maps the ``OpenDirectory (ldap attributes)`` to a python object.
Any of the methods that have a default implementation can be accessed on
a derived subclass via the ``super`` mechanism.
"""
_query_cn = None
@classmethod
@abc.abstractmethod
[docs] def ldap_attribute_map(cls) -> Dict[str, str]: # pragma: no cover
"""Return a mapping of <model attribute: ldap key> values.
This does not have a default implementation, and must be implemented
on the subclass.
"""
pass
@classmethod
[docs] def ldap_keys(cls) -> Tuple[str]:
"""Return all the ldap keys
This default implementation returns the ``values`` from the
:meth:`ldap_attribute_map`.
You can use the default implementation from the ``super`` mechanism in
your subclass.
"""
return tuple(cls.ldap_attribute_map().values())
@classmethod
[docs] def attribute_name_for(cls, ldap_key: str) -> Union[None, str]:
"""Retrieve the python model object's attribute name for a given ldap
entry key.
This default implementation checks the :meth:`ldap_attribute_map`,
returning, ``key`` for the ``value`` in the mapping, or ``None`` if
not found.
You can use the default implementation from the ``super`` mechanism in
your subclass.
:param ldap_key: The ldap entry key.
"""
for key, value in cls.ldap_attribute_map().items():
if str(value) == str(ldap_key):
return key
@classmethod
[docs] def query_cn(cls) -> str:
"""Return the query cn to be used in queries. This value will get
added to the ``base_dn`` as 'cn=<this value>,<base_dn>' for queries.
The default implementation returns the lower case version of the class
name and appends an 's'.
You can use the default implementation from the ``super`` mechanism in
your subclass.
Example::
# given a base dn of 'dc=example,dc=com'
>>> class User(ModelABC): pass
>>> open_directory = OpenDirectory()
>>> query = Query(open_directory=open_directory, model=User)
>>> query.search_base
'cn=users,dc=example,dc=com'
"""
return cls._query_cn or cls.__name__.lower() + 's'
@classmethod
[docs] def from_entry(cls, entry: ldap3.Entry) -> Any:
"""Return an instance of the class from an :class:`ldap3.Entry`
The default implementation requires that a subclass accepts ``kwargs``
for all attributes in it's ``__init__`` method.
:param entry: An :class:`ldap3.Entry` to convert to this python model.
:rtype: An instance of the python model.
"""
return cls(**entry.entry_attributes_as_dict)
@classmethod
def __subclasshook__(cls, Cls):
if cls is ModelABC:
methods = [
any('ldap_attribute_map' in dir(B) for B in Cls.__mro__),
any('attribute_name_for' in dir(B) for B in Cls.__mro__),
any('query_cn' in dir(B) for B in Cls.__mro__),
any('from_entry' in dir(B) for B in Cls.__mro__),
any('ldap_keys' in dir(B) for B in Cls.__mro__),
]
if all(methods):
return True
return NotImplemented # pragma: no cover