author Dan Minor <>
Fri, 18 Dec 2015 15:12:07 -0500
changeset 362083 2248273cff1c9789e941b14cb0a303dbfca0ff98
parent 361463 9012df1a03fc2b30214bc412907781c3d9f90e3b
permissions -rwxr-xr-x
mozreview: people besides review requesters should be able autoland (bug 1205018) r=mdoglio This removes the check for whether or not the review is mutable by the current user, which allows people other than the review requester to autoland provided they have sufficient privileges.

#!/usr/bin/env python2
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at

# This script updates the hgAccessDate LDAP attribute from pushlog
# data if the pushlog date is newer than LDAP.

import datetime
import os
import sqlite3
import sys

import ldap

user_last_push = {}

# We use 2 LDAP URLs because the master doesn't allow reading:
# only setting of hgAccessDate. See bug 1200847 comment 35.
ldap_ro_url, ldap_rw_url, username, password = sys.argv[1:]

# We receive list of repo paths on stdin.
for repo in sys.stdin:
    repo = repo.rstrip()
    if not repo.startswith('/repo/hg/mozilla'):
        repo = '/repo/hg/mozilla/%s' % repo

    pushlog_path = os.path.join(repo, '.hg', 'pushlog2.db')
    if not os.path.exists(pushlog_path):

    conn = sqlite3.connect(pushlog_path)
    res = conn.execute('SELECT user, date FROM pushlog')
    for user, date in res:
        user_last_push[user] = max(user_last_push.get(user, 0), date)

ldap_ro = ldap.initialize(ldap_ro_url)
ldap_ro.simple_bind_s(username, password)
ldap_rw = ldap.initialize(ldap_rw_url)
ldap_rw.simple_bind_s(username, password)

for user, date in sorted(user_last_push.items()):
    res = ldap_ro.search_s('dc=mozilla', ldap.SCOPE_SUBTREE,
                           '(mail=%s)' % user,
                           ['objectClass', 'hgAccessDate'])

    if not res:
        print('%s not in LDAP' % user)

    dn, entry = res[0]
    if 'hgAccount' not in entry['objectClass']:
        print('%s missing hgAccount objectClass' % user)

    last_push = datetime.datetime.utcfromtimestamp(date)
    last_access = None

    if 'hgAccessDate' in entry:
            last_access = datetime.datetime.strptime(entry['hgAccessDate'][0],
        except ValueError:
            last_access = datetime.datetime.strptime(entry['hgAccessDate'][0],

    max_range = datetime.timedelta(days=1)
    if last_access and (last_access > last_push - max_range):
        print('%s is up to date; last access %s' % (user,

    if not last_access:
        print('%s missing hgAccessDate' % user)
    elif last_access < last_push:
        print('%s hgAccessDate is out of date; %s < %s' % (user,
              last_access.isoformat(), last_push.isoformat()))
        assert False

    value = last_push.strftime('%Y%m%d%H%M%S.%fZ')
    print('Updating %s hgAccessDate to %s' % (user, value))
    ldap_rw.modify_s(dn, [(ldap.MOD_REPLACE, 'hgAccessDate', value)])

# Now set hgAccessDate on users that don't have it.
res = ldap_ro.search_s('dc=mozilla', ldap.SCOPE_SUBTREE,
                       ['hgAccessDate', 'hgAccountEnabled'])
for dn, entry in sorted(res):
    if dn.startswith('uid='):
        print('ignoring system account %s' % dn)

    assert dn.startswith('mail=')
    mail = dn[5:]
    mail = mail.split(',')[0]

    # Entry has hgAccessDate. Nothing to do.
    if 'hgAccessDate' in entry:

    # There is a race condition between setting the value above
    # and querying from a replica here. Ignore all entries that may have
    # been modified above.
    if mail in user_last_push:

    # Set the access date to a dummy value long in the past. This
    # ensures the account gets deactivated within 24 hours.
    print('Setting hgAccessDate on %s' % dn)
    ldap_rw.modify_s(dn, [(ldap.MOD_REPLACE, 'hgAccessDate',