def test_process_close(self): def check_contents(o): self.assertEqual(o.audit_author, self.persons.fd) self.assertEqual(o.audit_notes, "test message") self.assertIsInstance(o.audit_time, datetime.datetime) self.assertEqual(o.process, self.processes.app) o = pops.ProcessClose(audit_author=self.persons.fd, audit_notes="test message", process=self.processes.app) self.check_op(o, check_contents)
def test_close_process_dm(self): self.processes.app.applying_for = const.STATUS_DM self.processes.app.save() op = pops.ProcessClose(audit_author=self.persons.dam, audit_notes="audit_closed", process=self.processes.app, logtext="closed", audit_time=self.now) op.execute() self.persons.app.refresh_from_db() self.processes.app.refresh_from_db() self.assertEqual(self.persons.app.status, const.STATUS_DM) self.assertEqual(self.persons.app.status_changed, op.audit_time) self.assertEqual(self.processes.app.closed_by, op.audit_author) self.assertEqual(self.processes.app.closed_time, op.audit_time) self.assertEqual(len(mail.outbox), 0)
def test_close_process_dd_u(self): self.processes.app.applying_for = const.STATUS_DD_U self.processes.app.save() op = pops.ProcessClose(audit_author=self.persons.dam, audit_notes="audit_closed", process=self.processes.app, logtext="closed", audit_time=self.now) op.execute() self.persons.app.refresh_from_db() self.processes.app.refresh_from_db() self.assertEqual(self.persons.app.status, const.STATUS_DD_U) self.assertEqual(self.persons.app.status_changed, self.now) self.assertEqual(self.processes.app.closed_by, op.audit_author) self.assertEqual(self.processes.app.closed_time, self.now) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ["*****@*****.**"])
def ops(self): person = self.process.person if person.fpr != self.fpr: raise OperationError( self.log_entry, "{} in process {} has fingerprint {} but the commit has {}". format(person.lookup_key, self.details, person.fpr, self.fpr)) if self.process.closed: return if self.rt: logtext = "Closed from keyring changelog {}, RT #{}".format( self.log_entry.shasum, self.rt) else: logtext = "Closed from keyring changelog {}, RT unknown".format( self.log_entry.shasum) yield pops.ProcessClose( process=self.process, audit_time=self.log_entry.dt, audit_author=self.author, audit_notes=logtext, )
def ops(self): # Check for existing records in the database person = self._get_person() # If it is all new, keyring has a DD that DAM does not know about: # yell. if person is None: raise OperationError( self.log_entry, "commit has new DD {} {} that we do not know about".format( self.uid, self.fpr)) if person.fpr != self.fpr: # Keyring-maint added a different key: sync with them if self.rt: audit_notes = "Set fingerprint to {}, RT #{}".format( self.fpr, self.rt) else: audit_notes = "Set fingerprint to {}, RT unknown".format( self.fpr) yield bops.ChangeFingerprint(person=person, fpr=self.fpr, audit_author=self.author, audit_notes=audit_notes) #person.save(audit_author=self.author, audit_notes=audit_notes) #log.info("%s: %s: %s", self.logtag, self.person_link(person), audit_notes) # Do not return yet, we still need to check the status role_status_map = { "DD": const.STATUS_DD_U, "DN": const.STATUS_DD_NU, } if person.status == role_status_map[self.role]: # Status already matches #log.info("%s: %s is already %s: skipping duplicate entry", self.logtag, self.person_link(person), const.ALL_STATUS_DESCS[person.status]) return # Look for a process to close applying_for = role_status_map[self.role] found = False for p in pmodels.Process.objects.filter(person=person, applying_for=applying_for, closed_time__isnull=True): if self.rt: logtext = "Added to {} keyring, RT #{}".format( self.role, self.rt) else: logtext = "Added to {} keyring, RT unknown".format(self.role) yield pops.ProcessClose( process=p, audit_time=self.log_entry.dt, audit_author=self.author, audit_notes=logtext, ) #log.info("%s: %s has an open process to become %s, keyring added them as %s", # self.logtag, self.person_link(person), const.ALL_STATUS_DESCS[p.applying_for], self.role) found = True if not found: # f3d1c1ee92bba3ebe05f584b7efea0cfd6e4ebe4 is an example commit # that triggers this raise OperationError( self.log_entry, "commit adds {} as {}, but we have no active process for it". format(person.lookup_key, self.role))
def run_main(self, stage): for entry in dmodels.list_people(): # Skip DDs if entry.is_dd and entry.single("keyFingerPrint") is not None: continue fpr = entry.single("keyFingerPrint") # Skip people without fingerprints if fpr is None: continue email = entry.single("emailForward") # Skip entries without emails (happens when running outside of the Debian network) if email is None: continue # Find the corresponding person in our database person = bmodels.Person.objects.get_from_other_db( "LDAP", uid=entry.uid, fpr=fpr, email=email, format_person=self.hk.link, ) if not person: # New DC_GA audit_notes = "created new guest account entry from LDAP" person = bmodels.Person.objects.create_user( cn=entry.single("cn"), mn=entry.single("mn") or "", sn=entry.single("sn") or "", email=email, email_ldap=email, uid=entry.uid, fpr=fpr, status=const.STATUS_DC_GA, username="******".format(entry.uid), audit_author=self.hk.housekeeper.user, audit_notes=audit_notes, ) log.warn("%s: %s %s", self.IDENTIFIER, self.hk.link(person), audit_notes) else: # Validate fields if person.uid is not None and person.uid != entry.uid: log.warn( "%s: LDAP has uid %s for person %s, but uid is %s in our database", self.IDENTIFIER, entry.uid, self.hk.link(person), person.uid) continue if person.fpr is not None and person.fpr != fpr: log.warn( "%s: LDAP has fingerprint %s for person %s, but fingerprint is %s in our database", self.IDENTIFIER, fpr, self.hk.link(person), person.fpr) continue audit_notes = ["entry found in LDAP"] # Ignore differences in email forward: they are caught by # CheckLDAPConsistency if person.status in (const.STATUS_DC_GA, const.STATUS_DM_GA): # We already know about it: nothing to do pass elif person.status in (const.STATUS_DC, const.STATUS_DM): if person.status == const.STATUS_DM: # DM that becomes DM_GA (acquires uid) new_status = const.STATUS_DM_GA else: # DC that becomes DC_GA (acquires uid) new_status = const.STATUS_DC_GA audit_notes = "entry found in LDAP, adding 'guest account' status" try: process = pmodels.Process.objects.get( person=person, closed_by__isnull=True, applying_for=new_status) except pmodels.Process.DoesNotExist: process = None if process is None: op = bops.ChangeStatus( audit_author=self.hk.housekeeper.user, audit_notes=audit_notes, person=person, status=new_status) op.execute() else: op = pops.ProcessClose( audit_author=self.hk.housekeeper.user, audit_notes=audit_notes, process=process, ) op.execute() log.info("%s: %s %s", self.IDENTIFIER, self.hk.link(person), audit_notes) else: # New uid on a status that is not supposed to have one: # just warn about it log.warn( "%s: LDAP has new uid %s for person %s, which already has status %s in our database", self.IDENTIFIER, entry.uid, self.hk.link(person), const.ALL_STATUS_DESCS[person.status])
def run_main(self, stage): # Prefetch people and index them by uid people_by_uid = dict() for p in bmodels.Person.objects.all(): if p.uid is None: continue people_by_uid[p.uid] = p for entry in dmodels.list_people(): person = people_by_uid.get(entry.uid, None) if person is None: fpr = entry.single("keyFingerPrint") if fpr: log.warn( "%s: %s has fingerprint %s and gid %s in LDAP, but is not in our db", self.IDENTIFIER, entry.uid, fpr, entry.single("gidNumber")) else: args = { "cn": entry.single("cn"), "mn": entry.single("mn") or "", "sn": entry.single("sn") or "", "email": entry.single("emailForward"), "email_ldap": entry.single("emailForward"), "uid": entry.uid, "fpr": "FIXME-REMOVED-" + entry.uid, "username": "******".format(entry.uid), "audit_author": self.hk.housekeeper.user, } if entry.is_dd: args["status"] = const.STATUS_REMOVED_DD args[ "audit_notes"] = "created to mirror a removed DD account from LDAP" if not args["email"]: args["email"] = "{}@debian.org".format(entry.uid) else: args["status"] = const.STATUS_DC_GA args[ "audit_notes"] = "created to mirror a removed guest account from LDAP" if not args["email"]: args["email"] = "{}@example.org".format(entry.uid) person = bmodels.Person.objects.create_user(**args) log.warn("%s: %s: %s", self.IDENTIFIER, self.hk.link(person), args["audit_notes"]) else: dsa_status = entry.single("accountStatus") if dsa_status is not None: dsa_status = dsa_status.split()[0] if dsa_status in ("retiring", "inactive"): if person.status in (const.STATUS_DC_GA, const.STATUS_DM_GA): pass # TODO: handle guest accounts that have been closed elif person.status not in (const.STATUS_REMOVED_DD, const.STATUS_EMERITUS_DD): try: process = pmodels.Process.objects.get( person=person, closed_by__isnull=True, applying_for__in=(const.STATUS_REMOVED_DD, const.STATUS_EMERITUS_DD)) except pmodels.Process.DoesNotExist: process = None if process is not None: audit_notes = "closed from dsa: " + entry.single( "accountStatus") op = pops.ProcessClose( audit_author=self.hk.housekeeper.user, audit_notes=audit_notes, process=process, ) op.execute() log.info("%s: %s %s", self.IDENTIFIER, self.hk.link(person), audit_notes) else: log.warn( "%s: %s has accountStatus '%s' but in our db the state is %s", self.IDENTIFIER, self.hk.link(person), entry.single("accountStatus"), const.ALL_STATUS_DESCS[person.status]) if entry.is_dd and entry.single("keyFingerPrint") is not None: if person.status not in (const.STATUS_DD_U, const.STATUS_DD_NU): log.warn( "%s: %s has supplementaryGid 'Debian' and fingerprint %s in LDAP, but in our db the state is %s", self.IDENTIFIER, self.hk.link(person), entry.single("keyFingerPrint"), const.ALL_STATUS_DESCS[person.status]) email = entry.single("emailForward") if email != person.email: if email is not None: log.info( "%s: %s changing email_ldap from %s to %s (source: LDAP)", self.IDENTIFIER, self.hk.link(person), person.email, email) person.email_ldap = email person.save(audit_author=self.hk.housekeeper.user, audit_notes="updated email_ldap from LDAP")