API

The public interface for Flask-OpenDirectory.

OpenDirectory

class flask_open_directory.OpenDirectory(app=None)[source]

Bases: flask_open_directory.base.BaseOpenDirectory

base_dn

Return the base dn for the open directory server. This looks in the config dict for key ‘OPEN_DIRECTORY_BASE_DN’ and if nothing is found it will create one from the server_url.

Note

If your server url is an ip address, then you need to set this in the config.

Return type:str
connect()

Create’s an ldap3.Connection, that will need to be managed by the caller (closed, cleanup, etc). This is primarily used when there is a flask application running. It is better to use the connection_ctx context manager.

Return type:Connection
connection

Return’s a shared connection when a flask application is running. If there is not flask application running, then this property will return None.

If you need a connection outside of a flask application context, then you can create one with the connect method or use the connection_ctx method, which works the same with or without an application context.

Return type:Union[None, Connection]
connection_ctx()

A context manager that will use the shared connection if a flask application context is available if not it will create a connection for running one-off commands.

Return type:ContextManager[]
create_server()

Create’s a ldap3.Server with this instances server_url. This method is typically only used internally.

Return type:Server
init_app(app)[source]

Initialize the extension with the application.

Parameters:app – The flask.Flask application to register the extension with.
query(model=None, **kwargs)[source]

Create a query with this instance as it’s open_directory attribute.

Parameters:
  • model – An optional ModelABC subclass to set for the query.
  • kwargs – Extra key-word arguments to pass to the Query, constructor.
Return type:

Query

server_url

Return the server url for an instance. This looks in the config dict for key ‘OPEN_DIRECTORY_SERVER’ and if noting is found it returns _default_server set on the class (default ‘localhost’).

Return type:str
teardown(exception)[source]

Clean-up for the extension.

Decorators

This package contains the following decorators/ decorator helpers.

flask_open_directory.requires_group(group_name)[source]

Decorator to mark a flask route as requiring the authenticated user to be apart of the specified group.

Parameters:group_name – The group name to query the open_directory connection for and is required that the username (retrieved from Authorization header in the request) is apart of the given group. If the user is not then we abort with a 401 code.

Example:

# app.py
from flask import Flask
from open_directory_utils import OpenDirectory, utils, requires_group

app = Flask(__name__)
app.config['OPEN_DIRECTORY_SERVER'] = 'example.com'

od = OpenDirectory(app)

... other routes

@app.route('/admins')
@requires_group('administrators')
def admins_only():
    return "Hello, '{}'. You are an administrator".format(
        utils.username_from_request()
    )
flask_open_directory.requires_any_group(*group_names)[source]

Decorator to mark a flask route as requiring the authenticated user to be apart of any of the passed in groups.

This is similar to the requires_all_groups, only it accepts multiple groups to check against, and succeeds if the authenticated user is a member of any of the specified groups.

Parameters:group_names – The group name(s) to query the open_directory connection for and is required that the username (retrieved from Authorization header in the request) is apart of the given group. If the user is not then we abort with a 401 code.

Example:

# app.py
from flask import Flask
from open_directory_utils import OpenDirectory, utils,             requires_any_group

app = Flask(__name__)
app.config['OPEN_DIRECTORY_SERVER'] = 'example.com'

open_directory = OpenDirectory(app)

@app.route('/office-or-admins')
@requires_any_group('administrators', 'office')
def office_or_admins():
    return "Hello, '{}'. You're an administrator or office user".format(
        utils.username_from_request()
    )
flask_open_directory.requires_all_groups(*group_names)[source]

Decorator to mark a flask route as requiring the authenticated user to be apart of all the passed in groups.

This is similar to the requires_group, only it accepts multiple groups to check against.

Parameters:group_names – The group name(s) to query the open_directory connection for and is required that the username (retrieved from Authorization header in the request) is apart of the given group. If the user is not then we abort with a 401 code.

Example:

# app.py
from flask import Flask
from open_directory_utils import OpenDirectory, utils,             requires_all_groups

app = Flask(__name__)
app.config['OPEN_DIRECTORY_SERVER'] = 'example.com'

open_directory = OpenDirectory(app)


@app.route('/office-admins')
@requires_all_groups('administrators', 'office')
def office_admins():
    return "Hello, '{}'. You are an office administrator".format(
        utils.username_from_request()
    )
flask_open_directory.pass_context(fn)[source]

Helper that passes a DecoratorContex with the OpenDirectory registered with the current application and the username for the request. This will pass the context as the first arg to the decorator.

Models

The following models are included as part of the package, however a user is welcome to create there own as well.

class flask_open_directory.User(**kwargs)[source]

Bases: flask_open_directory.model.model.BaseModel

Represents a user in the open directory

See also

BaseModel for inherited methods.

email = Attribute('mail', allow_multiple=True)

The email address(s) (mail) for a user.

full_name = Attribute('cn', allow_multiple=False)

The full name (cn) for the user

id = Attribute('apple-generateduid', allow_multiple=False)

The id (apple-generateduid) for a user.

username = Attribute('uid', allow_multiple=False)

The username (uid/short-name) for a user

class flask_open_directory.Group(**kwargs)[source]

Bases: flask_open_directory.model.model.BaseModel

Represents a group in the open directory

See also

BaseModel for inherited methods.

full_name = Attribute('apple-group-realname', allow_multiple=False)

The group full name (apple-group-realname) for the group

group_name = Attribute('cn', allow_multiple=False)

The group name (cn) for the group

has_user(user)[source]

Check if a user is part of the group.

Parameters:user (str) – Either the username (uid) or id (apple-generateduid) of a user.
Return type:bool
id = Attribute('apple-generateduid', allow_multiple=False)

The id (apple-generateduid) for the group

member_ids = Attribute('apple-group-memberguid', allow_multiple=True)

The user(s) id’s (apple-group-memberguid) of the group

users = Attribute('memberUid', allow_multiple=True)

The usernames (memberUid) that are members of the group

Query

The following class is not often used directly, it is typically easier to use the OpenDirectory.query(), to get a query that is already setup.

class flask_open_directory.Query(open_directory=None, model=None, search_base=None, search_filter=None, connection=None, ldap_attributes=None)[source]

Bases: flask_open_directory.query.base_query.BaseQuery

Extends the BaseQuery with some helper methods. The helpers typically return the query when called for method chaining.

This allows a query to be mostly setup by another object, and the caller provide the criteria for the search.

Example:

>>> query = Query(open_directory=OpenDirectory())
>>> query(User).filter(username='testuser').first()
User(...)
>>> query.all()  # reuse the same criteria, only get all the users
[User(...),
...]
>>> q = query(Group)  # change the ``model`` on a query
>>> q == query
True
>>> q.filter(group_name='testgroup').first()
Group(...)
all(connection=None, convert=True)

Query the connection and return a tuple of all items found for the current state of the query. This will return an empty tuple if a connection was not established or there was no entries to match the filter criteria.

Parameters:
  • connection (Optional[Connection]) – An optional ldap3.Connection to use for the search.
  • convert – Whether to convert the ldap3.Entry‘s to the model set on a class. Default is True
Return type:

Tuple[Any]

connection

An ldap3.Connection for the query. If this is not set explicitly, then we will see if an open_directory is set on the instance and return it’s connection, which could still be None, if there is no flask application running.

If you don’t know wheter you are working inside of the application context or not, then it is safer to use the BaseQuery.connection_ctx() method.

Raises:TypeError – If explicitly setting this to something other than an ldap3.Connection.
Return type:ContextManager[]
connection_ctx()

A context manager that tries to find a connection to use. If a connection is found on the instance or the open_directory of an instance, then that will be used. If not it will try to create one using an instances open_directory.

If a connection is not able to be found this will yield None, so you should check before using the connection.

Example:

>>> with query.connection_ctx() as conn:
...     model = query.first(conn)
Return type:Optional[Connection]
filter(*args, **kwargs)[source]

Set’s the search filter for an instance, returning the query for method chaining.

This method will accept a string or keyword arguments as parameters.

If there is a string passed in it is set as the filter on the instance. There is no parsing or validation done on a string if it’s passed in, which can cause errors when performing the query if it is an invalid filter string.

If there is no string passed in then keyword arguments are parsed with the current model set on an instance. This means that you can build a query using either the model’s attribute name or the ldap entry key.

Example:
>>> from open_directory_utils.model import User
>>> from open_directory_utils.query import Query
>>> q = Query(search_base='dc=example,dc=com', model=User)
>>> q.filter(username='testuser')
Query(...)
>>> print(q.search_filter)
'(uid=testuser)'
>>> q.filter(uid='anotheruser')
Query(...)
>>> print(q.search_filter)
'(uid=anotheruser)'
>>> q.filter('(cn='Test User')')
>>> print(q.search_filter)
'(cn=Test User)'
Return type:

Query

first(connection=None, convert=True)

Query the connection and return the first item found for the current state of the query. This will return None if a connection was not established or there was no entry to match the filter criteria.

Parameters:
  • connection (Optional[Connection]) – An optional ldap3.Connection to use for the search.
  • convert – Whether to convert the ldap3.Entry to the model set on a class. Default is True
Return type:

Any

ldap_attributes

The ldap attributes to return with the query. These should be a list/tuple of strings that are attributes in the ldap database.

If none have been set explicitly on an instance, then we will check if there is a model, and use it’s ldap_keys for this value.

Return type:Union[None, Iterable[str]]
model

A ModelABC subclass used for search criterea and to in converting returned entries into the desired model.

The attribute can be set with either a class or an instance of a class, but we always store a reference to the class.

Return type:Any
open_directory

A OpenDirectoryABC subclass used for the connection for the query.

Return type:Any
search_base

A string representing where to search.

This property will try to derive this value from several places. If it set explicitly, then that will be the preferred return value.

If it is not set explicitly, then we will check if there is an OpenDirectory set for the instance. If so we will use the base_dn property from the open_directory. We will further check if a model has been set for the instance and we will add it’s query_cn.

Return type:Optional[str]
search_filter

The ldap filter string for the query. If not set then we will return the class’s _default_search_filter which is ‘(objectClass=*)’

Return type:str

Utilities

This package includes the following utility methods. These are not importable from the main entrypoint, these need to be imported directly from flask_open_directory.utils module.

flask_open_directory.utils.base_dn_from_url(url)[source]

Split a url into a base_dn.

Parameters:

url (str) – A url string.

Example:
>>> base_dn_from_url('open_directory.local')
'dc=open_directory,dc=local'
>>> base_dn_from_url('api.open_directory.com')
'dc=api,dc=open_directory,dc=com'
Return type:

str

flask_open_directory.utils.username_from_request()[source]

Get the username from the request, or None if not found

Return type:Optional[str]

Base Classes and Helper Classes

This package includes the following base classes, which are good place to start if trying to implement your own sub-classes.

class flask_open_directory.BaseOpenDirectory(**config)[source]

Bases: flask_open_directory.base.open_directory_abc.OpenDirectoryABC

Implements the OpenDirectoryABC.

This stores all kwargs into a config attribute, which is used to get the server url and an optional base dn.

base_dn

Return the base dn for the open directory server. This looks in the config dict for key ‘OPEN_DIRECTORY_BASE_DN’ and if nothing is found it will create one from the server_url.

Note

If your server url is an ip address, then you need to set this in the config.

Return type:str
connect()[source]

Create’s an ldap3.Connection, that will need to be managed by the caller (closed, cleanup, etc). This is primarily used when there is a flask application running. It is better to use the connection_ctx context manager.

Return type:Connection
connection

Return’s a shared connection when a flask application is running. If there is not flask application running, then this property will return None.

If you need a connection outside of a flask application context, then you can create one with the connect method or use the connection_ctx method, which works the same with or without an application context.

Return type:Union[None, Connection]
connection_ctx()[source]

A context manager that will use the shared connection if a flask application context is available if not it will create a connection for running one-off commands.

Return type:ContextManager[]
create_server()[source]

Create’s a ldap3.Server with this instances server_url. This method is typically only used internally.

Return type:Server
server_url

Return the server url for an instance. This looks in the config dict for key ‘OPEN_DIRECTORY_SERVER’ and if noting is found it returns _default_server set on the class (default ‘localhost’).

Return type:str
class flask_open_directory.BaseModel(**kwargs)[source]

Bases: flask_open_directory.model.model_abc.ModelABC

Implementation of ModelABC. Used to map ldap entry keys to python attributes.

Parameters:

kwargs – Values to set for the attributes of an instance. These can be passed in with the python attribute name as the key or the ldap entry name as the key and the values will be stored and accessible appropriately.

Example:
>>> class User(BaseModel):
...     id = Attribute('apple-generateduid')
>>> user = User(id='123')
>>> user.id == '123'
True
>>> user2 = User(**{'apple-generateduid': '456'})
>>> user2.id == '456'
True
classmethod attribute_for_key(key)[source]

Return Attribute for the given key, which can be the python attribute or the ldap entry key.

Returns None if not found

Return type:Optional[Attribute]
attribute_name_for(ldap_key)

Retrieve the python model object’s attribute name for a given ldap entry key.

This default implementation checks the 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.

Parameters:ldap_key (str) – The ldap entry key.
Return type:Union[None, str]
from_entry(entry)

Return an instance of the class from an ldap3.Entry

The default implementation requires that a subclass accepts kwargs for all attributes in it’s __init__ method.

Parameters:entry (Entry) – An ldap3.Entry to convert to this python model.
Return type:An instance of the python model.
classmethod ldap_attribute_map()[source]

Returns the mapping between python attributes and ldap entry keys.

The python attribute name is the key and ldap entry key is the value in the mapping.

Return type:Dict[str, str]
ldap_keys()

Return all the ldap keys

This default implementation returns the values from the ldap_attribute_map().

You can use the default implementation from the super mechanism in your subclass.

Return type:Tuple[str]
ldap_values

Stores the actual values for the Attribute. These are the values returned when accessing an Attribute instance set on a Model.

Return type:Dict[str, Iterable[str]]
query_cn()

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 type:str
class flask_open_directory.BaseQuery(open_directory=None, model=None, search_base=None, search_filter=None, connection=None, ldap_attributes=None)[source]

Bases: flask_open_directory.query.query_abc.QueryABC

Implementation of QueryABC abstract class. This is used to search an ldap connection and convert the entries into a ModelABC subclass.

This also adds a helpful context manager BaseQuery.connection_ctx(), which is helpful if you do not have a flask application running, it will try to create a connection with the open_directory set on the instance for the search.

all(connection=None, convert=True)[source]

Query the connection and return a tuple of all items found for the current state of the query. This will return an empty tuple if a connection was not established or there was no entries to match the filter criteria.

Parameters:
  • connection (Optional[Connection]) – An optional ldap3.Connection to use for the search.
  • convert – Whether to convert the ldap3.Entry‘s to the model set on a class. Default is True
Return type:

Tuple[Any]

connection

An ldap3.Connection for the query. If this is not set explicitly, then we will see if an open_directory is set on the instance and return it’s connection, which could still be None, if there is no flask application running.

If you don’t know wheter you are working inside of the application context or not, then it is safer to use the BaseQuery.connection_ctx() method.

Raises:TypeError – If explicitly setting this to something other than an ldap3.Connection.
Return type:ContextManager[]
connection_ctx()[source]

A context manager that tries to find a connection to use. If a connection is found on the instance or the open_directory of an instance, then that will be used. If not it will try to create one using an instances open_directory.

If a connection is not able to be found this will yield None, so you should check before using the connection.

Example:

>>> with query.connection_ctx() as conn:
...     model = query.first(conn)
Return type:Optional[Connection]
first(connection=None, convert=True)[source]

Query the connection and return the first item found for the current state of the query. This will return None if a connection was not established or there was no entry to match the filter criteria.

Parameters:
  • connection (Optional[Connection]) – An optional ldap3.Connection to use for the search.
  • convert – Whether to convert the ldap3.Entry to the model set on a class. Default is True
Return type:

Any

ldap_attributes

The ldap attributes to return with the query. These should be a list/tuple of strings that are attributes in the ldap database.

If none have been set explicitly on an instance, then we will check if there is a model, and use it’s ldap_keys for this value.

Return type:Union[None, Iterable[str]]
model

A ModelABC subclass used for search criterea and to in converting returned entries into the desired model.

The attribute can be set with either a class or an instance of a class, but we always store a reference to the class.

Return type:Any
open_directory

A OpenDirectoryABC subclass used for the connection for the query.

Return type:Any
search_base

A string representing where to search.

This property will try to derive this value from several places. If it set explicitly, then that will be the preferred return value.

If it is not set explicitly, then we will check if there is an OpenDirectory set for the instance. If so we will use the base_dn property from the open_directory. We will further check if a model has been set for the instance and we will add it’s query_cn.

Return type:Optional[str]
search_filter

The ldap filter string for the query. If not set then we will return the class’s _default_search_filter which is ‘(objectClass=*)’

Return type:str
class flask_open_directory.DecoratorContext[source]

Bases: dict

A specialized dict, used in request decorators. The keys are accessible using normal dict style or with attribute (dot) style syntax.

class flask_open_directory.Attribute(ldap_key, allow_multiple=False)[source]

Bases: object

Represents an LDAP entry attribute. It maps the ldap entry key to an attribute on a Model.

Parameters:
  • ldap_key – The ldap entry key for the attribute.
  • allow_multiple – If False (default) then any lists only return the first item. If True return the whole list of values.

Abstract Classes

This package provides the following abstract classes, that can be used to test if an object implements the correct interface.

class flask_open_directory.OpenDirectoryABC[source]

Bases: object

This abc has all the methods that should be implemented for an object to be considered a valid OpenDirectory subclass.

All of the methods must be implemented to create a subclass, or to pass an isinstance or issubclass check.

base_dn

Return the base ldap domain. Used to build ldap queries.

Return type:str
connection

An ldap3.Connection used for queries. This should only return a connection if there is a flask application running with the connection stored on it.

Return type:Optional[Connection]
connection_ctx()[source]

A context manager to yield an ldap3.Connection, this should try to create a connection regardless of if a flask application is running.

Return type:ContextManager[]
server_url

Return the ldap server url. Used for the ldap connection.

Return type:str
class flask_open_directory.ModelABC[source]

Bases: object

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.

classmethod attribute_name_for(ldap_key)[source]

Retrieve the python model object’s attribute name for a given ldap entry key.

This default implementation checks the 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.

Parameters:ldap_key (str) – The ldap entry key.
Return type:Union[None, str]
classmethod from_entry(entry)[source]

Return an instance of the class from an ldap3.Entry

The default implementation requires that a subclass accepts kwargs for all attributes in it’s __init__ method.

Parameters:entry (Entry) – An ldap3.Entry to convert to this python model.
Return type:An instance of the python model.
classmethod ldap_attribute_map()[source]

Return a mapping of <model attribute: ldap key> values.

This does not have a default implementation, and must be implemented on the subclass.

Return type:Dict[str, str]
classmethod ldap_keys()[source]

Return all the ldap keys

This default implementation returns the values from the ldap_attribute_map().

You can use the default implementation from the super mechanism in your subclass.

Return type:Tuple[str]
classmethod query_cn()[source]

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 type:str
class flask_open_directory.QueryABC[source]

Bases: object

This abc has all the methods that should be implemented for an object to be considered a valid Query subclass.

All of the methods must be implemented to create a subclass, or to pass an isinstance or issubclass check.

all(connection=None)[source]

Searches the ldap connection returning tuple of all the entries found. Or an empty tuple.

Parameters:connection (Optional[Connection]) – An optional connection to use for the search.
Return type:Tuple[Any]
connection

Return the ldap connection for the query.

Return type:Union[None, Connection]
first(connection=None)[source]

Searches the ldap connection returning the first entry, if any are found or None if not.

Parameters:connection (Optional[Connection]) – An optional connection to use for the search.
Return type:Any
ldap_attributes

Return the ldap attributes to return for the query.

Return type:Iterable[str]
search_base

Return the ldap serach base dn string for the query.

Return type:str
search_filter

Return the ldap search filter string for the query.

Return type:str