mirror of
https://github.com/retspen/webvirtcloud
synced 2025-02-05 20:15:19 +00:00
Compare commits
No commits in common. "ca54ae0c653f5872989b7c40dc43bad39e77cf26" and "5e368d1ee05d7d5e0617c0f7dbc0a4639f56277f" have entirely different histories.
ca54ae0c65
...
5e368d1ee0
5 changed files with 217 additions and 74 deletions
82
README.md
82
README.md
|
@ -385,73 +385,55 @@ python manage.py test
|
||||||
|
|
||||||
## LDAP Configuration
|
## LDAP Configuration
|
||||||
|
|
||||||
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).
|
The example settings are based on an OpenLDAP server with groups defined as "cn" of class "groupOfUniqueNames"
|
||||||
|
|
||||||
Enable LDAP
|
Enable LDAP
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo sed -i "s~#\"django_auth_ldap.backend.LDAPBackend\",~\"django_auth_ldap.backend.LDAPBackend\",~g" /srv/webvirtcloud/webvirtcloud/settings.py
|
sudo sed -i "s/LDAP_ENABLED = False/LDAP_ENABLED = True/g"" /srv/webvirtcloud/webvirtcloud/settings.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Set the LDAP server name and bind DN
|
Set the LDAP server name and root DN
|
||||||
|
|
||||||
```python
|
```bash
|
||||||
# Active Directory
|
sudo sed -i "s/LDAP_URL = ''/LDAP_URL = 'myldap.server.com'/g"" /srv/webvirtcloud/webvirtcloud/settings.py
|
||||||
AUTH_LDAP_SERVER_URI = "ldap://example.com"
|
sudo sed -i "s/LDAP_ROOT_DN = ''/LDAP_ROOT_DN = 'dc=server,dc=com'/g"" /srv/webvirtcloud/webvirtcloud/settings.py
|
||||||
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 user filter and user and group search base and filter
|
Set the passphrase to decrypt the password
|
||||||
|
```bash
|
||||||
```python
|
sudo sed -i "s/pass:MYPASSPHRASE/pass:MYTRUEPASSPHRASE/g" /srv/webvirtcloud/webvirtcloud/.dec_ldap_pwd.sh
|
||||||
# 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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Set group which is required to access WebVirtCloud. You may set this to `False` to disable this filter.
|
Encrypt the password
|
||||||
|
```bash
|
||||||
```python
|
echo MYPASSWORD | openssl enc -pbkdf2 -salt -pass pass:MYTRUEPASSPHRASE | base64
|
||||||
AUTH_LDAP_REQUIRE_GROUP = "CN=WebVirtCloud Access,CN=Users,DC=example,DC=com"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Populate user fields with values from LDAP
|
Set the user that has browse access to LDAP and its password encrypted
|
||||||
|
|
||||||
```python
|
```bash
|
||||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
sudo sed -i "s/LDAP_MASTER_DN = ''/LDAP_MASTER_DN = 'cn=admin,ou=users,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py
|
||||||
"is_staff": "CN=WebVirtCloud Staff,CN=Users,DC=example,DC=com",
|
sudo sed -i "s/LDAP_MASTER_PW_ENC = ''/LDAP_MASTER_PW_ENC = 'MYPASSWORDENCRYPTED'/g"" /srv/webvirtcloud/webvirtcloud/settings.py
|
||||||
"is_superuser": "CN=WebVirtCloud Admins,CN=Users,DC=example,DC=com",
|
|
||||||
}
|
|
||||||
AUTH_LDAP_USER_ATTR_MAP = {
|
|
||||||
"first_name": "givenName",
|
|
||||||
"last_name": "sn",
|
|
||||||
"email": "mail",
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
Set the attribute that will be used to find the username, i usually use the cn
|
||||||
|
|
||||||
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.
|
```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
|
||||||
|
|
||||||
|
|
||||||
## REST API / BETA
|
## REST API / BETA
|
||||||
|
|
|
@ -20,4 +20,3 @@ djangorestframework==3.14.0
|
||||||
drf-nested-routers==0.93.4
|
drf-nested-routers==0.93.4
|
||||||
drf-yasg==1.21.7
|
drf-yasg==1.21.7
|
||||||
markdown>=3.4.1
|
markdown>=3.4.1
|
||||||
django-auth-ldap==4.5.0
|
|
||||||
|
|
18
webvirtcloud/.dec_ldap_pwd.sh
Executable file
18
webvirtcloud/.dec_ldap_pwd.sh
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#####
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
# LDAP PASSWORD DECRYPTION SCRIPT
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
#####
|
||||||
|
|
||||||
|
ENC_PASSWD=$1
|
||||||
|
|
||||||
|
echo $(echo $ENC_PASSWD | base64 -d | openssl enc -pbkdf2 -salt -d -pass pass:MYPASSPHRASE )
|
||||||
|
|
142
webvirtcloud/ldapbackend.py
Normal file
142
webvirtcloud/ldapbackend.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
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
|
|
@ -3,9 +3,7 @@ Django settings for webvirtcloud project.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import ldap
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from django_auth_ldap.config import LDAPSearch, NestedActiveDirectoryGroupType
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
@ -103,7 +101,7 @@ DATABASES = {
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
"django.contrib.auth.backends.ModelBackend",
|
"django.contrib.auth.backends.ModelBackend",
|
||||||
#"django_auth_ldap.backend.LDAPBackend",
|
"webvirtcloud.ldapbackend.LdapAuthenticationBackend",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGIN_URL = "/accounts/login/"
|
LOGIN_URL = "/accounts/login/"
|
||||||
|
@ -282,23 +280,27 @@ EMAIL_HOST_PASSWORD = ''
|
||||||
# LDAP Config
|
# LDAP Config
|
||||||
#
|
#
|
||||||
|
|
||||||
AUTH_LDAP_SERVER_URI = "ldap://example.com"
|
LDAP_ENABLED = False
|
||||||
AUTH_LDAP_BIND_DN = "username@example.com"
|
LDAP_URL = ''
|
||||||
AUTH_LDAP_BIND_PASSWORD = "password"
|
LDAP_PORT = 389
|
||||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
USE_SSL = False
|
||||||
"CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)"
|
## The user with search rights on ldap. (e.g cn=admin,dc=kendar,dc=org)
|
||||||
)
|
LDAP_MASTER_DN = ''
|
||||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
|
LDAP_MASTER_PW_ENC = ''
|
||||||
"CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(objectClass=group)"
|
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)
|
||||||
AUTH_LDAP_GROUP_TYPE = NestedActiveDirectoryGroupType()
|
LDAP_ROOT_DN = ''
|
||||||
AUTH_LDAP_REQUIRE_GROUP = "CN=WebVirtCloud Access,CN=Users,DC=example,DC=com"
|
## Queries to identify the users, i use groupOfUniqueNames on openldap
|
||||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
|
||||||
"is_staff": "CN=WebVirtCloud Staff,CN=Users,DC=example,DC=com",
|
### PLEASE BE SURE memberOf overlay is activated on slapd
|
||||||
"is_superuser": "CN=WebVirtCloud Admins,CN=Users,DC=example,DC=com",
|
## e.g. memberOf=cn=admins,cn=staff,cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org
|
||||||
}
|
LDAP_SEARCH_GROUP_FILTER_ADMINS = ''
|
||||||
AUTH_LDAP_USER_ATTR_MAP = {
|
## e.g. memberOf=cn=staff,cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org
|
||||||
"first_name": "givenName",
|
LDAP_SEARCH_GROUP_FILTER_STAFF = ''
|
||||||
"last_name": "sn",
|
## e.g. memberOf=cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org
|
||||||
"email": "mail",
|
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 = ''
|
||||||
|
|
Loading…
Reference in a new issue