diff --git a/README.md b/README.md index cbbfb29..7f9069f 100644 --- a/README.md +++ b/README.md @@ -385,55 +385,73 @@ python manage.py test ## LDAP Configuration -The example settings are based on an OpenLDAP server with groups defined as "cn" of class "groupOfUniqueNames" +The config options below can be changed in `webvirtcloud/settings.py` file. Variants for Active Directory and OpenLDAP are shown. This is a minimal config to get LDAP running, for further info read the [django-auth-ldap documentation](https://django-auth-ldap.readthedocs.io). Enable LDAP ```bash -sudo sed -i "s/LDAP_ENABLED = False/LDAP_ENABLED = True/g"" /srv/webvirtcloud/webvirtcloud/settings.py +sudo sed -i "s~#\"django_auth_ldap.backend.LDAPBackend\",~\"django_auth_ldap.backend.LDAPBackend\",~g" /srv/webvirtcloud/webvirtcloud/settings.py ``` -Set the LDAP server name and root DN +Set the LDAP server name and bind DN -```bash -sudo sed -i "s/LDAP_URL = ''/LDAP_URL = 'myldap.server.com'/g"" /srv/webvirtcloud/webvirtcloud/settings.py -sudo sed -i "s/LDAP_ROOT_DN = ''/LDAP_ROOT_DN = 'dc=server,dc=com'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +```python +# Active Directory +AUTH_LDAP_SERVER_URI = "ldap://example.com" +AUTH_LDAP_BIND_DN = "username@example.com" +AUTH_LDAP_BIND_PASSWORD = "password" + +# OpenLDAP +AUTH_LDAP_SERVER_URI = "ldap://example.com" +AUTH_LDAP_BIND_DN = "CN=username,CN=Users,OU=example,OU=com" +AUTH_LDAP_BIND_PASSWORD = "password" ``` -Set the passphrase to decrypt the password -```bash -sudo sed -i "s/pass:MYPASSPHRASE/pass:MYTRUEPASSPHRASE/g" /srv/webvirtcloud/webvirtcloud/.dec_ldap_pwd.sh +Set the user filter and user and group search base and filter + +```python +# Active Directory +AUTH_LDAP_USER_SEARCH = LDAPSearch( + "CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)" +) +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + "CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(objectClass=group)" +) +AUTH_LDAP_GROUP_TYPE = NestedActiveDirectoryGroupType() + +# OpenLDAP +AUTH_LDAP_USER_SEARCH = LDAPSearch( + "CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(cn=%(user)s)" +) +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + "CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfUniqueNames)" +) +AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType() # import needs to be changed at the top of settings.py ``` -Encrypt the password -```bash -echo MYPASSWORD | openssl enc -pbkdf2 -salt -pass pass:MYTRUEPASSPHRASE | base64 +Set group which is required to access WebVirtCloud. You may set this to `False` to disable this filter. + +```python +AUTH_LDAP_REQUIRE_GROUP = "CN=WebVirtCloud Access,CN=Users,DC=example,DC=com" ``` -Set the user that has browse access to LDAP and its password encrypted +Populate user fields with values from LDAP -```bash -sudo sed -i "s/LDAP_MASTER_DN = ''/LDAP_MASTER_DN = 'cn=admin,ou=users,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py -sudo sed -i "s/LDAP_MASTER_PW_ENC = ''/LDAP_MASTER_PW_ENC = 'MYPASSWORDENCRYPTED'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +```python +AUTH_LDAP_USER_FLAGS_BY_GROUP = { + "is_staff": "CN=WebVirtCloud Staff,CN=Users,DC=example,DC=com", + "is_superuser": "CN=WebVirtCloud Admins,CN=Users,DC=example,DC=com", +} +AUTH_LDAP_USER_ATTR_MAP = { + "first_name": "givenName", + "last_name": "sn", + "email": "mail", +} ``` -Set the attribute that will be used to find the username, i usually use the cn +Now when you login with an LDAP user it will be assigned the rights defined. The user will be authenticated then with LDAP and authorized through the WebVirtCloud permissions. -```bash -sudo sed -i "s/LDAP_USER_UID_PREFIX = ''/LDAP_USER_UID_PREFIX = 'cn'/g"" /srv/webvirtcloud/webvirtcloud/settings.py -``` - -You can now create the filters to retrieve the users for the various group. This will be used during the user creation only - -```bash -sudo sed -i "s/LDAP_SEARCH_GROUP_FILTER_ADMINS = ''/LDAP_SEARCH_GROUP_FILTER_ADMINS = 'memberOf=cn=admins,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py -sudo sed -i "s/LDAP_SEARCH_GROUP_FILTER_STAFF = ''/LDAP_SEARCH_GROUP_FILTER_STAFF = 'memberOf=cn=staff,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py -sudo sed -i "s/LDAP_SEARCH_GROUP_FILTER_USERS = ''/LDAP_SEARCH_GROUP_FILTER_USERS = 'memberOf=cn=users,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py -``` - -Now when you login with an LDAP user it will be assigned the rights defined. The user will be authenticated then with ldap and authorized through the WebVirtCloud permissions. - -If you'd like to move a user from ldap to WebVirtCloud, just change its password from the UI and (eventually) remove from the group in ldap +If you'd like to move a user from ldap to WebVirtCloud, just change its password from the UI and (eventually) remove from the group in LDAP. ## REST API / BETA diff --git a/conf/requirements.txt b/conf/requirements.txt index 64206b8..a9390be 100644 --- a/conf/requirements.txt +++ b/conf/requirements.txt @@ -20,3 +20,4 @@ djangorestframework==3.14.0 drf-nested-routers==0.93.4 drf-yasg==1.21.7 markdown>=3.4.1 +django-auth-ldap==4.5.0 diff --git a/webvirtcloud/.dec_ldap_pwd.sh b/webvirtcloud/.dec_ldap_pwd.sh deleted file mode 100755 index 2cda920..0000000 --- a/webvirtcloud/.dec_ldap_pwd.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -##### - -# - -# LDAP PASSWORD DECRYPTION SCRIPT - -# - -# - -##### - -ENC_PASSWD=$1 - -echo $(echo $ENC_PASSWD | base64 -d | openssl enc -pbkdf2 -salt -d -pass pass:MYPASSPHRASE ) - diff --git a/webvirtcloud/ldapbackend.py b/webvirtcloud/ldapbackend.py deleted file mode 100644 index 0f7a797..0000000 --- a/webvirtcloud/ldapbackend.py +++ /dev/null @@ -1,142 +0,0 @@ -from django.contrib.auth.backends import ModelBackend -from django.contrib.auth.models import User, Group -from django.conf import settings -from accounts.models import UserAttributes, UserInstance, UserSSHKey -from django.contrib.auth.models import Permission -from logs.models import Logs -import uuid - -try: - from ldap3 import Server, Connection, ALL - #/srv/webvirtcloud/ldap/ldapbackend.py - class LdapAuthenticationBackend(ModelBackend): - - def get_LDAP_user(self, username, password, filterString): - print('get_LDAP_user {}'.format(username)) - try: - server = Server(settings.LDAP_URL, port=settings.LDAP_PORT, - use_ssl=settings.USE_SSL,get_info=ALL) - connection = Connection(server, - settings.LDAP_MASTER_DN, - settings.LDAP_MASTER_PW, auto_bind=True) - connection.search(settings.LDAP_ROOT_DN, - '(&({attr}={login})({filter}))'.format( - attr=settings.LDAP_USER_UID_PREFIX, - login=username, - filter=filterString), attributes=['*']) - - if len(connection.response) == 0: - print('get_LDAP_user-no response') - return None - specificUser = connection.response[0] - userDn = str(specificUser.get('raw_dn'),'utf-8') - userGivenName = connection.entries[0].givenName - userSn = connection.entries[0].sn - userMail = connection.entries[0].mail - with Connection(server, userDn, password) as con: - return username, userGivenName, userSn, userMail - except Exception as e: - print("LDAP Exception: {}".format(e)) - return None - return None - - def authenticate(self, request, username=None, password=None, **kwargs): - if not settings.LDAP_ENABLED: - return None - print("authenticate_ldap") - # Get the user information from the LDAP if he can be authenticated - isAdmin = False - isStaff = False - isTechnician = False - - requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_ADMINS) - isAdmin = requeteLdap is not None - isStaff = requeteLdap is not None - - if requeteLdap is None: - requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_STAFF) - isStaff = requeteLdap is not None - - if requeteLdap is None: - requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_TECHNICIANS) - isTechnician = requeteLdap is not None - - if requeteLdap is None: - requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_USERS) - - if requeteLdap is None: - print("User does not belong to any search group. Check LDAP_SEARCH_GROUP_FILTER in settings.") - return None - - techniciansGroup = Group.objects.get(name='Technicians') - - try: - user = User.objects.get(username=username) - attributes = UserAttributes.objects.get(user=user) - user.is_staff = isStaff - user.is_superuser = isAdmin - if not isTechnician and user.groups.filter(name='Technicians').exists(): - user.groups.remove(techniciansGroup) - elif isTechnician and not user.groups.filter(name='Technicians').exists(): - user.groups.add(techniciansGroup) - else: - print("The user is already in the Technicians group") - user.save() - # TODO VERIFY - except User.DoesNotExist: - print(f"authenticate-create new user: {username}") - user = User(username=username) - user.first_name = requeteLdap[1] - user.last_name = requeteLdap[2] - user.email = requeteLdap[3] - user.is_active = True - user.is_staff = isStaff - user.is_superuser = isAdmin - user.set_password(uuid.uuid4().hex) - user.save() - if isTechnician: - user.groups.add(techniciansGroup) - maxInstances = 1 - maxCpus = 1 - maxMemory = 128 - maxDiskSize = 1 - if isStaff: - maxMemory = 2048 - maxDiskSize = 20 - permission = Permission.objects.get(codename='clone_instances') - user.user_permissions.add(permission) - if isAdmin: - maxInstances = -1 - maxCpus = -1 - maxMemory = -1 - maxDiskSize = -1 - permission = Permission.objects.get(codename='clone_instances') - user.user_permissions.add(permission) - user.save() - UserAttributes.objects.create( - user=user, - max_instances=maxInstances, - max_cpus=maxCpus, - max_memory=maxMemory, - max_disk_size=maxDiskSize, - ) - user.save() - - print("authenticate-user created") - return user - - def get_user(self, user_id): - if not settings.LDAP_ENABLED: - return None - print("get_user_ldap") - try: - return User.objects.get(pk=user_id) - except User.DoesNotExist: - print("get_user-user not found") - return None -except: - class LdapAuthenticationBackend(ModelBackend): - def authenticate(self, request, username=None, password=None, **kwargs): - return None - def get_user(self, user_id): - return None diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index 50dce23..fabe94c 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -3,7 +3,9 @@ Django settings for webvirtcloud project. """ +import ldap import subprocess +from django_auth_ldap.config import LDAPSearch, NestedActiveDirectoryGroupType from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -101,7 +103,7 @@ DATABASES = { AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.ModelBackend", - "webvirtcloud.ldapbackend.LdapAuthenticationBackend", + #"django_auth_ldap.backend.LDAPBackend", ] LOGIN_URL = "/accounts/login/" @@ -280,27 +282,23 @@ EMAIL_HOST_PASSWORD = '' # LDAP Config # -LDAP_ENABLED = False -LDAP_URL = '' -LDAP_PORT = 389 -USE_SSL = False -## The user with search rights on ldap. (e.g cn=admin,dc=kendar,dc=org) -LDAP_MASTER_DN = '' -LDAP_MASTER_PW_ENC = '' -LDAP_MASTER_PW = subprocess.Popen(["bash", str(BASE_DIR) + "/webvirtcloud/.dec_ldap_pwd.sh", LDAP_MASTER_PW_ENC],stdout=subprocess.PIPE, encoding='utf8').stdout.read().strip('\n') -## The root dn (e.g. dc=kendar,dc=org) -LDAP_ROOT_DN = '' -## Queries to identify the users, i use groupOfUniqueNames on openldap - -### PLEASE BE SURE memberOf overlay is activated on slapd -## e.g. memberOf=cn=admins,cn=staff,cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org -LDAP_SEARCH_GROUP_FILTER_ADMINS = '' -## e.g. memberOf=cn=staff,cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org -LDAP_SEARCH_GROUP_FILTER_STAFF = '' -## e.g. memberOf=cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org -LDAP_SEARCH_GROUP_FILTER_TECHNICIANS = '' -## e.g. memberOf=cn=webvirtcloud,ou=groups,dc=kendar,dc=org -LDAP_SEARCH_GROUP_FILTER_USERS = '' - -## The user name prefix to identify the user name (e.g. cn) -LDAP_USER_UID_PREFIX = '' +AUTH_LDAP_SERVER_URI = "ldap://example.com" +AUTH_LDAP_BIND_DN = "username@example.com" +AUTH_LDAP_BIND_PASSWORD = "password" +AUTH_LDAP_USER_SEARCH = LDAPSearch( + "CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)" +) +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + "CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(objectClass=group)" +) +AUTH_LDAP_GROUP_TYPE = NestedActiveDirectoryGroupType() +AUTH_LDAP_REQUIRE_GROUP = "CN=WebVirtCloud Access,CN=Users,DC=example,DC=com" +AUTH_LDAP_USER_FLAGS_BY_GROUP = { + "is_staff": "CN=WebVirtCloud Staff,CN=Users,DC=example,DC=com", + "is_superuser": "CN=WebVirtCloud Admins,CN=Users,DC=example,DC=com", +} +AUTH_LDAP_USER_ATTR_MAP = { + "first_name": "givenName", + "last_name": "sn", + "email": "mail", +}