X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=daklib%2Fcommand.py;h=867f7e3692589a5c26d1f1bc059cb40d7a62298f;hb=391f5ec09a119131dc846b796ca791f4cecc69e4;hp=639611c2a7ee5c1cbc82c09b94279beee0319483;hpb=d10bcf2dc97d5c85612264274717e598399f6d97;p=dak.git diff --git a/daklib/command.py b/daklib/command.py index 639611c2..867f7e36 100644 --- a/daklib/command.py +++ b/daklib/command.py @@ -35,15 +35,15 @@ class CommandError(Exception): pass class CommandFile(object): - def __init__(self, path, log=None): + def __init__(self, filename, data, log=None): if log is None: from daklib.daklog import Logger log = Logger() self.cc = [] self.result = [] self.log = log - self.path = path - self.filename = os.path.basename(path) + self.filename = filename + self.data = data def _check_replay(self, signed_file, session): """check for replays @@ -59,23 +59,36 @@ class CommandFile(object): session.add(signature_history) session.commit() + def _quote_section(self, section): + lines = [] + for l in str(section).splitlines(): + lines.append("> {0}".format(l)) + return "\n".join(lines) + def _evaluate_sections(self, sections, session): session.rollback() try: - sections.next() - section = sections.section - - action = section.get('Action', None) - if action is None: - raise CommandError('Encountered section without Action field') - self.result.append('Action: {0}'.format(action)) - - if action == 'dm': - self.action_dm(self.fingerprint, section, session) - elif action == 'break-the-archive': - self.action_break_the_archive(self.fingerprint, section, session) - else: - raise CommandError('Unknown action: {0}'.format(action)) + while True: + sections.next() + section = sections.section + self.result.append(self._quote_section(section)) + + action = section.get('Action', None) + if action is None: + raise CommandError('Encountered section without Action field') + + if action == 'dm': + self.action_dm(self.fingerprint, section, session) + elif action == 'dm-remove': + self.action_dm_remove(self.fingerprint, section, session) + elif action == 'dm-migrate': + self.action_dm_migrate(self.fingerprint, section, session) + elif action == 'break-the-archive': + self.action_break_the_archive(self.fingerprint, section, session) + else: + raise CommandError('Unknown action: {0}'.format(action)) + + self.result.append('') except StopIteration: pass finally: @@ -117,8 +130,7 @@ class CommandFile(object): keyrings = session.query(Keyring).filter_by(active=True).order_by(Keyring.priority) keyring_files = [ k.keyring_name for k in keyrings ] - raw_contents = open(self.path, 'r').read() - signed_file = SignedFile(raw_contents, keyring_files) + signed_file = SignedFile(self.data, keyring_files) if not signed_file.valid: self.log.log(['invalid signature', self.filename]) return False @@ -146,6 +158,8 @@ class CommandFile(object): section = sections.section if 'Uploader' in section: self.uploader = section['Uploader'] + if 'Cc' in section: + self.cc.append(section['Cc']) # TODO: Verify first section has valid Archive field if 'Archive' not in section: raise CommandError('No Archive field in first section.') @@ -157,13 +171,12 @@ class CommandFile(object): self.result.append('') except Exception as e: self.log.log(['ERROR', e]) - self.result.append("There was an error processing this section:\n{0}".format(e)) + self.result.append("There was an error processing this section. No changes were committed.\nDetails:\n{0}".format(e)) result = False self._notify_uploader() session.close() - self.log.log(['done', self.filename]) return result @@ -189,7 +202,10 @@ class CommandFile(object): acl_name = cnf.get('Command::DM::ACL', 'dm') acl = session.query(ACL).filter_by(name=acl_name).one() - fpr = session.query(Fingerprint).filter_by(fingerprint=section['Fingerprint']).one() + fpr_hash = section['Fingerprint'].translate(None, ' ') + fpr = session.query(Fingerprint).filter_by(fingerprint=fpr_hash).first() + if fpr is None: + raise CommandError('Unknown fingerprint {0}'.format(fpr_hash)) if fpr.keyring is None or fpr.keyring.keyring_name not in cnf.value_list('Command::DM::Keyrings'): raise CommandError('Key {0} is not in DM keyring.'.format(fpr.fingerprint)) addresses = gpg_get_key_addresses(fpr.fingerprint) @@ -203,6 +219,10 @@ class CommandFile(object): self.result.append('Uid: {0}'.format(addresses[0])) for source in self._split_packages(section.get('Allow', '')): + # Check for existance of source package to catch typos + if session.query(DBSource).filter_by(source=source).first() is None: + raise CommandError('Tried to grant permissions for unknown source package: {0}'.format(source)) + if session.query(ACLPerSource).filter_by(acl=acl, fingerprint=fpr, source=source).first() is None: aps = ACLPerSource() aps.acl = acl @@ -219,12 +239,85 @@ class CommandFile(object): session.flush() for source in self._split_packages(section.get('Deny', '')): - session.query(ACLPerSource).filter_by(acl=acl, fingerprint=fpr, source=source).delete() + count = session.query(ACLPerSource).filter_by(acl=acl, fingerprint=fpr, source=source).delete() + if count == 0: + raise CommandError('Tried to remove upload permissions for package {0}, ' + 'but no upload permissions were granted before.'.format(source)) + self.log.log(['dm', 'deny', fpr.fingerprint, source]) self.result.append('Denied: {0}'.format(source)) session.commit() + def _action_dm_admin_common(self, fingerprint, section, session): + cnf = Config() + + if 'Command::DM-Admin::AdminFingerprints' not in cnf \ + or 'Command::DM::ACL' not in cnf: + raise CommandError('DM admin command is not configured for this archive.') + + allowed_fingerprints = cnf.value_list('Command::DM-Admin::AdminFingerprints') + if fingerprint.fingerprint not in allowed_fingerprints: + raise CommandError('Key {0} is not allowed to admin DM'.format(fingerprint.fingerprint)) + + def action_dm_remove(self, fingerprint, section, session): + self._action_dm_admin_common(fingerprint, section, session) + + cnf = Config() + acl_name = cnf.get('Command::DM::ACL', 'dm') + acl = session.query(ACL).filter_by(name=acl_name).one() + + fpr_hash = section['Fingerprint'].translate(None, ' ') + fpr = session.query(Fingerprint).filter_by(fingerprint=fpr_hash).first() + if fpr is None: + self.result.append('Unknown fingerprint: {0}\nNo action taken.'.format(fpr_hash)) + return + + self.log.log(['dm-remove', fpr.fingerprint]) + + count = 0 + for entry in session.query(ACLPerSource).filter_by(acl=acl, fingerprint=fpr): + self.log.log(['dm-remove', fpr.fingerprint, 'source={0}'.format(entry.source)]) + count += 1 + session.delete(entry) + + self.result.append('Removed: {0}.\n{1} acl entries removed.'.format(fpr.fingerprint, count)) + + session.commit() + + def action_dm_migrate(self, fingerprint, section, session): + self._action_dm_admin_common(fingerprint, section, session) + cnf = Config() + acl_name = cnf.get('Command::DM::ACL', 'dm') + acl = session.query(ACL).filter_by(name=acl_name).one() + + fpr_hash_from = section['From'].translate(None, ' ') + fpr_from = session.query(Fingerprint).filter_by(fingerprint=fpr_hash_from).first() + if fpr_from is None: + self.result.append('Unknown fingerprint (From): {0}\nNo action taken.'.format(fpr_hash_from)) + return + + fpr_hash_to = section['To'].translate(None, ' ') + fpr_to = session.query(Fingerprint).filter_by(fingerprint=fpr_hash_to).first() + if fpr_to is None: + self.result.append('Unknown fingerprint (To): {0}\nNo action taken.'.format(fpr_hash_to)) + return + if fpr_to.keyring is None or fpr_to.keyring.keyring_name not in cnf.value_list('Command::DM::Keyrings'): + self.result.append('Key (To) {0} is not in DM keyring.\nNo action taken.'.format(fpr_to.fingerprint)) + return + + self.log.log(['dm-migrate', 'from={0}'.format(fpr_hash_from), 'to={0}'.format(fpr_hash_to)]) + + sources = [] + for entry in session.query(ACLPerSource).filter_by(acl=acl, fingerprint=fpr_from): + self.log.log(['dm-migrate', 'from={0}'.format(fpr_hash_from), 'to={0}'.format(fpr_hash_to), 'source={0}'.format(entry.source)]) + entry.fingerprint = fpr_to + sources.append(entry.source) + + self.result.append('Migrated {0} to {1}.\n{2} acl entries changed: {3}'.format(fpr_hash_from, fpr_hash_to, len(sources), ", ".join(sources))) + + session.commit() + def action_break_the_archive(self, fingerprint, section, session): name = 'Dave' uid = fingerprint.uid