summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNico von Geyso <Nico.Geyso@FU-Berlin.de>2012-09-20 13:05:42 +0200
committerNico von Geyso <Nico.Geyso@FU-Berlin.de>2012-09-20 13:45:53 +0200
commiteb07f6e414551125a216f8556ffac77010feb60b (patch)
tree0d3831bd0f1d2d1d0ffca2959380944a2e95ef78
parent4dddc4d5d2643c6f561a3f79b82aec16381a58d7 (diff)
downloadweb-eb07f6e414551125a216f8556ffac77010feb60b.tar.gz
web-eb07f6e414551125a216f8556ffac77010feb60b.tar.bz2
web-eb07f6e414551125a216f8556ffac77010feb60b.zip
Updated AccountService API
AccountService is now stateless. That means every request needs its own authentication request (bind). Usage examples: * register a new user >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES) >> foo = Account('foo','foo@bar.de', password='bar') >> service.register(foo, LDAP_ADMIN_USER, LDAP_ADMIN_PASS) * authenticate a new user >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES) >> foo = service.auth('foo', 'bar') * updates an account >> foo.change_mail('a@b.de') >> foo.change_password('bar2') # changes root password >> foo.change_password('bar2', 'gitlab') # changes password for gitlab >> service.update(foo) # save changes in ldap backend # save changes in ldap backend as admin user >> service.update(foo, LDAP_ADMIN_USER, LDAP_ADMIN_USER) * delete an account >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES) >> service.delete(Account) >> service.delete('foo') * find accounts >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES) >> all_accounts = service.find(LDAP_ADMIN_USER, LDAP_ADMIN_PASS) >> print([x.uid for x in all_accounts])
-rw-r--r--account.py208
1 files changed, 146 insertions, 62 deletions
diff --git a/account.py b/account.py
index 46356d5..ef70d9d 100644
--- a/account.py
+++ b/account.py
@@ -2,26 +2,55 @@
import ldap
-LDAP_HOST = 'ldap://localhost'
-LDAP_BASE_DN = 'dc=nodomain,dc=local'
-LDAP_ADMIN_USER = 'root'
-LDAP_ADMIN_PASS = 'root'
+LDAP_HOST = 'ldap://localhost:5678'
+LDAP_BASE_DN = 'dc=account,dc=spline,dc=inf,dc=fu-berlin,dc=de'
+LDAP_ADMIN_USER = 'admin'
+LDAP_ADMIN_PASS = 'admin'
+SERVICES = ['foren','jabber''gitlab']
class AccountService:
"""
To simplify account management through ldap this class can be used.
+ The AccountService class is stateless. It means that every request needs
+ its own authentication request (bind).
+
+ To test you stuff against our test setup use Port-Forwarding
+ ssh spline -L 5678:vm-splinux:389 -N
+
+ * register a new user
+ >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES)
+ >> foo = Account('foo','foo@bar.de', password='bar')
+ >> service.register(foo, LDAP_ADMIN_USER, LDAP_ADMIN_PASS)
+
+ * authenticate a new user
+ >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES)
+ >> foo = service.auth('foo', 'bar')
+
+ * updates an account
+ >> foo.change_mail('a@b.de')
+ >> foo.change_password('bar2') # changes root password
+ >> foo.change_password('bar2', 'gitlab') # changes password for gitlab
+ >> service.update(foo) # save changes in ldap backend
+ # save changes in ldap backend as admin user
+ >> service.update(foo, LDAP_ADMIN_USER, LDAP_ADMIN_PASS)
+
+ * delete an account
+ >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES)
+ >> service.delete(Account)
+ >> service.delete('foo')
+
+ * find accounts
+ >> service = AccountService(LDAP_HOST, LDAP_BASE_DN,SERVICES)
+ >> all_accounts = service.find(LDAP_ADMIN_USER, LDAP_ADMIN_PASS)
+ >> print([x.uid for x in all_accounts])
+
"""
- def __init__(self, ldap_host, base_dn, admin_user, admin_pass):
+ def __init__(self, ldap_host, base_dn, services):
self.ldap_host = ldap_host
self.base_dn = base_dn
- self.admin_cn = 'cn=%s,%s' % (admin_user, self.base_dn)
- self.admin_pass = admin_pass
- self.binded = False
-
- self.connection = ldap.initialize(ldap_host)
- self.connection.version = ldap.VERSION3
+ self.services = services
def auth(self, username, password):
@@ -29,68 +58,144 @@ class AccountService:
Tries to authenticate a user with a given password. If the
authentication is successful an Account object will be returned.
"""
- dn = 'uid=%s,%s' % (username, self.base_dn)
+ dn = 'uid=%s,ou=users,%s' % (username, self.base_dn)
self._bind(dn, password)
- dn, data = self.connection.search_s(dn, ldap.SCOPE_BASE)[0]
- acc = Account(dn, data['cn'][0], data['sn'][0])
+
+ dn_user, data_user = self.connection.search_s(dn, ldap.SCOPE_SUBTREE)[0]
+ uid = data_user['uid'][0]
+ mail = data_user['mail'][0]
+
+ dn = 'ou=services,%s' % self.base_dn
+ filterstr = '(uid=%s)' % uid
+ data_service = self.connection.search_s(dn,ldap.SCOPE_SUBTREE,filterstr)
+ services = []
+ for entry in data_service:
+ cn = filter(lambda x: x.startswith('cn='), entry[0].split(','))[0]
+ services.append(cn.split('=')[-1])
+
+ acc = Account(uid, mail, services, dn_user, password)
+
self._unbind()
+
return acc
- def register(self, account):
+ def find(self, admin_user, admin_pass, filterstr = '(objectClass=*)'):
+ """
+ Find accounts with raw ldap filter syntax
+ """
+ self._bind('cn=%s,%s' % (admin_user, self.base_dn), admin_pass)
+
+ dn = 'ou=users,%s' % self.base_dn
+ data = self.connection.search_s(dn,ldap.SCOPE_SUBTREE,filterstr)
+
+ accounts = []
+ for a in data[1:]:
+ accounts.append(Account(a[1]['uid'],a[1]['mail']))
+
+ self._unbind()
+
+ return accounts
+
+
+ def register(self, account, admin_user, admin_pass):
"""
Persists an account in the ldap backend
"""
- self._bind(self.admin_cn, self.admin_pass)
- dn = 'uid=%s,%s' % (account.uid, self.base_dn)
- self.connection.add_s(dn, account.to_ldif())
+ self._bind('cn=%s,%s' % (admin_user, self.base_dn), admin_pass)
+
+ dn = 'uid=%s,ou=users,%s' % (account.uid, self.base_dn)
+ attr = [
+ ('objectClass', ['top','inetOrgPerson']), ('uid',account.uid),
+ ('sn', ' '), ('cn', ' '), ('mail', account.mail),
+ ('userPassword', account.password)
+ ]
+ self.connection.add_s(dn, attr)
account.dn = dn
+
self._alter_passwords(account)
+
self._unbind()
- def update(self, account):
+ def update(self, account, admin_user = None, admin_pass = None):
"""
Updates account informations like passwords or email.
"""
- self._bind(self.admin_cn, self.admin_pass)
+ user = 'uid=%s,ou=users' % account.uid
+ if admin_user:
+ user = 'cn=%' % user
+
+ password = account.password
+ if admin_pass:
+ password = admin_pass
+
+ self._bind('%s,%s' % (user, self.base_dn), password)
+
attr = [(ldap.MOD_REPLACE, 'mail', account.mail)]
- dn = 'uid=%s,%s' % (account.uid, self.base_dn)
+ dn = 'uid=%s,ou=users,%s' % (account.uid, self.base_dn)
self.connection.modify_s(dn, attr)
self._alter_passwords(account)
+
self._unbind()
- def delete(self, uid):
+ def delete(self, account, admin_user = None, admin_pass = None):
"""
Deletes an account permanently.
"""
- self._bind(self.admin_cn, self.admin_pass)
- dn = 'uid=%s,%s' % (uid, self.base_dn)
- self.connection.delete_s(dn)
- self._unbind()
+ try: dn_user = account.dn
+ except: dn_user = 'uid=%s,ou=users,%s' % (account, self.base_dn)
+
+ user = dn_user
+ if admin_user:
+ user = 'cn=%s,%s' % (admin_user, self.base_dn)
+
+ password = account.password
+ if admin_pass:
+ password = admin_pass
+
+ self._bind(user, password)
+ dn = ['uid=%s,cn=%s,ou=services,%s' % (account.uid,s,self.base_dn) for s in account.services]
+ dn.append(dn_user)
+
+ for x in dn:
+ self.connection.delete_s(x)
+
+ self._unbind()
def _bind(self, dn, password):
- if not self.binded:
- self.connection.simple_bind_s(dn, password)
- self.binded = True
+ self.connection = ldap.initialize(self.ldap_host)
+ self.connection.version = ldap.VERSION3
+
+ self.connection.simple_bind_s(dn, password)
def _unbind(self):
- if self.binded:
- self.connection.unbind_s()
- self.binded = False
+ self.connection.unbind_s()
def _alter_passwords(self, account):
- if self.binded and 'root' in account.passwords:
+ if account.new_password_root:
attr = [
- (ldap.MOD_REPLACE, 'userPassword', account.passwords['root'])
+ (ldap.MOD_REPLACE,'userPassword',account.new_password_root)
]
- dn = 'uid=%s,%s' % (account.uid, self.base_dn)
+ dn = 'uid=%s,ou=users,%s' % (account.uid, self.base_dn)
self.connection.modify_s(dn, attr)
+ account.new_password_root = None
+
+ for service, password in account.new_password_services:
+ attr = [
+ (ldap.MOD_REPLACE, 'objectClass', LDAP_OBJECT_CLASS)
+ (ldap.MOD_REPLACE, 'userPassword', password)
+ ]
+ dn = 'uid=%s,ou=services,cn=%s,%s' % (account.uid, service, self.base_dn)
+ self.connection.modify_s(dn, attr)
+
+ account.new_password_services = []
+
class Account:
@@ -103,10 +208,9 @@ class Account:
self.mail = mail
self.services = services
self.dn = dn
- self.passwords = {}
-
- if password:
- self.change_password(password)
+ self.password = password
+ self.new_password_root = None
+ self.new_password_services = {}
def __str__(self):
@@ -120,9 +224,9 @@ class Account:
password will be changed.
"""
if not service:
- service = 'root'
-
- self.passwords[service] = new_password
+ self.new_password_root = new_password
+ else:
+ self.new_password_services[service] = new_password
def change_email(self, new_mail):
@@ -131,23 +235,3 @@ class Account:
to make changes permanent.
"""
self.mail = new_mail
-
-
- def to_ldif(self):
- """
- Returns basic account data as a list of tuples (ldif format)
- """
- return [
- ('objectClass', ['top','inetOrgPerson']), ('uid',self.uid),
- ('sn', self.uid), ('cn', self.uid), ('mail', self.mail)
- ]
-
-
-service = AccountService(LDAP_HOST, LDAP_BASE_DN, LDAP_ADMIN_USER, LDAP_ADMIN_PASS)
-#print(service.auth('testaccountt6', 'secret'))
-#service.delete('testaccountt5')
-#a = Account('testaccountt6', 'test@test.de', password='secret')
-a = Account('testaccount4', 'mail@mail.de', password='secret')
-service.update(a)
-#print(service.register(a))
-