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 theserver_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 theconnection_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 theconnection_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 instancesserver_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: Return type:
-
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 a401
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 a401
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 a401
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 theOpenDirectory
registered with the current application and theusername
for the request. This will pass thecontext
as the first arg to the decorator.See also
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 optionalldap3.Connection
to use for the search. - convert – Whether to convert the
ldap3.Entry
‘s to themodel
set on a class. Default isTrue
Return type: Tuple
[Any
]- connection (
-
connection
An
ldap3.Connection
for the query. If this is not set explicitly, then we will see if anopen_directory
is set on the instance and return it’sconnection
, which could still beNone
, 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 instancesopen_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:
-
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 optionalldap3.Connection
to use for the search. - convert – Whether to convert the
ldap3.Entry
to themodel
set on a class. Default isTrue
Return type: Any
- connection (
-
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’sldap_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 thebase_dn
property from theopen_directory
. We will further check if amodel
has been set for the instance and we will add it’squery_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 foundReturn 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 theserver_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 theconnection_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 theconnection_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 instancesserver_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 foundReturn 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 thevalue
in the mapping, orNone
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
) – Anldap3.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 theldap_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 anAttribute
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
-
classmethod
-
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 aModelABC
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 theopen_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 optionalldap3.Connection
to use for the search. - convert – Whether to convert the
ldap3.Entry
‘s to themodel
set on a class. Default isTrue
Return type: Tuple
[Any
]- connection (
-
connection
An
ldap3.Connection
for the query. If this is not set explicitly, then we will see if anopen_directory
is set on the instance and return it’sconnection
, which could still beNone
, 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 instancesopen_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 optionalldap3.Connection
to use for the search. - convert – Whether to convert the
ldap3.Entry
to themodel
set on a class. Default isTrue
Return type: Any
- connection (
-
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’sldap_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 thebase_dn
property from theopen_directory
. We will further check if amodel
has been set for the instance and we will add it’squery_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. IfTrue
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 validOpenDirectory
subclass.All of the methods must be implemented to create a subclass, or to pass an
isinstance
orissubclass
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 thevalue
in the mapping, orNone
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
) – Anldap3.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 theldap_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
-
classmethod
-
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 validQuery
subclass.All of the methods must be implemented to create a subclass, or to pass an
isinstance
orissubclass
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
-