| #!/usr/bin/python |
| """ A script to export an identities.yaml file from Patchworks that |
| attempts to cross-reference with user information from LDAP. |
| |
| This script shold be run with the following exports: |
| export PYTHONPATH=$PYTHONPATH:../project:/srv/linaro-git-tools |
| export DJANGO_SETTINGS_MODULE=local_settings |
| """ |
| |
| import sys |
| import os |
| import time |
| from jinja2 import Template |
| import linaro_ldap |
| from patchwork.models import User |
| from patchwork.models import Person |
| import django |
| |
| django.setup() |
| |
| # hack to make python 2.7 use unicode by default |
| # since some of our usernames have non-ascii chars |
| reload(sys) |
| sys.setdefaultencoding('utf8') |
| |
| SKIP_PROFILES = [ |
| 'bugzilla-daemon' |
| ] |
| |
| OUTFILE = "/tmp/identities.yaml" |
| |
| if os.path.isfile(OUTFILE): |
| os.unlink(OUTFILE) |
| |
| MEMBER_DOMAINS_TABLE = {} |
| |
| # TODO: add more domains |
| KNOWN_DOMAINS_TABLE = { |
| "debian.org": "Debian", |
| "gcc.gnu.org": "GNU", |
| "gnu.org": "GNU", |
| "lge.com": "LG", |
| "ibm.com": "IBM", |
| "il.ibm.com": "IBM", |
| "in.ibm.com": "IBM", |
| "linux.vnet.ibm.com": "IBM", |
| "suse.com": "SuSE", |
| "linuxfoundation.org": "Linux Foundation", |
| "arm.com": "ARM", |
| "freescale.com": "Freescale", |
| "hp.com": "HP", |
| "hpe.com": "HP", |
| "caviumnetworks.com": "Cavium Networks", |
| "ubuntu.com": "Ubuntu", |
| "canonical.com": "Canonical", |
| "stericsson.com": "ST", |
| "amd.com": "AMD", |
| "broadcom.com": "Broadcom", |
| "qti.qualcomm.com": "Qualcomm", |
| "quicinc.com": "Qualcomm", |
| "collabora.com": "Collabora", |
| "collabora.co.uk": "Collabora" |
| } |
| |
| |
| MERGE_TABLE = {} |
| |
| |
| class NoLdapUserException(Exception): |
| pass |
| |
| |
| def build_member_table(): |
| """ Builds a lookup table using email domains as the key to match |
| email addresses with member organizations. """ |
| member_ous = linaro_ldap.do_complex_query( |
| search_filter='(organizationalStatus=*)', |
| attrlist=['*']) |
| |
| for mou in member_ous: |
| member_name = mou[1]["description"][0] |
| if "mail" in mou[1]: |
| for domain in mou[1]["mail"]: |
| MEMBER_DOMAINS_TABLE[domain] = member_name |
| |
| |
| def get_name(target_email, target_attr='displayName'): |
| """ Attempts to get the user's real name from LDAP based on email address. |
| Raises an NoLdapUserException if the user is not found, otherwise |
| returns the "target_attr" setting from LDAP (defaults to |
| "displayName").""" |
| try: |
| result = linaro_ldap.do_query( |
| search_attr='mail', |
| search_pat=target_email, |
| attrlist=[target_attr]) |
| if result and target_attr in result[0][1]: |
| return result[0][1][target_attr][0] |
| except linaro_ldap.ldap.FILTER_ERROR: |
| # user entered in a bogus email and used illegel chars that |
| # caused an LDAP error |
| pass |
| |
| raise NoLdapUserException( |
| "no user found in LDAP for %s" % target_email) |
| |
| |
| def get_org(target_email): |
| """ Attempts to determine the organization a user belongs to based |
| on their email addresses. Look first for a linaro.org address, |
| then searches through member domains. If nothing is found, |
| returns 'Unknown' as default. """ |
| # if they have a l.o address, claim them |
| if target_email.endswith('@linaro.org'): |
| return 'Linaro' |
| |
| domain = target_email.split('@')[-1] |
| # if still here, see if we can match a domain to a member |
| if domain in MEMBER_DOMAINS_TABLE: |
| return MEMBER_DOMAINS_TABLE[domain] |
| |
| # last ditch effort, try to see if it's a domain we recognize |
| if domain in KNOWN_DOMAINS_TABLE: |
| return KNOWN_DOMAINS_TABLE[domain] |
| |
| return 'Unknown' |
| |
| |
| def merge_entry(uid, target_emails, target_org, target_end_date=None): |
| """ Add a profile entry for the user or merge with existing entry |
| to prevent duplicates """ |
| if uid in MERGE_TABLE: |
| for e in target_emails: |
| if e not in MERGE_TABLE[uid]['emails']: |
| MERGE_TABLE[uid]['emails'].append(e) |
| # if the new org is Linaro or previous org unknown, override it |
| if target_org == 'Linaro' or MERGE_TABLE[uid]['org'] == 'Unknown': |
| MERGE_TABLE[uid]['org'] = target_org |
| if target_end_date is not None: |
| MERGE_TABLE[uid]['end_date'] = target_end_date |
| else: |
| MERGE_TABLE[uid] = {} |
| MERGE_TABLE[uid]['emails'] = target_emails |
| MERGE_TABLE[uid]['org'] = target_org |
| MERGE_TABLE[uid]['end_date'] = target_end_date |
| |
| |
| TMPL = Template(u'''\ |
| - profile: |
| name: {{ username }} |
| enrollments: |
| - organization: {{ org }} |
| {%- if end_date %} |
| end_date: {{ end_date }} |
| {%- endif %} |
| email: |
| {%- for email in emails %} |
| - {{ email }} |
| {%- endfor %} |
| |
| ''') |
| |
| |
| def write_entry(uid, target_emails, target_org, target_end_date=None): |
| """ Write a profile entry for the specified user to output """ |
| try: |
| |
| entry_template = TMPL.render( |
| username=uid, |
| org=target_org, |
| end_date=target_end_date, |
| emails=target_emails |
| ) |
| with open(OUTFILE, 'a') as outfile: |
| outfile.write(entry_template) |
| except TypeError: |
| pass |
| |
| |
| build_member_table() |
| |
| for user in User.objects.filter(is_active=True): |
| # skip unwanted profiles |
| if user.username in SKIP_PROFILES: |
| continue |
| |
| persons = Person.objects.filter(user=user) |
| # concat all emails from Person objs, but skip '(address hidden)' |
| emails = [x.email for x in persons if '@' in x.email] |
| |
| # some cases we have a linaro Person linked to non-Linaro User.. |
| # make sure their email is included before we start looking |
| # for organization membership |
| if '@' in user.username and user.username not in emails: |
| emails.append(user.username) |
| |
| # don't bother if user just has an account but no email addresses |
| if not emails: |
| continue |
| |
| end_date = None |
| org = None |
| |
| stop_asking_ldap = False |
| for email in emails: |
| # if we've already found them in ldap, no need to keep searching |
| if stop_asking_ldap: |
| continue |
| |
| # see if this email is in LDAP. If yes, it's an |
| # an active account (either @linaro.org, member, |
| # or community account) |
| try: |
| name = get_name(email, "displayName") |
| stop_asking_ldap = True |
| org = get_org(email) |
| # if it's still unknown, let's call it "Linaro Community" |
| if org == "Unknown": |
| org = "Linaro Community" |
| except NoLdapUserException: |
| # not in ldap. No matter what happens next, |
| # we have to get username from patchworks. |
| name = user.username |
| |
| # If it's a l.o address, then assume user is no longer |
| # an employee since no LDAP entry. Exit loop. |
| if email.endswith('@linaro.org'): |
| org = "Linaro" |
| end_date = time.strftime('%Y-%m-%d', time.localtime()) |
| stop_asking_ldap = True |
| else: |
| # only bother doing a look up if the |
| # org hasn't been set yet. This will |
| # continue through the loop and let us |
| # pick up @l.o addresses if they occur |
| # later. |
| if org is None or org == 'Unknown': |
| org = get_org(email) |
| |
| merge_entry(name, emails, org, end_date) |
| |
| for uid in MERGE_TABLE.keys(): |
| write_entry(uid, |
| MERGE_TABLE[uid]['emails'], |
| MERGE_TABLE[uid]['org'], |
| MERGE_TABLE[uid]['end_date']) |