# -*- coding: utf-8 -*- class NoSuchUserError(ValueError): pass class InvalidPasswordError(ValueError): pass class ShouldNotHappen(RuntimeError): pass class Backend(object): """ This is the backend class for the account management. It is stateless, so every request needs the authentication data again. * register a new user >> backend = Backend(app) >> foo = Account('foo','foo@bar.de', password='bar') >> backend.register(foo) * authenticate a new user >> backend = Backend(app) >> foo = backend.auth('foo', 'bar') * updates an account >> foo.change_mail('a@b.de') >> foo.change_password('newpw','oldpw') # changes root password >> foo.change_password('newpw','oldpw', 'gitlab') # changes password for gitlab >> backend.update(foo) # save changes in the backend # save changes in the backend as admin user (no need for old password) >> backend.update(foo, as_admin=True) * delete an account >> backend = Backend(app) >> backend.delete(Account) * find accounts >> backend = Backend(app) >> all_accounts = backend.find() # find all accounts >> print([x.uid for x in all_accounts]) >> backend.find_by_uid('test') # find users by uid >> backend.get_by_uid('test') # same, raise NoSuchUserError if no match >> backend.find_by_mail('test@test.de') # find users by mail >> backend.find_by_uid('test*', wildcard=True) # find with wildcards """ def __init__(self, app): self.app = app #: Exception type, that is raised if no matching user was found. self.NoSuchUserError = NoSuchUserError #: Exception type, that is raised if you try to authenticate with #: wrong password. Because this backend is stateless, this exception #: could also be raised, if you want to change user information. self.InvalidPasswordError = InvalidPasswordError def auth(self, username, password): """ Tries to authenticate a user with a given password. If the authentication is successful an Account object will be returned. """ raise NotImplementedError() def get_by_uid(self, uid): """ Find a single user by uid. Unlike find_by_uid, don't return a list but raise NoSuchUserError if there is no such user. """ users = self.find_by_uid(uid) if len(users) == 0: raise NoSuchUserError('No such user') if len(users) > 1: raise ShouldNotHappen('Several users for one uid returned.') return users[0] def get_by_mail(self, mail): """ Find a single user by mail. Unlike find_by_mail, don't return a list but raise NoSuchUserError if there is no such user. """ users = self.find_by_mail(mail) if len(users) == 0: raise NoSuchUserError('No such user') if len(users) > 1: raise ShouldNotHappen('Several users for one mail returned.') return users[0] def find_by_uid(self, uid, wildcard=False): return self.find({'uid': uid}, wildcard) def find_by_mail(self, mail, wildcard=False): return self.find({'mail': mail}, wildcard) def find(self, filters=None, wildcard=False): """ Find accounts by a given filter. """ raise NotImplementedError() def register(self, account): """ Register a new user account. This message checks the given account for plausibility, get a new uidNumber and store the account into the backend. """ if account.password is None: raise ValueError("Password required for register") self._store(account) def update(self, account, as_admin=False): """ Updates account information like passwords or email. """ raise NotImplementedError() def delete(self, account, as_admin=False): """ Deletes an account permanently. """ raise NotImplementedError() def _store(self, account): """ Persists an account in the backend. """ raise NotImplementedError()