Commit 6f558426 by Czémán Arnold

Add LDAP and AD authentication

Issue: #477, #478
parent c177b3e8
Pipeline #364 passed with stage
in 0 seconds
...@@ -413,6 +413,11 @@ LOGGING = { ...@@ -413,6 +413,11 @@ LOGGING = {
'level': 'INFO', 'level': 'INFO',
'propagate': True, 'propagate': True,
}, },
'django_auth_ldap': {
'handlers': ['syslog'],
'level': 'DEBUG',
'propagate': True,
},
} }
} }
########## END LOGGING CONFIGURATION ########## END LOGGING CONFIGURATION
...@@ -446,6 +451,12 @@ CACHES = { ...@@ -446,6 +451,12 @@ CACHES = {
} }
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
######### SAML2 AUTHENTICATION
if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE': if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
try: try:
from shutil import which # python >3.4 from shutil import which # python >3.4
...@@ -456,8 +467,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE': ...@@ -456,8 +467,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
INSTALLED_APPS += ( INSTALLED_APPS += (
'djangosaml2', 'djangosaml2',
) )
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS += (
'django.contrib.auth.backends.ModelBackend',
'common.backends.Saml2Backend', 'common.backends.Saml2Backend',
) )
...@@ -575,3 +585,72 @@ REQUEST_HOOK_URL = get_env_variable("REQUEST_HOOK_URL", "") ...@@ -575,3 +585,72 @@ REQUEST_HOOK_URL = get_env_variable("REQUEST_HOOK_URL", "")
SSHKEY_EMAIL_ADD_KEY = False SSHKEY_EMAIL_ADD_KEY = False
TWO_FACTOR_ISSUER = get_env_variable("TWO_FACTOR_ISSUER", "CIRCLE") TWO_FACTOR_ISSUER = get_env_variable("TWO_FACTOR_ISSUER", "CIRCLE")
######### LDAP AUTHENTICATION
if get_env_variable('LDAP_AUTH', 'FALSE') == 'TRUE':
import ldap
from django_auth_ldap.config import (
LDAPSearch, GroupOfNamesType, PosixGroupType, ActiveDirectoryGroupType
)
LDAP_SCOPE_MAP = {
"SUBTREE": ldap.SCOPE_SUBTREE,
"BASE": ldap.SCOPE_BASE,
"ONELEVEL": ldap.SCOPE_SUBTREE
}
LDAP_GROUP_MAP = {
"POSIX": PosixGroupType(),
"AD": ActiveDirectoryGroupType(),
"GROUP_OF_NAMES": GroupOfNamesType(),
}
# Baseline configuration.
AUTH_LDAP_SERVER_URI = get_env_variable("LDAP_SERVER_URI", "")
AUTH_LDAP_BIND_DN = get_env_variable("LDAP_BIND_DN", "")
AUTH_LDAP_BIND_PASSWORD = get_env_variable("LDAP_BIND_PASSWORD", "")
LDAP_USER_BASE_DN = get_env_variable("LDAP_USER_BASE_DN")
LDAP_USER_SCOPE = LDAP_SCOPE_MAP.get(
get_env_variable("LDAP_USER_SCOPE", ""))
LDAP_USER_FILTER = get_env_variable("LDAP_USER_FILTER")
AUTH_LDAP_USER_SEARCH = LDAPSearch(LDAP_USER_BASE_DN,
LDAP_USER_SCOPE,
LDAP_USER_FILTER)
# Set up the basic group parameters.
LDAP_GROUP_BASE_DN = get_env_variable("LDAP_GROUP_BASE_DN")
LDAP_GROUP_SCOPE = LDAP_SCOPE_MAP.get(
get_env_variable("LDAP_GROUP_SCOPE", ""))
LDAP_GROUP_FILTER = get_env_variable("LDAP_GROUP_FILTER")
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(LDAP_GROUP_BASE_DN,
LDAP_GROUP_SCOPE,
LDAP_GROUP_FILTER)
LDAP_GROUP_TYPE = get_env_variable("LDAP_GROUP_TYPE", "")
AUTH_LDAP_GROUP_TYPE = LDAP_GROUP_MAP.get(LDAP_GROUP_TYPE, "POSIX")
# Populate the Django user from the LDAP directory.
AUTH_LDAP_USER_ATTR_MAP = loads(get_env_variable("LDAP_USER_ATTR_MAP",
'{"first_name": "givenName", "last_name": "sn", "email": "mail"}'))
AUTH_LDAP_FIND_GROUP_PERMS = False
# Cache group memberships for an hour to minimize LDAP traffic
AUTH_LDAP_GROUP_CACHE_TIMEOUT = int(get_env_variable(
"LDAP_GROUP_CACHE_TIMEOUT", 0))
if AUTH_LDAP_GROUP_CACHE_TIMEOUT != 0:
AUTH_LDAP_CACHE_GROUPS = False
# Add LDAP backend
AUTHENTICATION_BACKENDS += (
'django_auth_ldap.backend.LDAPBackend',
)
# org_id attribute
if get_env_variable('LDAP_ORG_ID_ATTRIBUTE', False):
LDAP_ORG_ID_ATTRIBUTE = get_env_variable(
'LDAP_ORG_ID_ATTRIBUTE')
...@@ -341,22 +341,23 @@ def create_profile_hook(sender, user, request, **kwargs): ...@@ -341,22 +341,23 @@ def create_profile_hook(sender, user, request, **kwargs):
user_logged_in.connect(create_profile_hook) user_logged_in.connect(create_profile_hook)
if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'): if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
logger.debug("Register save_org_id to djangosaml2 pre_user_save") logger.debug("Register saml_save_org_id to djangosaml2 pre_user_save")
from djangosaml2.signals import pre_user_save from djangosaml2.signals import pre_user_save
def save_org_id(sender, **kwargs): def saml_save_org_id(sender, **kwargs):
logger.debug("save_org_id called by %s", sender.username) logger.debug("saml_save_org_id called by %s", sender.username)
attributes = kwargs.pop('attributes') attributes = kwargs.pop('attributes')
atr = settings.SAML_ORG_ID_ATTRIBUTE atr = settings.SAML_ORG_ID_ATTRIBUTE
try: try:
value = attributes[atr][0].upper() value = attributes[atr][0].upper()
except Exception as e: except Exception as e:
value = None value = None
logger.info("save_org_id couldn't find attribute. %s", unicode(e)) logger.info("saml_save_org_id couldn't find attribute. %s",
unicode(e))
if sender.pk is None: if sender.pk is None:
sender.save() sender.save()
logger.debug("save_org_id saved user %s", unicode(sender)) logger.debug("saml_save_org_id saved user %s", unicode(sender))
profile, created = Profile.objects.get_or_create(user=sender) profile, created = Profile.objects.get_or_create(user=sender)
if created or profile.org_id != value: if created or profile.org_id != value:
...@@ -397,7 +398,55 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'): ...@@ -397,7 +398,55 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
return False # User did not change return False # User did not change
pre_user_save.connect(save_org_id) pre_user_save.connect(saml_save_org_id)
if hasattr(settings, 'LDAP_ORG_ID_ATTRIBUTE'):
logger.debug("Register ldap_save_org_id to django-ldap-auth populate user")
from django_auth_ldap.backend import populate_user
def ldap_save_org_id(sender, user, ldap_user, **kwargs):
logger.debug("ldap_save_org_id called by %s", user.username)
attributes = ldap_user.attrs
attr = settings.LDAP_ORG_ID_ATTRIBUTE
try:
value = attributes[attr][0].upper()
except Exception as e:
value = None
logger.info("ldap_save_org_id couldn't find attribute. %s",
unicode(e))
if user.pk is None:
user.save()
logger.debug("ldap_save_org_id saved user %s", unicode(user))
profile, created = Profile.objects.get_or_create(user=user)
if created or profile.org_id != value:
logger.info("org_id of %s added to user %s's profile",
value, user.username)
profile.org_id = value
profile.save()
else:
logger.debug("org_id of %s already added to user %s's profile",
value, user.username)
logger.error(ldap_user.group_dns)
for group in ldap_user.group_names:
try:
g = GroupProfile.search(group)
except Group.DoesNotExist:
logger.debug('cant find membergroup %s', group)
else:
logger.debug('could find membergroup %s (%s)',
group, unicode(g))
g.user_set.add(user)
for i in FutureMember.objects.filter(org_id__iexact=value):
i.group.user_set.add(user)
i.delete()
return False # User did not change
populate_user.connect(ldap_save_org_id)
def update_store_profile(sender, **kwargs): def update_store_profile(sender, **kwargs):
......
...@@ -42,7 +42,7 @@ from ..models import FutureMember, GroupProfile ...@@ -42,7 +42,7 @@ from ..models import FutureMember, GroupProfile
from vm.models import Instance, InstanceTemplate from vm.models import Instance, InstanceTemplate
from ..tables import GroupListTable from ..tables import GroupListTable
from .util import (CheckedDetailView, AclUpdateView, search_user, from .util import (CheckedDetailView, AclUpdateView, search_user,
saml_available, DeleteViewBase) saml_available, DeleteViewBase, external_auth_available)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -145,7 +145,7 @@ class GroupDetailView(CheckedDetailView): ...@@ -145,7 +145,7 @@ class GroupDetailView(CheckedDetailView):
entity = search_user(name) entity = search_user(name)
self.object.user_set.add(entity) self.object.user_set.add(entity)
except User.DoesNotExist: except User.DoesNotExist:
if saml_available: if external_auth_available():
FutureMember.objects.get_or_create(org_id=name.upper(), FutureMember.objects.get_or_create(org_id=name.upper(),
group=self.object) group=self.object)
else: else:
......
...@@ -60,6 +60,10 @@ logger = logging.getLogger(__name__) ...@@ -60,6 +60,10 @@ logger = logging.getLogger(__name__)
saml_available = hasattr(settings, "SAML_CONFIG") saml_available = hasattr(settings, "SAML_CONFIG")
def external_auth_available():
return saml_available or hasattr(settings, "AUTH_LDAP_SERVER_URI")
class RedirectToLoginMixin(AccessMixin): class RedirectToLoginMixin(AccessMixin):
redirect_exception_classes = (PermissionDenied, ) redirect_exception_classes = (PermissionDenied, )
......
...@@ -42,3 +42,5 @@ pika==0.9.14 ...@@ -42,3 +42,5 @@ pika==0.9.14
django-pipeline==1.4.7 django-pipeline==1.4.7
Fabric==1.10.1 Fabric==1.10.1
lxml==3.4.4 lxml==3.4.4
django-auth-ldap==1.2.8
python-ldap==2.4.30
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment