Implementing cutom backends

API

The backend modules must respect the following API:

class ldapcherry.backend.Backend(config, logger, name, attrslist, key)[source]

Bases: object

__init__(config, logger, name, attrslist, key)[source]

Initialize the backend

Parameters:
  • config (dict {‘config key’: ‘value’}) – the configuration of the backend
  • logger (python logger) – the cherrypy error logger object
  • name (string) – id of the backend
  • attrslist (list of strings) – list of the backend attributes
  • key (string) – the key attribute
add_to_groups(username, groups)[source]

Add a user to a list of groups

Parameters:
  • username (string) – ‘key’ attribute of the user
  • groups (list of strings) – list of groups
add_user(attrs)[source]

Add a user to the backend

Parameters:attrs (dict ({<attr>: <value>})) – attributes of the user

Warning

raise UserAlreadyExists if user already exists

auth(username, password)[source]

Check authentication against the backend

Parameters:
  • username (string) – ‘key’ attribute of the user
  • password (string) – password of the user
Return type:

boolean (True is authentication success, False otherwise)

del_from_groups(username, groups)[source]

Delete a user from a list of groups

Parameters:
  • username (string) – ‘key’ attribute of the user
  • groups (list of strings) – list of groups

Warning

raise GroupDoesntExist if group doesn’t exist

del_user(username)[source]

Delete a user from the backend

Parameters:username (string) – ‘key’ attribute of the user
get_groups(username)[source]

Get a user’s groups

Parameters:username (string) – ‘key’ attribute of the user
Return type:list of groups
get_user(username)[source]

Get a user’s attributes

Parameters:username (string) – ‘key’ attribute of the user
Return type:dict ( {<attr>: <value>} )

Warning

raise UserDoesntExist if user doesn’t exist

search(searchstring)[source]

Search backend for users

Parameters:searchstring (string) – the search string
Return type:dict of dict ( {<user attr key>: {<attr>: <value>}} )
set_attrs(username, attrs)[source]

Set a list of attributes for a given user

Parameters:
  • username (string) – ‘key’ attribute of the user
  • attrs (dict ({<attr>: <value>})) – attributes of the user

Configuration

Configuration for your backend is declared in the main ini file, inside [backends] section:

For example with the configuration:

[backends]

# class path to module
b_id.module = "my.backend.module"

b_id.param1 = "my value 1"
b_id.param2 = "my value 2"

Note

One module can be instanciated several times, the prefix b_id permits to differenciate instances and their specific configuration.

The following hash will be passed as configuration to the module constructor as parameter config:

{
    'param1': "my value 1",
    'param2': "my value 2",
}

After having set self.config to config in the constructor, parameters can be recovered by self.get_param:

class ldapcherry.backend.Backend(config, logger, name, attrslist, key)[source]

Bases: object

get_param(param, default=None)[source]

Get a parameter in config (handle default value)

Parameters:
  • param (string) – name of the parameter to recover
  • default (string or None) – the default value, raises an exception if param is not in configuration and default is None (which is the default value).
Return type:

the value of the parameter or the default value if not set in configuration

Exceptions

The following exception can be used in your module

exception ldapcherry.exceptions.UserDoesntExist(user, backend)[source]

Bases: exceptions.Exception

exception ldapcherry.exceptions.UserAlreadyExists(user, backend)[source]

Bases: exceptions.Exception

exception ldapcherry.exceptions.GroupDoesntExist(group, backend)[source]

Bases: exceptions.Exception

These exceptions permit a nicer error handling and avoid a generic message to be thrown at the user.

Example

Here is the ldap backend module that comes with LdapCherry:

# -*- coding: utf-8 -*-
# vim:set expandtab tabstop=4 shiftwidth=4:
#
# The MIT License (MIT)
# LdapCherry
# Copyright (c) 2014 Carpentier Pierre-Francois

# This is a demo backend

from ldapcherry.exceptions import MissingParameter
from sets import Set
import ldapcherry.backend
import re


class Backend(ldapcherry.backend.Backend):

    def __init__(self, config, logger, name, attrslist, key):
        """ Initialize the backend

        :param config: the configuration of the backend
        :type config: dict {'config key': 'value'}
        :param logger: the cherrypy error logger object
        :type logger: python logger
        :param name: id of the backend
        :type name: string
        :param attrslist: list of the backend attributes
        :type attrslist: list of strings
        :param key: the key attribute
        :type key: string
        """
        self.config = config
        self._logger = logger
        self.users = {}
        self.backend_name = name
        admin_user = self.get_param('admin.user', 'admin')
        admin_password = self.get_param('admin.password', 'admin')
        admin_groups = Set(re.split('\W+', self.get_param('admin.groups')))
        basic_user = self.get_param('basic.user', 'user')
        basic_password = self.get_param('basic.password', 'user')
        basic_groups = Set(re.split('\W+', self.get_param('basic.groups')))
        pwd_attr = self.get_param('pwd_attr')
        self.search_attrs = Set(
            re.split('\W+', self.get_param('search_attributes')),
            )
        self.pwd_attr = pwd_attr
        self.admin_user = admin_user
        self.basic_user = basic_user
        self.key = key
        self.users[admin_user] = {
                key: admin_user,
                pwd_attr: admin_password,
                'groups': admin_groups,
                }
        self.users[basic_user] = {
                key: basic_user,
                pwd_attr: basic_password,
                'groups': basic_groups,
                }

    def _check_fix_users(self, username):
        if self.admin_user == username or self.basic_user == username:
            raise Exception('User cannot be modified')

    def auth(self, username, password):
        """ Check authentication against the backend

        :param username: 'key' attribute of the user
        :type username: string
        :param password: password of the user
        :type password: string
        :rtype: boolean (True is authentication success, False otherwise)
        """
        if username not in self.users:
            return False
        elif self.users[username][self.pwd_attr] == password:
            return True
        return False

    def add_user(self, attrs):
        """ Add a user to the backend

        :param attrs: attributes of the user
        :type attrs: dict ({<attr>: <value>})

        .. warning:: raise UserAlreadyExists if user already exists
        """
        username = attrs[self.key]
        if username in self.users:
            raise UserAlreadyExists(username, self.backend_name)
        self.users[username] = attrs
        self.users[username]['groups'] = Set([])

    def del_user(self, username):
        """ Delete a user from the backend

        :param username: 'key' attribute of the user
        :type username: string

        """
        self._check_fix_users(username)
        del self.users[username]

    def set_attrs(self, username, attrs):
        """ Set a list of attributes for a given user

        :param username: 'key' attribute of the user
        :type username: string
        :param attrs: attributes of the user
        :type attrs: dict ({<attr>: <value>})
        """
        self._check_fix_users(username)
        for attr in attrs:
            self.users[username][attr] = attrs[attr]

    def add_to_groups(self, username, groups):
        """ Add a user to a list of groups

        :param username: 'key' attribute of the user
        :type username: string
        :param groups: list of groups
        :type groups: list of strings
        """
        self._check_fix_users(username)
        current_groups = self.users[username]['groups']
        new_groups = current_groups | Set(groups)
        self.users[username]['groups'] = new_groups

    def del_from_groups(self, username, groups):
        """ Delete a user from a list of groups

        :param username: 'key' attribute of the user
        :type username: string
        :param groups: list of groups
        :type groups: list of strings

        .. warning:: raise GroupDoesntExist if group doesn't exist
        """
        self._check_fix_users(username)
        current_groups = self.users[username]['groups']
        new_groups = current_groups - Set(groups)
        self.users[username]['groups'] = new_groups

    def search(self, searchstring):
        """ Search backend for users

        :param searchstring: the search string
        :type searchstring: string
        :rtype: dict of dict ( {<user attr key>: {<attr>: <value>}} )
        """
        ret = {}
        for user in self.users:
            match = False
            for attr in self.search_attrs:
                if attr not in self.users[user]:
                    pass
                elif re.search(searchstring + '.*', self.users[user][attr]):
                    match = True
            if match:
                ret[user] = self.users[user]
        return ret

    def get_user(self, username):
        """ Get a user's attributes

        :param username: 'key' attribute of the user
        :type username: string
        :rtype: dict ( {<attr>: <value>} )

        .. warning:: raise UserDoesntExist if user doesn't exist
        """
        return self.users[username]

    def get_groups(self, username):
        """ Get a user's groups

        :param username: 'key' attribute of the user
        :type username: string
        :rtype: list of groups
        """
        return self.users[username]['groups']