models.py 4.14 KB
Newer Older
1 2
import logging

3 4 5
import ldap
import ldapdb.models
import pyasn1.codec.ber.encoder
6 7 8
import pyasn1.type.namedtype
import pyasn1.type.univ
from django.conf import settings
9
from django.db import connections, router
10 11
from ldapdb.models.fields import CharField, ListField

12
log = logging.getLogger(__name__)
13

14

15 16 17 18 19 20 21 22 23 24
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
25 26
    base_dn = settings.REG_GROUP_BASE_DN
    object_classes = settings.REG_GROUP_OBJECT_CLASSES
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

    # LDAP group attributes
    cn = CharField(db_column='cn', max_length=200, primary_key=True)
    description = CharField(db_column='description', max_length=200)
    members = ListField(db_column='member')

    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
50 51
    base_dn = settings.REG_PERSON_BASE_DN
    object_classes = settings.REG_PERSON_OBJECT_CLASSES
52 53 54 55 56 57

    # Minimal attributes
    uid = CharField(db_column='uid', max_length=200, primary_key=True)
    cn = CharField(db_column='cn', max_length=200)
    description = CharField(db_column='description', max_length=200)
    sn = CharField(db_column='sn', max_length=200)
58
    mail = CharField(db_column='mail', max_length=200)
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

    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))


# The following code taken from https://github.com/asyd/pyldap_orm/blob/master/pyldap_orm/controls.py
# Copyright 2016 Bruno Bonfils
# SPDX-License-Identifier: Apache-2.0 (no NOTICE file)


class PasswordModify(ldap.extop.ExtendedRequest):
    """
    Implements RFC 3062, LDAP Password Modify Extended Operation
    Reference: https://www.ietf.org/rfc/rfc3062.txt
    """

    def __init__(self, identity, new, current=None):
        self.requestName = '1.3.6.1.4.1.4203.1.11.1'
        self.identity = identity
        self.new = new
        self.current = current

    def encodedRequestValue(self):
        request = self.PasswdModifyRequestValue()
        request.setComponentByName('userIdentity', self.identity)
        if self.current is not None:
            request.setComponentByName('oldPasswd', self.current)
        request.setComponentByName('newPasswd', self.new)
        return pyasn1.codec.ber.encoder.encode(request)

    class PasswdModifyRequestValue(pyasn1.type.univ.Sequence):
        """
        PyASN1 representation of:
            PasswdModifyRequestValue ::= SEQUENCE {
            userIdentity    [0]  OCTET STRING OPTIONAL
            oldPasswd       [1]  OCTET STRING OPTIONAL
            newPasswd       [2]  OCTET STRING OPTIONAL }
        """
        componentType = pyasn1.type.namedtype.NamedTypes(
            pyasn1.type.namedtype.OptionalNamedType(
                'userIdentity',
                pyasn1.type.univ.OctetString().subtype(
                    implicitTag=pyasn1.type.tag.Tag(pyasn1.type.tag.tagClassContext, pyasn1.type.tag.tagFormatSimple, 0)
                )),
            pyasn1.type.namedtype.OptionalNamedType(
                'oldPasswd',
                pyasn1.type.univ.OctetString().subtype(
                    implicitTag=pyasn1.type.tag.Tag(pyasn1.type.tag.tagClassContext, pyasn1.type.tag.tagFormatSimple, 1)
                )),
            pyasn1.type.namedtype.OptionalNamedType(
                'newPasswd',
                pyasn1.type.univ.OctetString().subtype(
                    implicitTag=pyasn1.type.tag.Tag(pyasn1.type.tag.tagClassContext, pyasn1.type.tag.tagFormatSimple, 2)
                )),
125
        )