I've migrated this question from StackOverflow.

I'm administering a Gitlab-Omnibus (Community Edition) instance in our company as an additional duty.

Gitlab uses company's Active Directory server for authentication. Our network managers create AD accounts for new employees and remove them after they leave. Gitlab normally creates its own user accounts on first login and blocks them on sign-in attempts after the AD account is removed.

Then it should be very easy to clean those "dead souls".

The problem is: users are not blocked, the status of the Gitlab account remains "Active" after an employee leaves, because nobody tries to sign in to the Gitlab after leaving. The server itself is inaccessible from the Internet, ex-employees cannot enter the office since their pass cards are destroyed, their PCs are disassembled or formatted with a fresh OS install.

So, I have to manually try to impersonate each account and check if Gitlab blocks it (thanks Gitlab developers for the Impersonation feature, at least something I've got).

I'd like to automate this process, because there are over 100 user accounts in Gitlab, and I don't receive notifications about those employees who has left the company (I'm neither HR manager, nor sysadmin; I also generally don't need them, because there are many employees that do not require Gitlab accounts).

My goal is to develop a script or a Ruby snippet that I could paste into the gitlab-rails console.

This snippet should scan all users, registered in Gitlab, check their existence in LDAP and block all missing in LDAP.

Then I would review all blocked users, transfer their personal projects to their supervisors and remove them.

The problem is that I'm completely not familiar neither with Ruby, nor with Gitlab internals.

Any help?

I do:

irb(main):039:0> User.all
=> #<ActiveRecord::Relation [#<User id:98 @n.name>, #<User id:86 @n.name2>,  ...]>


irb(main):040:0> for u in User.all do
irb(main):041:1* puts u
irb(main):042:1> done
irb(main):044:1* ^C

No output.

What am I doing wrong?


Correct loop syntax should be like this:

User.all.each { |u| puts u.name }

Now it is time to wait for someone leaving and find what to check to set u.state = "ldap_blocked"


Python-gitlab solves the problem in combination with https://ldap3.readthedocs.io/

Here is the code:

import time
from ldap3 import Server, Connection
import gitlab

GITLAB_SERVER = 'https://gitlab.example.com'
LDAP_SERVER = 'ldap.example.com'
no_block = {'root', 'ghost', 'other_custom_user'}

def main():
    gl = gitlab.Gitlab(GITLAB_SERVER, private_token='')

    server = Server(LDAP_SERVER)
    conn = Connection(server,
                      user='CN=Bind User,CN=Users,DC=example,DC=com',

    users = gl.users.list(all=True)
    gitlab_users = {u.attributes['username']: u for u in users}
    ldap_filter = '(&(objectclass=person)(|' + ''.join(['(samaccountname='+u+')' for u in gitlab_users.keys()]) + '))'
    print(conn.search('DC=example,DC=com', ldap_filter, attributes=['cn', 'samaccountname']))

    ldap_users = {str(u.sAMAccountName) for u in conn.entries}
    to_block = set(gitlab_users.keys()).difference(ldap_users).difference(no_block)

    print("Following users will be blocked: {}".format(to_block))
    print("Waiting 10 seconds, then block")


    for u in to_block:
        user = gitlab_users[u]

if __name__ == '__main__':

As mentioned here the gitlab:cleanup:block_removed_ldap_users rake task should do the job. I've personally tried it and all absent from AD users got blocked in GitLab.

  • Our LDAP structure changes sometimes, and if users don't login to Gitlab, their DNs stored in Gitlab are not updated. Then this task considers then as 'Not in LDAP', which is incorrect.
    – wl2776
    Nov 16 '20 at 10:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.