Exemple #1
0
def users(request):

    # Connect to LDAP
    try:
        conn = ldap.initialize(Config.ldap_uri)
        conn.bind_s(Config.ldap_bind_dn, Config.ldap_bind_password)
    except Exception as e:
        return request.response_json({'success': False, 
                                      'msg': "Can't bind to LDAP: %s" % e.message['desc']})
    if request.method == "GET":
        # GET requests a user tree, but only one level deep

        # Determine the DN for the current depth
        dn_prepend = ""
        for url_part in request.url_parts[-1:0:-1]:
            dn_prepend += "ou=%s," % escape_dn(url_part)
        search_dn = "%s%s" % (dn_prepend, Config.ldap_base_dn)

        # Perform LDAP search
        try:
            res = conn.search_s(search_dn, ldap.SCOPE_ONELEVEL, "(|(objectclass=sambaSamAccount)(objectClass=organizationalUnit))", attrlist=[ '*', 'entryUUID' ])
        except Exception as e:
            return request.response_json({'success': False, 'msg': 'Search failed: %s' % e.message['desc']}, status='404 Not Found')

        # Determine the depth for the search dn, needed for reference when building the tree
        # This also works with DNs with escaped characters
        mysplit = shlex.shlex(search_dn, posix=True)
        mysplit.escape = '\\'
        mysplit.quotes = ''
        mysplit.whitespace = ','
        mysplit.whitespace_split = True
        search_dn_length = len(list(mysplit))

        # Build an attribute tree
        tree = {}
        for dn, attrs in res:
            mysplit = shlex.shlex(dn, posix=True)
            mysplit.escape = '\\'
            mysplit.quotes = ''
            mysplit.whitespace = ','
            mysplit.whitespace_split = True
            add_to_indexed_tree(tree, list(mysplit)[-search_dn_length-1::-1], dn, attrs)

        # Parse the attribute tree to convert it to something ExtJS can parse
        output_tree = parse_subtree({'children': tree }, "root")

        return request.response_json({'success': True, 
                                      'data': output_tree['children'] })
    else:
        if request.method == 'PUT':
            try:
                existing_attrs = conn.search_s(Config.ldap_base_dn, ldap.SCOPE_SUBTREE, "(entryUUID=%s)" % escape_filter(request.url_parts[0]))
            except:
                return request.response_json({'success': False, 'message': 'Failed to locate object in LDAP directory'})


            # TODO: Handle 'sambaAcctFlags': '[U          ]', (samba Disabled, workstation, user, etc)
            # TODO: Handle password changes

            attrs = {}
            if 'first' in request.put:
                attrs['givenName'] = request.put['first']
            if 'last' in request.put:
                attrs['sn'] = request.put['last']
            if 'first' in request.put and 'last' in request.put:
                attrs['cn'] = "%s %s" % (request.put['first'], request.put['last'])
                attrs['displayName'] = attrs['cn']
                attrs['description'] = attrs['cn']
                attrs['gecos'] = "%s %s,,," % (request.put['first'], request.put['last'])
                request.put['displayname'] = attrs['cn']
            if 'email' in request.put:
                attrs['mail'] = request.put['email']
            if 'uidNumber' in request.put:
                attrs['uidNumber'] = request.put['uidNumber']
            if 'gidNumber' in request.put:
                attrs['gidNumber'] = request.put['gidNumber']
            if 'sambaSID' in request.put:
                attrs['sambaSID'] = request.put['sambaSID']
            if 'homeDirectory' in request.put:
                attrs['homeDirectory'] = request.put['homeDirectory']
            if 'loginShell' in request.put:
                attrs['loginShell'] = request.put['loginShell']
            if 'accountStatus' in request.put:
                attrs['accountStatus'] = request.put['accountStatus']
            ldif = modlist.modifyModlist(existing_attrs[0][1], attrs, ignore_oldexistent=True)

            if len(ldif):
                conn.modify_s(existing_attrs[0][0], ldif)

            if 'username' in request.put and existing_attrs[0][1]['uid'][0] != request.put['username'].lower():
                conn.rename_s(existing_attrs[0][0], "uid=%s" % escape_dn(request.put['username'].lower()))


            return request.response_json({'success': True,
                                          'message': "User info has been saved.",
                                          'data': request.put })
        else:
            return request.response_json({'success': False,
                                          'msg': 'Feature not implemented', 'more': request.url_parts})
def create_user(request):
    # Connect to LDAP
    try:
        conn = ldap.initialize(Config.ldap_uri)
        conn.bind_s(Config.ldap_bind_dn, Config.ldap_bind_password)
    except Exception as e:
        return request.response_json({'success': False, 
                                      'msg': "Can't bind to LDAP: %s" % e.message['desc']})


    # Validate inputs
    try:
        errors = []
        if request.post['answer'][0] != '42':
            errors.append( {'id': 'answer', 'msg': 'Sorry, wrong answer' } )
        if not re.match(r'^[a-z0-9\-\{\}\.]+$', request.post['username'][0]):
            errors.append( {'id': 'username', 'msg': 'Username should match r\'^a-z0-9\-\{\}\.]+$\'' } )
        if not re.match(r'^[a-z0-9\-\_\.]+\@[a-z0-9\-]+\.[a-z]{2,30}$', request.post['email'][0]):
            errors.append( {'id': 'email', 'msg': 'E-mail address incorrect.' } )
        if request.post['first'][0] == "":
            errors.append( {'id': 'first', 'msg': 'First name cannot be empty' } )
        if request.post['last'][0] == "":
            errors.append( {'id': 'last', 'msg': 'Last name cannot be empty' } )
        if len(request.post['password'][0]) < 5:
            errors.append( {'id': 'password', 'msg': 'Password too short' } )

        if len(errors) > 0:
            return request.response_json({'success': False, 'errors': errors })
    except:
            return request.response_json({'success': False, 'msg': 'Missing POST variables or internal server error'}, status="500 Internal Server Error")

    attrs = {
        'objectClass': [ 'top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'sambaSamAccount', 'qmailUser' ],
        'cn': "%s %s" % (request.post['first'][0], request.post['last'][0]),
        'displayName': "%s %s" % (request.post['first'][0], request.post['last'][0]),
        'description': "%s %s" % (request.post['first'][0], request.post['last'][0]),
        'gecos': "%s %s,,," % (request.post['first'][0], request.post['last'][0]),
        'givenName': request.post['first'][0],
        'sn': request.post['last'][0],
        'accountStatus': '1', # Unverified account
        'mail': request.post['email'][0],
        'uid': request.post['username'][0].lower(),
        'userPassword': "******" % create_ssha_password(request.post['password'][0]),
        'sambaNTPassword': create_nt_password(request.post['password'][0]),
        'uidNumber': str(assign_uid(conn)),
        'gidNumber': '1000',
        'sambaSID': assign_sambasid(conn),
        'homeDirectory': "/home/%s" % request.post['username'][0].lower(),
        'loginShell': '/bin/bash',
        'sambaAcctFlags': '[U          ]',
    }

    #
    # Add the object to the directory
    #
    # FIXME: ou should be configurable
    ldif = modlist.addModlist(attrs)
    try:
        conn.add_s("uid=%s,ou=accounts,%s" % (escape_dn(request.post['username'][0].lower()), Config.ldap_base_dn), ldif)
    except Exception as e:
        return request.response_json({'success': False,
                                      'errors': [{'id': 'username', 'msg': e.message['desc'] }]})

    #
    # Grab the entryUUID for the newly created object, this is the unique identifier for account email verification
    #
    res = conn.search_s("uid=%s,ou=accounts,%s" % (escape_dn(request.post['username'][0].lower()), Config.ldap_base_dn), ldap.SCOPE_BASE, attrlist=[ 'entryUUID' ])

    if len(res) != 1:
        return request.response_json({'success': False, 
                                  'msg': 'Could not find newly created user in the directory' })

    attrs['entryUUID'] = res[0][1]['entryUUID'][0]


    # FIXME Text should be configurable
    msg = MIMEText("Hello %(givenName)s,\n\nYou've created an account for %(site_name)s, but you still need to activate it. You can activate it by going to http://services.ifcat.org/registration/verify.html?uuid=%(entryUUID)s&username=%(uid)s\n\nOr enter the form fields manually:\nUUID: %(entryUUID)s\nUsername: %(uid)s\n\nRegards,\n\nThe %(site_name)s team" % dict(attrs.items() + {'site_name': Config.site_name }.items()))
    msg['Subject'] = 'Activate your account'
    msg['From'] = "\"%(site_name)s\" <%(site_mail)s>" % dict(attrs.items() + {'site_name': Config.site_name, 'site_mail': Config.site_mail }.items())
    msg['To'] = attrs['mail']

    s = smtplib.SMTP('localhost')
    s.sendmail(msg['From'], msg['To'], msg.as_string())
    s.quit()

    return request.response_json({'success': True, 
                                  'msg': 'A verification email has been sent to you' })