class LdapUser(ldapdb.models.Model): class Meta: managed = False rdn_keys = ['username'] # inetOrgPerson first_name = ldap_fields.CharField(db_column='givenName') last_name = ldap_fields.CharField(db_column='sn') full_name = ldap_fields.CharField(db_column='cn') email = ldap_fields.CharField(db_column='mail') # posixAccount username = ldap_fields.CharField(db_column='uid') # ldap specific modified_date = ldap_fields.DateTimeField(db_column='modifytimestamp',blank=True) def __str__(self): return self.username def __str__(self): return self.full_name def save(self,*args,**kwargs): force_insert = kwargs.pop('force_insert',None) super(LdapUser,self).save(*args,**kwargs) class Meta: abstract=True
class Maintainer(ldapdb.models.Model): """A tool maintainer.""" base_dn = settings.TOOLS_MAINTAINER_BASE_DN object_classes = ['posixAccount'] username = fields.CharField(db_column='uid', primary_key=True) full_name = fields.CharField(db_column='cn') def __str__(self): return self.username
class LdapMultiPKRoom(ldapdb.models.Model): """ Class for representing a room, using a composite primary key. """ # LDAP meta-data base_dn = "ou=rooms,dc=example,dc=org" object_classes = ['room'] # room attributes name = fields.CharField(db_column='cn', max_length=200, primary_key=True) number = fields.CharField(db_column='roomNumber', max_length=10, primary_key=True) phone = fields.CharField(db_column='telephoneNumber', max_length=20, blank=True, null=True) def __str__(self): return "%s (%s)" % (self.name, self.number)
class FakeModel(models.Model): class Meta: abstract = True base_dn = 'ou=test,dc=example,dc=org' object_classes = ['inetOrgPerson'] name = fields.CharField(db_column='cn')
class Tool(ldapdb.models.Model): """A tool is a specially named LDAP group.""" base_dn = settings.TOOLS_TOOL_BASE_DN object_classes = ['posixGroup', 'groupOfNames'] objects = ToolManager() group_name = fields.CharField(db_column='cn', max_length=200, primary_key=True) gid = fields.IntegerField(db_column='gidNumber', unique=True) maintainer_ids = fields.ListField(db_column='member') @property def name(self): return self.group_name[6:] @name.setter def name(self, value): self.group_name = 'tools.{0!s}'.format(value) def maintainers(self): # OMG, this is horrible. You can't search LDAP by dn. return Maintainer.objects.filter( username__in=(dn.split(',')[0].split('=')[1] for dn in self.maintainer_ids)) def __str__(self): return self.name
class CuLdapUser(LdapUser): class Meta: managed = False base_dn = settings.LDAPCONFS['culdap']['people_dn'] object_classes = [] uid = ldap_fields.IntegerField(db_column='uidNumber', unique=True) # Used for automatic determination of role and affiliation. edu_affiliation = ldap_fields.ListField(db_column='eduPersonAffiliation') edu_primary_affiliation = ldap_fields.CharField(db_column='eduPersonPrimaryAffiliation') cu_primary_major = ldap_fields.CharField(db_column='cuEduPersonPrimaryMajor1') cu_home_department = ldap_fields.CharField(db_column='cuEduPersonHomeDepartment') @sensitive_variables('pwd') def authenticate(self,pwd): authed = ldap_utils.authenticate(self.dn,pwd,'culdap') logger = logging.getLogger('accounts') logger.info('CU user {} auth attempt: {}'.format(self.username, authed)) return authed
class RepositoryLdapUser(ldapdb.models.Model): """ Class for representing a repository user in LDAP """ # pylint: disable=W0222 # LDAP meta-data base_dn = settings.BORGHIVE['LDAP_USER_BASEDN'] object_classes = ['organizationalPerson', 'posixAccount', 'shadowAccount'] last_modified = fields.DateTimeField(db_column='modifyTimestamp') # posixAccount uid = fields.IntegerField(db_column='uidNumber', unique=True) group = fields.IntegerField(db_column='gidNumber') gecos = fields.CharField(db_column='gecos', default='Borghive Repo User') home = fields.CharField(db_column='homeDirectory', default=settings.BORGHIVE['REPO_PATH']) shell = fields.CharField(db_column='loginShell', default='/bin/bash') username = fields.CharField(db_column='uid', primary_key=True) sn = fields.CharField(db_column='sn', default='') cn = fields.CharField(db_column='cn', default='') def save(self, *args, **kwargs): self.home = os.path.join(settings.BORGHIVE['REPO_PATH'], self.username) self.sn = self.username self.cn = self.username super().save(*args, **kwargs) def __str__(self): return 'RepositoryLdapUser: {0.dn}: uid={0.uid} gid={0.group}'.format( self)
class AbstractGroup(ldapdb.models.Model): class Meta: abstract = True object_classes = ['posixGroup'] gid = fields.IntegerField(db_column='gidNumber', unique=True) name = fields.CharField(db_column='cn', max_length=200, primary_key=True) usernames = fields.ListField(db_column='memberUid') def __str__(self): return self.name def __unicode__(self): return self.name
class LdapGroup(ldapdb.models.Model): """ Class for representing an LDAP group entry. """ class Meta: verbose_name = "LDAP group" verbose_name_plural = "LDAP groups" # LDAP meta-data base_dn = settings.REG_GROUP_BASE_DN object_classes = settings.REG_GROUP_OBJECT_CLASSES # LDAP group attributes cn = ldapdb_fields.CharField(db_column="cn", max_length=200, primary_key=True) description = ldapdb_fields.CharField(db_column="description", max_length=200) members = ldapdb_fields.ListField(db_column="member") def __str__(self): return self.cn def __unicode__(self): return self.cn
class LdapGroup(ldapdb.models.Model): """ Class for representing an LDAP group entry. """ # LDAP meta-data base_dn = "ou=groups,dc=example,dc=org" object_classes = ['posixGroup'] # posixGroup attributes gid = fields.IntegerField(db_column='gidNumber', unique=True) name = fields.CharField(db_column='cn', max_length=200, primary_key=True) usernames = fields.ListField(db_column='memberUid') def __str__(self): return self.name def __unicode__(self): return self.name
class RCLdapUser(ldapdb.models.Model): """ Class for representing an LDAP user entry. """ rdn_keys = ['username'] # LDAP meta-data base_dn = 'ou=users,dc=example,dc=org' object_classes = ['posixAccount', 'inetOrgPerson'] last_modified = fields.DateTimeField(db_column='modifyTimestamp') organization = fields.CharField(max_length=128,blank=False,null=False) # inetOrgPerson first_name = fields.CharField(db_column='givenName', verbose_name="Prime name") last_name = fields.CharField("Final name", db_column='sn') full_name = fields.CharField(db_column='cn') email = fields.CharField(db_column='mail') # posixAccount uid = fields.IntegerField(db_column='uidNumber', unique=True) group = fields.IntegerField(db_column='gidNumber') gecos = fields.CharField(db_column='gecos') home_directory = fields.CharField(db_column='homeDirectory') login_shell = fields.CharField(db_column='loginShell', default='/bin/bash') username = fields.CharField(db_column='uid') @property def organization(self): return self.org def __str__(self): return self.username def __unicode__(self): return self.full_name
class LdapUser(Model): """ Class for representing an LDAP user entry. """ # LDAP meta-data ROOT_DN = os.environ.get('LDAP_USER_ENTRY', 'dc=test,dc=de') base_dn = ROOT_DN object_classes = ['inetOrgPerson'] # last_modified = ldap_fields.DateTimeField(db_column='modifyTimestamp', blank=True) # inetOrgPerson username = ldap_fields.CharField(db_column='uid', primary_key=True) display_name = ldap_fields.CharField(db_column='displayName', blank=True) password = ldap_fields.CharField(db_column='userPassword') first_name = ldap_fields.CharField(db_column='cn', blank=True) last_name = ldap_fields.CharField(db_column='sn', blank=True) email = ldap_fields.CharField(db_column='mail') phone = ldap_fields.CharField(db_column='telephoneNumber', blank=True) mobile_phone = ldap_fields.CharField(db_column='mobile', blank=True) photo = ldap_fields.ImageField(db_column='photo') last_login = ldap_fields.DateTimeField(db_column='authTimestamp', blank=True) # photo = ldap_fields.ImageField(db_column='jpegPhoto') def __str__(self): return self.username def __unicode__(self): return self.full_name @staticmethod def create_with_django_user_creation_and_welcome_mail( realm, protocol, domain, username, email): ldap_user, user = LdapUser.create_with_django_user( realm, username, email) send_welcome_mail(domain, email, protocol, realm, user) return ldap_user @staticmethod def create_with_django_user(realm, username, email, password=None): if not LdapUser.is_user_duplicate(username): LdapUser.base_dn = f'ou=people, {realm.ldap_base_dn}' # TODO: rewrite if password: ldap_user = LdapUser.objects.create(username=username, email=email, first_name=" ", last_name=" ", password=password) else: ldap_user = LdapUser.objects.create(username=username, email=email, first_name=" ", last_name=" ") if password: user, _ = User.objects.get_or_create(username=username, email=email, password=password) else: user, _ = User.objects.get_or_create(username=username, email=email) return ldap_user, user else: raise ALREADY_EXISTS('User already exists') @staticmethod def get_extended_user(ldap_user): wrapper = {'user': ldap_user} try: wrapper['deleted_user'] = DeletedUser.objects.get( ldap_dn=ldap_user.dn) except ObjectDoesNotExist: wrapper['deleted_user'] = {} try: django_user = User.objects.get(username=ldap_user.username) wrapper['active'] = True if django_user.last_login else False except ObjectDoesNotExist: wrapper['active'] = False return wrapper @staticmethod def password_reset(user, raw_password): LdapUser.base_dn = LdapUser.ROOT_DN ldap_user = LdapUser.objects.get(username=user.username) ldap_user.password = raw_password LdapUser.base_dn = re.compile('(uid=[a-zA-Z0-9_-]*),(.*)').match( ldap_user.dn).group(2) ldap_user.save() @staticmethod def get_users_by_dn(realm, users): LdapGroup.base_dn = f'ou=groups,{realm.ldap_base_dn}' # logger.debug(users) users = [ re.compile('uid=([a-zA-Z0-9_-]*),(ou=[a-zA-Z_]*),(.*)').match( user).group(1) for user in users ] query = Q(username=users.pop()) for user in users: query = query | Q(username=user) LdapUser.base_dn = LdapUser.ROOT_DN return LdapUser.objects.filter(query) @staticmethod def is_user_duplicate(username: str): LdapUser.base_dn = LdapUser.ROOT_DN try: LdapUser.objects.get(username=username) return True except (NO_SUCH_OBJECT, ObjectDoesNotExist) as err: return False @staticmethod def is_active_user(ldap_user): try: django_user = User.objects.get(username=ldap_user.username) return django_user.last_login except ObjectDoesNotExist: return False @staticmethod def get_user_active_marked(ldap_users): user_wrappers = [] for user in ldap_users: if LdapUser.is_active_user(user): user_wrappers.append({'user': user, 'active': True}) else: user_wrappers.append({'user': user, 'active': False}) return user_wrappers def get_users_realm_base_dn(self): return re.compile('(uid=[a-zA-Z0-9_-]*),(ou=[a-zA-Z_-]*),(.*)').match( self.dn).group(3) @staticmethod def get_user(username: str, realm: Realm = None): LdapUser.base_dn = f'ou=people, {realm.ldap_base_dn}' if realm else LdapUser.ROOT_DN try: return LdapUser.objects.get(username=username) except Exception as e: return None @staticmethod def get_user_by_dn(dn: str, realm: Realm = None): LdapUser.base_dn = f'ou=people, {realm.ldap_base_dn}' if realm else LdapUser.ROOT_DN try: return LdapUser.objects.get(dn=dn) except Exception as e: return None @staticmethod def get_users(realm: Realm = None): LdapUser.base_dn = f'ou=people, {realm.ldap_base_dn}' if realm else LdapUser.ROOT_DN try: return LdapUser.objects.all() except Exception as e: return None @staticmethod def get_inactive_users(realm: Realm = None): LdapUser.base_dn = realm.ldap_base_dn if realm else LdapUser.ROOT_DN last_semester = datetime.now() - timedelta(days=182) return (LdapUser.objects.filter(last_login__lte=last_semester) | LdapUser.objects.exclude(last_login__lte=datetime.now() + timedelta(days=1))) def get_django_user(self): try: return User.objects.get(username=self.username) except ObjectDoesNotExist: return None def get_deletable(self): try: return DeletedUser.objects.get(ldap_dn=self.dn) except ObjectDoesNotExist: return None def delete_complete(self): django_user = self.get_django_user() deletable_user = self.get_deletable() LdapGroup.remove_user_from_groups(self.dn) self.delete() if django_user: django_user.delete() if deletable_user: deletable_user.delete() @staticmethod def set_root_dn(realm): LdapUser.base_dn = f'ou=people,{realm.ldap_base_dn}'
def test_char_field_max_length(self): self.assertEqual(fields.CharField(max_length=42).max_length, 42)
class LdapUser(ldapdb.models.Model): """ Class for representing an LDAP user entry. """ # LDAP meta-data base_dn = "ou=people,dc=example,dc=org" object_classes = ['posixAccount', 'shadowAccount', 'inetOrgPerson'] last_modified = fields.DateTimeField(db_column='modifyTimestamp') # inetOrgPerson first_name = fields.CharField(db_column='givenName', verbose_name="Prime name") last_name = fields.CharField("Final name", db_column='sn') full_name = fields.CharField(db_column='cn') email = fields.CharField(db_column='mail') phone = fields.CharField(db_column='telephoneNumber', blank=True) mobile_phone = fields.CharField(db_column='mobile', blank=True) photo = fields.ImageField(db_column='jpegPhoto') # posixAccount uid = fields.IntegerField(db_column='uidNumber', unique=True) group = fields.IntegerField(db_column='gidNumber') gecos = fields.CharField(db_column='gecos') home_directory = fields.CharField(db_column='homeDirectory') login_shell = fields.CharField(db_column='loginShell', default='/bin/bash') username = fields.CharField(db_column='uid', primary_key=True) password = fields.CharField(db_column='userPassword') # shadowAccount last_password_change = fields.TimestampField(db_column='shadowLastChange') def __str__(self): return self.username def __unicode__(self): return self.full_name
class RcLdapUser(LdapUser): class Meta: verbose_name = 'LDAP user' verbose_name_plural = 'LDAP users' managed = False def __init__(self,*args,**kwargs): super(RcLdapUser,self).__init__(*args,**kwargs) rdn = self.dn.lower().replace(self.base_dn.lower(), '') rdn_list = rdn.split(',') self.org = '' if len(rdn_list) > 1: ou = self.base_dn.lower().split(',')[0] __, org = ou.split('=') self.org = org self.base_dn = self.base_dn.lower() objects = RcLdapUserManager() base_dn = str(settings.LDAPCONFS['rcldap']['people_dn']) object_classes = list(map(str, ['top','person','inetorgperson','posixaccount','curcPerson','shadowAccount'])) expires = ldap_fields.IntegerField(db_column='shadowExpire',blank=True,null=True) uid = ldap_fields.IntegerField(db_column='uidNumber',null=True,blank=True) gid = ldap_fields.IntegerField(db_column='gidNumber',null=True,blank=True) gecos = ldap_fields.CharField(db_column='gecos',default='') home_directory = ldap_fields.CharField(db_column='homeDirectory') login_shell = ldap_fields.CharField(db_column='loginShell', default='/bin/bash') #curcPerson attributes role = ldap_fields.ListField(db_column='curcRole',blank=True,null=True) affiliation = ldap_fields.ListField(db_column='curcAffiliation',blank=True,null=True) @property def organization(self): return self.org @property def effective_uid(self): suffixed_username = ldap_utils.get_suffixed_username(self.username,self.organization) return suffixed_username def _set_base_dn(self,org): if org in list(map(str, list(settings.ORGANIZATION_INFO.keys()))): ou = 'ou={}'.format(org) self.org = org if ou not in self.base_dn.lower(): self.base_dn = ','.join([ou,self.base_dn]) else: raise ValueError('Invalid organization specified: {}'.format(org)) def save(self,*args,**kwargs): org = str(kwargs.pop('organization', None)) if not org: raise ValueError('No organization specified.') self._set_base_dn(org) # If no UID/GID specified, auto-assign logger = logging.getLogger('accounts') if (self.uid == None) and (self.gid == None): id_tracker = IdTracker.objects.get(category='posix') uid = id_tracker.get_next_id() self.uid = uid self.gid = uid logger.info('Auto-assigning UID and GID to user: {}, {}'.format(uid, self.effective_uid)) elif self.uid == None: self.uid = self.gid logger.info('Auto-assigning UID to user: {}, {}'.format(self.gid, self.effective_uid)) elif self.gid == None: self.gid = self.uid logger.info('Auto-assigning GID to user: {}, {}'.format(self.uid, self.effective_uid)) super(RcLdapUser,self).save(*args,**kwargs)
class LdapGroup(Model): """ Class for representing an LDAP group entry. """ # LDAP meta-data ROOT_DN = os.environ.get('LDAP_USER_ENTRY', 'dc=test,dc=de') base_dn = ROOT_DN object_classes = ['groupOfNames'] name = ldap_fields.CharField(db_column='cn', max_length=200, primary_key=True) description = ldap_fields.CharField(db_column='description', max_length=1024) members = ldap_fields.ListField(db_column='member') @staticmethod def get_user_groups(realm: Realm, ldap_user: LdapUser): LdapUser.base_dn = f'ou=people,{realm.ldap_base_dn}' LdapGroup.base_dn = LdapGroup.ROOT_DN return LdapGroup.objects.filter(members=ldap_user.dn) @staticmethod def add_user_to_groups(ldap_user: LdapUser, ldap_groups: List): for ldap_group in ldap_groups: ldap_group.members.append(ldap_user.dn) ldap_group.save() @staticmethod def remove_user_from_groups(ldap_user_dn, user_groups=None): if not user_groups: LdapGroup.base_dn = LdapGroup.ROOT_DN user_groups = LdapGroup.objects.filter( members__contains=ldap_user_dn) for group in user_groups: LdapGroup.base_dn = re.compile( 'cn=([a-zA-Z0-9_-]*),(ou=[a-zA-Z_]*.*)').match( group.dn).group(2) group.members.remove(ldap_user_dn) group.save() def get_django_group(self): django_group, _ = Group.objects.get_or_create(name=self.name) return django_group @staticmethod def get_group(group_name: str, realm: Realm = None): LdapGroup.base_dn = f'ou=groups,{realm.ldap_base_dn}' if realm else LdapGroup.ROOT_DN try: return LdapGroup.objects.get(name=group_name) except Exception as e: logger.error(e) return None @staticmethod def get_groups(realm: Realm = None): LdapGroup.base_dn = f'ou=groups,{realm.ldap_base_dn}' if realm else LdapGroup.ROOT_DN try: return LdapGroup.objects.all() except Exception as e: logger.error(e) return None @staticmethod def set_root_dn(realm): LdapGroup.base_dn = f'ou=groups,{realm.ldap_base_dn}' def __str__(self): return self.name def __unicode__(self): return self.name
class LdapPerson(ldapdb.models.Model): """ Class for representing an LDAP person entry. """ class Meta: verbose_name = "LDAP person" verbose_name_plural = "LDAP people" # LDAP meta-data base_dn = settings.REG_PERSON_BASE_DN object_classes = settings.REG_PERSON_OBJECT_CLASSES # Minimal attributes uid = ldapdb_fields.CharField(db_column="uid", max_length=200, primary_key=True) cn = ldapdb_fields.CharField(db_column="cn", max_length=200) sn = ldapdb_fields.CharField(db_column="sn", max_length=200) mail = ldapdb_fields.CharField(db_column="mail", max_length=200) def __str__(self): return self.uid def __unicode__(self): return self.uid def change_password(self, raw_password, using=None): # dig into the ldapdb primitives using = using or router.db_for_write(self.__class__, instance=self) connection = connections[using] cursor = connection._cursor() # call pyldap_orm password modification cursor.connection.extop_s(PasswordModify(self.dn, raw_password)) def check_password(self, raw_password, using=None): using = using or router.db_for_write(self.__class__, instance=self) conn_params = connections[using].get_connection_params() # This is copy-pasta from django-ldapdb/ldapdb/backends/ldap/base.py connection = ldap.ldapobject.ReconnectLDAPObject( uri=conn_params["uri"], retry_max=conn_params["retry_max"], retry_delay=conn_params["retry_delay"], bytes_mode=False, ) options = conn_params["options"] for opt, value in options.items(): if opt == "query_timeout": connection.timeout = int(value) elif opt == "page_size": self.page_size = int(value) else: connection.set_option(opt, value) if conn_params["tls"]: connection.start_tls_s() # After setting up the connection, we try to authenticate try: connection.simple_bind_s(self.dn, raw_password) except ldap.INVALID_CREDENTIALS: return False return True
class RcLdapGroup(ldapdb.models.Model): class Meta: verbose_name = 'LDAP group' verbose_name_plural = 'LDAP groups' managed = False def __init__(self,*args,**kwargs): super(RcLdapGroup,self).__init__(*args,**kwargs) rdn = self.dn.lower().replace(self.base_dn.lower(), '') rdn_list = rdn.split(',') self.org = '' if len(rdn_list) > 1: ou = self.base_dn.lower().split(',')[0] __, org = ou.split('=') self.org = org self.base_dn = self.base_dn.lower() objects = RcLdapGroupManager() rdn_keys = ['name'] base_dn = settings.LDAPCONFS['rcldap']['group_dn'] object_classes = ['top','posixGroup'] # posixGroup attributes # gid = ldap_fields.IntegerField(db_column='gidNumber', unique=True) gid = ldap_fields.IntegerField(db_column='gidNumber',null=True,blank=True) name = ldap_fields.CharField(db_column='cn', max_length=200) members = ldap_fields.ListField(db_column='memberUid',blank=True,null=True) def __str__(self): return self.name def __str__(self): return self.name @property def organization(self): return self.org @property def effective_cn(self): suffixed_name = ldap_utils.get_suffixed_username(self.name,self.organization) return suffixed_name def _set_base_dn(self,org): if org in list(settings.ORGANIZATION_INFO.keys()): ou = 'ou={}'.format(org) self.org = org if ou not in self.base_dn.lower(): self.base_dn = ','.join([ou,self.base_dn]) else: raise ValueError('Invalid organization specified: {}'.format(org)) def save(self,*args,**kwargs): org = kwargs.pop('organization', None) if not org: raise ValueError('No organization specified.') self._set_base_dn(org) force_insert = kwargs.pop('force_insert',None) # If no GID specified, auto-assign if self.gid == None: id_tracker = IdTracker.objects.get(category='posix') gid = id_tracker.get_next_id() self.gid = gid logger = logging.getLogger('accounts') logger.info('Auto-assigned GID to group: {}, {}'.format(gid, self.name)) super(RcLdapGroup,self).save(*args,**kwargs)