WIP: Add ungleich_ldap and use it to reset password
This commit is contained in:
parent
6a28b51354
commit
f6f688dcb5
2 changed files with 153 additions and 5 deletions
144
dal/ungleich_ldap.py
Normal file
144
dal/ungleich_ldap.py
Normal file
|
@ -0,0 +1,144 @@
|
|||
import base64
|
||||
import hashlib
|
||||
import random
|
||||
|
||||
import ldap3
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class LdapManager:
|
||||
__instance = None
|
||||
def __new__(cls):
|
||||
if LdapManager.__instance is None:
|
||||
LdapManager.__instance = object.__new__(cls)
|
||||
return LdapManager.__instance
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize the LDAP subsystem.
|
||||
"""
|
||||
self.rng = random.SystemRandom()
|
||||
self.server = ldap3.Server(settings.AUTH_LDAP_SERVER)
|
||||
|
||||
|
||||
def get_admin_conn(self):
|
||||
"""
|
||||
Return a bound :class:`ldap3.Connection` instance which has write
|
||||
permissions on the dn in which the user accounts reside.
|
||||
"""
|
||||
conn = self.get_conn(user=settings.LDAP_ADMIN_DN,
|
||||
password=settings.LDAP_ADMIN_PASSWORD,
|
||||
raise_exceptions=True)
|
||||
conn.bind()
|
||||
return conn
|
||||
|
||||
|
||||
def get_conn(self, **kwargs):
|
||||
"""
|
||||
Return an unbound :class:`ldap3.Connection` which talks to the configured
|
||||
LDAP server.
|
||||
|
||||
The *kwargs* are passed to the constructor of :class:`ldap3.Connection` and
|
||||
can be used to set *user*, *password* and other useful arguments.
|
||||
"""
|
||||
return ldap3.Connection(self.server, **kwargs)
|
||||
|
||||
|
||||
def _ssha_password(self, password):
|
||||
"""
|
||||
Apply the SSHA password hashing scheme to the given *password*.
|
||||
*password* must be a :class:`bytes` object, containing the utf-8
|
||||
encoded password.
|
||||
|
||||
Return a :class:`bytes` object containing ``ascii``-compatible data
|
||||
which can be used as LDAP value, e.g. after armoring it once more using
|
||||
base64 or decoding it to unicode from ``ascii``.
|
||||
"""
|
||||
SALT_BYTES = 15
|
||||
|
||||
sha1 = hashlib.sha1()
|
||||
salt = self.rng.getrandbits(SALT_BYTES * 8).to_bytes(SALT_BYTES,
|
||||
"little")
|
||||
sha1.update(password)
|
||||
sha1.update(salt)
|
||||
|
||||
digest = sha1.digest()
|
||||
passwd = b"{SSHA}" + base64.b64encode(digest + salt)
|
||||
return passwd
|
||||
|
||||
|
||||
def create_user(self, loginname, displayname, mail, password):
|
||||
"""
|
||||
Create a new user in the LDAP storage.
|
||||
|
||||
*loginname* must be a unique, valid user id. It is generally safe to
|
||||
pass lower-case ascii letters here. The *loginname* of an account
|
||||
cannot be changed.
|
||||
|
||||
*displayname* is the name which is shown to other users. This can be
|
||||
changed in the future.
|
||||
|
||||
*mail* is a valid mail address of the user.
|
||||
|
||||
*password* is the initial plain text password for the user.
|
||||
"""
|
||||
|
||||
conn = self.get_admin_conn()
|
||||
try:
|
||||
conn.add(
|
||||
("uid={uid}," + settings.LDAP_CUSTOMER_DN).format(uid=loginname),
|
||||
["inetOrgPerson"],
|
||||
{
|
||||
"uid": [loginname],
|
||||
"cn": [displayname],
|
||||
"sn": ["XXX"],
|
||||
"givenName": ["XXX"],
|
||||
"mail": [mail],
|
||||
"userpassword": [self._ssha_password(
|
||||
password.encode("utf-8")
|
||||
)]
|
||||
}
|
||||
)
|
||||
finally:
|
||||
conn.unbind()
|
||||
|
||||
|
||||
def change_password(self, user_dn, new_password):
|
||||
"""
|
||||
Changes the password of the user identified by user_dn
|
||||
|
||||
:param user_dn: str The distinguished name for identifying the user
|
||||
:param new_password: str The new password string
|
||||
:return: True if password was changed successfully False otherwise
|
||||
"""
|
||||
conn = self.get_admin_conn()
|
||||
return_val = conn.modify(
|
||||
user_dn,
|
||||
{
|
||||
"userpassword": (
|
||||
ldap3.MODIFY_REPLACE,
|
||||
[self._ssha_password(new_password.encode("utf-8"))]
|
||||
)
|
||||
}
|
||||
)
|
||||
conn.unbind()
|
||||
return return_val
|
||||
|
||||
def check_user_exists(self, uid, is_customer=True):
|
||||
"""
|
||||
Check if the user with the given uid exists in the customer group.
|
||||
|
||||
:param uid: str representing the user
|
||||
:param is_customer: bool representing whether the current user is a
|
||||
customer. By default, the user is a customer (assume)
|
||||
:return: True if the user exists otherwise return False
|
||||
"""
|
||||
conn = self.get_admin_conn()
|
||||
try:
|
||||
result = conn.search(
|
||||
settings.LDAP_CUSTOMER_DN if is_customer else settings.LDAP_USERS_DN,
|
||||
search_filter='(uid={uid})'.format(uid=uid)
|
||||
)
|
||||
finally:
|
||||
conn.unbind()
|
||||
return result
|
14
dal/views.py
14
dal/views.py
|
@ -342,11 +342,15 @@ class ResetRequest(View):
|
|||
if len(password1) < 8:
|
||||
return render(request, 'error.html', { 'service': service, 'error': 'The password is too short, please use a longer one. At least 8 characters.' } )
|
||||
# everything checks out, now change the password
|
||||
with get_pool().next() as rpc:
|
||||
pwd = r'%s' % password1
|
||||
result = rpc.changepassword.change_password(user, pwd)
|
||||
# password change successfull
|
||||
if result == True:
|
||||
|
||||
from .ungleich_ldap import LdapManager
|
||||
ldap_manager = LdapManager()
|
||||
result = ldap_manager.change_password(
|
||||
("uid={uid}," + settings.LDAP_CUSTOMER_DN).format(uid=user),
|
||||
password1
|
||||
)
|
||||
# password change successful
|
||||
if result:
|
||||
return render(request, 'changedpassword.html', { 'user': user } )
|
||||
# Something went wrong while changing the password
|
||||
else:
|
||||
|
|
Loading…
Reference in a new issue