[dak/master] Add suite ACLs and per-suite NEW.
---
dak/acl.py | 83 +++++++++++++++++++
dak/dak.py | 2 +
dak/dakdb/update83.py | 219 +++++++++++++++++++++++++++++++++++++++++++++++++
dak/import_keyring.py | 32 +-------
dak/update_db.py | 2 +-
daklib/archive.py | 18 +++--
daklib/checks.py | 162 +++++++++++++++++++++---------------
daklib/dbconn.py | 121 ++++++++-------------------
8 files changed, 449 insertions(+), 190 deletions(-)
create mode 100644 dak/acl.py
create mode 100644 dak/dakdb/update83.py
diff --git a/dak/acl.py b/dak/acl.py
new file mode 100644
index 0000000..f38a3a6
--- /dev/null
+++ b/dak/acl.py
@@ -0,0 +1,83 @@
+#! /usr/bin/env python
+#
+# Copyright (C) 2012, Ansgar Burchardt <ansgar@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import apt_pkg
+import sys
+
+from daklib.config import Config
+from daklib.dbconn import DBConn, Fingerprint, Uid, ACL
+
+def usage():
+ print """Usage: dak acl set-fingerprints <acl-name>
+
+Reads list of fingerprints from stdin and sets the ACL <acl-name> to these.
+"""
+
+def get_fingerprint(entry, session):
+ """get fingerprint for given ACL entry
+
+ The entry is a string in one of these formats::
+
+ uid:<uid>
+ name:<name>
+ fpr:<fingerprint>
+
+ @type entry: string
+ @param entry: ACL entry
+
+ @param session: database session
+
+ @rtype: L{daklib.dbconn.Fingerprint} or C{None}
+ @return: fingerprint for the entry
+ """
+ field, value = entry.split(":", 1)
+ q = session.query(Fingerprint)
+
+ if field == 'uid':
+ q = q.join(Fingerprint.uid).filter(Uid.uid == value)
+ elif field == 'name':
+ q = q.join(Fingerprint.uid).filter(Uid.name == value)
+ elif field == 'fpr':
+ q = q.filter(Fingerprint.fingerprint == value)
+
+ return q.all()
+
+def acl_set_fingerprints(acl_name, entries):
+ session = DBConn().session()
+ acl = session.query(ACL).filter_by(name=acl_name).one()
+
+ acl.fingerprints.clear()
+ for entry in entries:
+ entry = entry.strip()
+ fps = get_fingerprint(entry, session)
+ if len(fps) == 0:
+ print "Unknown key for '{0}'".format(entry)
+ else:
+ acl.fingerprints.update(fps)
+
+ session.commit()
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ if len(argv) != 3 or argv[1] != 'set-fingerprints':
+ usage()
+ sys.exit(1)
+
+ acl_set_fingerprints(argv[2], sys.stdin)
diff --git a/dak/dak.py b/dak/dak.py
index 7e78bc2..306137a 100755
--- a/dak/dak.py
+++ b/dak/dak.py
@@ -123,6 +123,8 @@ def init():
"Syncs fingerprint and uid tables with Debian LDAP db"),
("import-users-from-passwd",
"Sync PostgreSQL users with passwd file"),
+ ("acl",
+ "Manage upload ACLs"),
("admin",
"Perform administration on the dak database"),
("update-db",
diff --git a/dak/dakdb/update83.py b/dak/dakdb/update83.py
new file mode 100644
index 0000000..f0707d5
--- /dev/null
+++ b/dak/dakdb/update83.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python
+# coding=utf8
+
+"""
+switch to new ACL implementation and add pre-suite NEW
+
+@contact: Debian FTP Master <ftpmaster@debian.org>
+@copyright: 2012 Ansgar Burchardt <ansgar@debian.org>
+@license: GNU General Public License version 2 or later
+"""
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+################################################################################
+
+import psycopg2
+from daklib.dak_exceptions import DBUpdateError
+from daklib.config import Config
+
+statements = [
+"""ALTER TABLE suite ADD COLUMN new_queue_id INT REFERENCES policy_queue(id)""",
+
+"""CREATE TABLE acl (
+ id SERIAL PRIMARY KEY NOT NULL,
+ name TEXT NOT NULL,
+ is_global BOOLEAN NOT NULL DEFAULT 'f',
+
+ match_fingerprint BOOLEAN NOT NULL DEFAULT 'f',
+ match_keyring_id INTEGER REFERENCES keyrings(id),
+
+ allow_new BOOLEAN NOT NULL DEFAULT 'f',
+ allow_source BOOLEAN NOT NULL DEFAULT 'f',
+ allow_binary BOOLEAN NOT NULL DEFAULT 'f',
+ allow_binary_all BOOLEAN NOT NULL DEFAULT 'f',
+ allow_binary_only BOOLEAN NOT NULL DEFAULT 'f',
+ allow_hijack BOOLEAN NOT NULL DEFAULT 'f',
+ allow_per_source BOOLEAN NOT NULL DEFAULT 'f',
+ deny_per_source BOOLEAN NOT NULL DEFAULT 'f'
+ )""",
+
+"""CREATE TABLE acl_architecture_map (
+ acl_id INTEGER NOT NULL REFERENCES acl(id) ON DELETE CASCADE,
+ architecture_id INTEGER NOT NULL REFERENCES architecture(id) ON DELETE CASCADE,
+ PRIMARY KEY (acl_id, architecture_id)
+ )""",
+
+"""CREATE TABLE acl_fingerprint_map (
+ acl_id INTEGER NOT NULL REFERENCES acl(id) ON DELETE CASCADE,
+ fingerprint_id INTEGER NOT NULL REFERENCES fingerprint(id) ON DELETE CASCADE,
+ PRIMARY KEY (acl_id, fingerprint_id)
+ )""",
+
+"""CREATE TABLE acl_per_source (
+ acl_id INTEGER NOT NULL REFERENCES acl(id) ON DELETE CASCADE,
+ fingerprint_id INTEGER NOT NULL REFERENCES fingerprint(id) ON DELETE CASCADE,
+ source TEXT NOT NULL,
+ reason TEXT,
+ PRIMARY KEY (acl_id, fingerprint_id, source)
+ )""",
+
+"""CREATE TABLE suite_acl_map (
+ suite_id INTEGER NOT NULL REFERENCES suite(id) ON DELETE CASCADE,
+ acl_id INTEGER NOT NULL REFERENCES acl(id),
+ PRIMARY KEY (suite_id, acl_id)
+ )""",
+]
+
+################################################################################
+
+def get_buildd_acl_id(c, keyring_id):
+ c.execute("""
+ SELECT 'buildd-' || STRING_AGG(a.arch_string, '+' ORDER BY a.arch_string)
+ FROM keyring_acl_map kam
+ JOIN architecture a ON kam.architecture_id = a.id
+ WHERE kam.keyring_id = %(keyring_id)s
+ """, {'keyring_id': keyring_id})
+ acl_name, = c.fetchone()
+
+ c.execute('SELECT id FROM acl WHERE name = %(acl_name)s', {'acl_name': acl_name})
+ row = c.fetchone()
+ if row is not None:
+ return row[0]
+
+ c.execute("""
+ INSERT INTO acl
+ ( name, allow_new, allow_source, allow_binary, allow_binary_all, allow_binary_only, allow_hijack)
+ VALUES (%(acl_name)s, 't', 'f', 't', 'f', 't', 't')
+ RETURNING id""", {'acl_name': acl_name})
+ acl_id, = c.fetchone()
+
+ c.execute("""INSERT INTO acl_architecture_map (acl_id, architecture_id)
+ SELECT %(acl_id)s, architecture_id
+ FROM keyring_acl_map
+ WHERE keyring_id = %(keyring_id)s""",
+ {'acl_id': acl_id, 'keyring_id': keyring_id})
+
+ return acl_id
+
+def get_acl_id(c, acl_dd, acl_dm, keyring_id, source_acl_id, binary_acl_id):
+ c.execute('SELECT access_level FROM source_acl WHERE id = %(source_acl_id)s', {'source_acl_id': source_acl_id})
+ row = c.fetchone()
+ if row is not None:
+ source_acl = row[0]
+ else:
+ source_acl = None
+
+ c.execute('SELECT access_level FROM binary_acl WHERE id = %(binary_acl_id)s', {'binary_acl_id': binary_acl_id})
+ row = c.fetchone()
+ if row is not None:
+ binary_acl = row[0]
+ else:
+ binary_acl = None
+
+ if source_acl == 'full' and binary_acl == 'full':
+ return acl_dd
+ elif source_acl == 'dm' and binary_acl == 'full':
+ return acl_dm
+ elif source_acl is None and binary_acl == 'map':
+ return get_buildd_acl_id(c, keyring_id)
+
+ raise Exception('Cannot convert ACL combination automatically: binary_acl={0}, source_acl={1}'.format(binary_acl, source_acl))
+
+def do_update(self):
+ print __doc__
+ try:
+ cnf = Config()
+
+ c = self.db.cursor()
+
+ for stmt in statements:
+ c.execute(stmt)
+
+ c.execute("""
+ INSERT INTO acl
+ (name, allow_new, allow_source, allow_binary, allow_binary_all, allow_binary_only, allow_hijack)
+ VALUES ('dd', 't', 't', 't', 't', 't', 't')
+ RETURNING id""")
+ acl_dd, = c.fetchone()
+
+ c.execute("""
+ INSERT INTO acl
+ (name, allow_new, allow_source, allow_binary, allow_binary_all, allow_binary_only, allow_per_source, allow_hijack)
+ VALUES ('dm', 'f', 't', 't', 't', 'f', 't', 'f')
+ RETURNING id""")
+ acl_dm, = c.fetchone()
+
+ # convert per-fingerprint ACLs
+
+ c.execute('ALTER TABLE fingerprint ADD COLUMN acl_id INTEGER REFERENCES acl(id)')
+ c.execute("""SELECT id, keyring, source_acl_id, binary_acl_id
+ FROM fingerprint
+ WHERE source_acl_id IS NOT NULL OR binary_acl_id IS NOT NULL""")
+ for fingerprint_id, keyring_id, source_acl_id, binary_acl_id in c.fetchall():
+ acl_id = get_acl_id(c, acl_dd, acl_dm, keyring_id, source_acl_id, binary_acl_id)
+ c.execute('UPDATE fingerprint SET acl_id = %(acl_id)s WHERE id = %(fingerprint_id)s',
+ {'acl_id': acl_id, 'fingerprint_id': fingerprint_id})
+ c.execute("""ALTER TABLE fingerprint
+ DROP COLUMN source_acl_id,
+ DROP COLUMN binary_acl_id,
+ DROP COLUMN binary_reject""")
+
+ # convert per-keyring ACLs
+ c.execute('ALTER TABLE keyrings ADD COLUMN acl_id INTEGER REFERENCES acl(id)')
+ c.execute('SELECT id, default_source_acl_id, default_binary_acl_id FROM keyrings')
+ for keyring_id, source_acl_id, binary_acl_id in c.fetchall():
+ acl_id = get_acl_id(c, acl_dd, acl_dm, keyring_id, source_acl_id, binary_acl_id)
+ c.execute('UPDATE keyrings SET acl_id = %(acl_id)s WHERE id = %(keyring_id)s',
+ {'acl_id': acl_id, 'keyring_id': keyring_id})
+ c.execute("""ALTER TABLE keyrings
+ DROP COLUMN default_source_acl_id,
+ DROP COLUMN default_binary_acl_id,
+ DROP COLUMN default_binary_reject""")
+
+ c.execute("DROP TABLE keyring_acl_map")
+ c.execute("DROP TABLE binary_acl_map")
+ c.execute("DROP TABLE binary_acl")
+ c.execute("DROP TABLE source_acl")
+
+ # convert upload blocks
+ c.execute("""
+ INSERT INTO acl
+ ( name, is_global, allow_new, allow_source, allow_binary, allow_binary_all, allow_hijack, allow_binary_only, deny_per_source)
+ VALUES ('blocks', 't', 't', 't', 't', 't', 't', 't', 't')
+ RETURNING id""")
+ acl_block, = c.fetchone()
+ c.execute("SELECT source, fingerprint_id, reason FROM upload_blocks")
+ for source, fingerprint_id, reason in c.fetchall():
+ if fingerprint_id is None:
+ raise Exception(
+ "ERROR: upload blocks based on uid are no longer supported\n"
+ "=========================================================\n"
+ "\n"
+ "dak now only supports upload blocks based on fingerprints. Please remove\n"
+ "any uid-specific block by running\n"
+ " DELETE FROM upload_blocks WHERE fingerprint_id IS NULL\n"
+ "and try again.")
+
+ c.execute('INSERT INTO acl_match_source_map (acl_id, fingerprint_id, source, reason) VALUES (%(acl_id)s, %(fingerprint_id)s, %(source)s, %(reason)s)',
+ {'acl_id': acl_block, 'fingerprint_id': fingerprint_id, 'source': source, 'reason': reason})
+ c.execute("DROP TABLE upload_blocks")
+
+ c.execute("UPDATE config SET value = '83' WHERE name = 'db_revision'")
+ self.db.commit()
+
+ except psycopg2.ProgrammingError as msg:
+ self.db.rollback()
+ raise DBUpdateError('Unable to apply sick update 83, rollback issued. Error message: {0}'.format(msg))
diff --git a/dak/import_keyring.py b/dak/import_keyring.py
index 89c2b75..ff0cb98 100755
--- a/dak/import_keyring.py
+++ b/dak/import_keyring.py
@@ -178,13 +178,8 @@ def main():
changes.append((db_uid_byid.get(u, [None])[0], "Removed key: %s" % (f)))
session.execute("""UPDATE fingerprint
SET keyring = NULL,
- source_acl_id = NULL,
- binary_acl_id = NULL,
- binary_reject = TRUE
WHERE id = :fprid""", {'fprid': fid})
- session.execute("""DELETE FROM binary_acl_map WHERE fingerprint_id = :fprid""", {'fprid': fid})
-
# For the keys in this keyring, add/update any fingerprints that've
# changed.
@@ -208,19 +203,9 @@ def main():
if newuid:
fp.uid_id = newuid
- fp.binary_acl_id = keyring.default_binary_acl_id
- fp.source_acl_id = keyring.default_source_acl_id
- fp.default_binary_reject = keyring.default_binary_reject
session.add(fp)
session.flush()
- for k in keyring.keyring_acl_map:
- ba = BinaryACLMap()
- ba.fingerprint_id = fp.fingerprint_id
- ba.architecture_id = k.architecture_id
- session.add(ba)
- session.flush()
-
else:
if newuid and olduid != newuid and olduid == -1:
changes.append((newuiduid, "Linked key: %s" % f))
@@ -245,29 +230,14 @@ def main():
# Only change the keyring if it won't result in a loss of permissions
if movekey:
- session.execute("""DELETE FROM binary_acl_map WHERE fingerprint_id = :fprid""", {'fprid': oldfid})
-
session.execute("""UPDATE fingerprint
- SET keyring = :keyring,
- source_acl_id = :source_acl_id,
- binary_acl_id = :binary_acl_id,
- binary_reject = :binary_reject
+ SET keyring = :keyring
WHERE id = :fpr""",
{'keyring': keyring.keyring_id,
- 'source_acl_id': keyring.default_source_acl_id,
- 'binary_acl_id': keyring.default_binary_acl_id,
- 'binary_reject': keyring.default_binary_reject,
'fpr': oldfid})
session.flush()
- for k in keyring.keyring_acl_map:
- ba = BinaryACLMap()
- ba.fingerprint_id = oldfid
- ba.architecture_id = k.architecture_id
- session.add(ba)
- session.flush()
-
else:
print "Key %s exists in both %s and %s keyrings. Not demoting." % (f,
oldkeyring.keyring_name,
diff --git a/dak/update_db.py b/dak/update_db.py
index 3a33c97..cf327b0 100755
--- a/dak/update_db.py
+++ b/dak/update_db.py
@@ -46,7 +46,7 @@ from daklib.daklog import Logger
################################################################################
Cnf = None
-required_database_schema = 85
+required_database_schema = 86
################################################################################
diff --git a/daklib/archive.py b/daklib/archive.py
index fdd7cd7..5c98eec 100644
--- a/daklib/archive.py
+++ b/daklib/archive.py
@@ -876,7 +876,6 @@ class ArchiveUpload(object):
for chk in (
checks.TransitionCheck,
- checks.UploadBlockCheck,
checks.ACLCheck,
checks.NoSourceOnlyCheck,
checks.LintianCheck,
@@ -884,6 +883,7 @@ class ArchiveUpload(object):
chk().check(self)
for chk in (
+ checks.ACLCheck,
checks.SourceFormatCheck,
checks.SuiteArchitectureCheck,
checks.VersionCheck,
@@ -1173,16 +1173,22 @@ class ArchiveUpload(object):
binaries = self.changes.binaries
byhand = self.changes.byhand_files
- new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='new').one()
- if len(byhand) > 0:
- new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='byhand').one()
- new_suite = new_queue.suite
-
# we need a suite to guess components
suites = list(self.final_suites)
assert len(suites) == 1, "NEW uploads must be to a single suite"
suite = suites[0]
+ # decide which NEW queue to use
+ if suite.new_queue is None:
+ new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='new').one()
+ else:
+ new_queue = suite.new_queue
+ if len(byhand) > 0:
+ # There is only one global BYHAND queue
+ new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='byhand').one()
+ new_suite = new_queue.suite
+
+
def binary_component_func(binary):
return self._binary_component(suite, binary, only_overrides=False)
diff --git a/daklib/checks.py b/daklib/checks.py
index 770615a..81bd629 100644
--- a/daklib/checks.py
+++ b/daklib/checks.py
@@ -352,21 +352,70 @@ class SingleDistributionCheck(Check):
class ACLCheck(Check):
"""Check the uploader is allowed to upload the packages in .changes"""
- def _check_dm(self, upload):
+
+ def _does_hijack(self, session, upload, suite):
+ for binary_name in upload.changes.binary_names:
+ binaries = session.query(DBBinary).join(DBBinary.source) \
+ .filter(DBBinary.suites.contains(suite)) \
+ .filter(DBBinary.package == binary_name)
+ for binary in binaries:
+ if binary.source.source != upload.changes.changes['Source']:
+ return True, binary, binary.source.source
+ return False, None, None
+
+ def _check_acl(self, session, upload, acl):
+ source_name = upload.changes.source_name
+
+ if acl.match_fingerprint and upload.fingerprint not in acl.fingerprints:
+ return None, None
+ if acl.match_keyring is not None and upload.fingerprint.keyring != acl.match_keyring:
+ return None, None
+
+ if not acl.allow_new:
+ if upload.new:
+ return False, "NEW uploads are not allowed"
+ for f in upload.changes.files.itervalues():
+ if f.section == 'byhand' or f.section.startswith("raw-"):
+ return False, "BYHAND uploads are not allowed"
+ if not acl.allow_source and upload.changes.source is not None:
+ return False, "sourceful uploads are not allowed"
+ binaries = upload.changes.binaries
+ if len(binaries) != 0:
+ if not acl.allow_binary:
+ return False, "binary uploads are not allowed"
+ if upload.changes.source is None and not acl.allow_binary_only:
+ return False, "binary-only uploads are not allowed"
+ if not acl.allow_binary_all:
+ uploaded_arches = set(upload.changes.architectures)
+ uploaded_arches.discard('source')
+ allowed_arches = set(a.arch_string for a in acl.architectures)
+ for a in uploaded_arches:
+ if a not in allowed_arches:
+ return False, "uploads for architecture {0} are not allowed".format(a)
+ if not acl.allow_hijack:
+ for suite in upload.final_suites:
+ does_hijack, hijacked_binary, hijacked_from = self._does_hijack(session, upload, suite)
+ if does_hijack:
+ return False, "hijacks are not allowed (binary={0}, other-source={1})".format(hijacked_binary, hijacked_from)
+
+ acl_per_source = session.query(ACLPerSource).filter_by(acl=acl, fingerprint=upload.fingerprint, source=source_name).first()
+ if acl.allow_per_source:
+ # XXX: Drop DMUA part here and switch to new implementation.
+ dmua_status, dmua_reason = self._check_dmua(upload)
+ if not dmua_status:
+ return False, dmua_reason
+ #if acl_per_source is None:
+ # return False, "not allowed to upload source package '{0}'".format(source_name)
+ if acl.deny_per_source and acl_per_source is not None:
+ return False, acl_per_source.reason or "forbidden to upload source package '{0}'".format(source_name)
+
+ return True, None
+
+ def _check_dmua(self, upload):
# This code is not very nice, but hopefully works until we can replace
# DM-Upload-Allowed, cf. https://lists.debian.org/debian-project/2012/06/msg00029.html
session = upload.session
- if 'source' not in upload.changes.architectures:
- raise Reject('DM uploads must include source')
- for f in upload.changes.files.itervalues():
- if f.section == 'byhand' or f.section[:4] == "raw-":
- raise Reject("Uploading byhand packages is not allowed for DMs.")
-
- # Reject NEW packages
- if upload.new:
- raise Reject('Uploading NEW packages is not allowed for DMs.')
-
# Check DM-Upload-Allowed
suites = upload.final_suites
assert len(suites) == 1
@@ -379,84 +428,61 @@ class ACLCheck(Check):
.join(DBSource.suites).filter(Suite.suite_name.in_(last_suites)) \
.order_by(DBSource.version.desc()).limit(1).first()
if last is None:
- raise Reject('No existing source found in {0}'.format(' or '.join(last_suites)))
+ return False, 'No existing source found in {0}'.format(' or '.join(last_suites))
if not last.dm_upload_allowed:
- raise Reject('DM-Upload-Allowed is not set in {0}={1}'.format(last.source, last.version))
+ return False, 'DM-Upload-Allowed is not set in {0}={1}'.format(last.source, last.version)
# check current Changed-by is in last Maintainer or Uploaders
uploader_names = [ u.name for u in last.uploaders ]
changed_by_field = upload.changes.changes.get('Changed-By', upload.changes.changes['Maintainer'])
if changed_by_field not in uploader_names:
- raise Reject('{0} is not an uploader for {1}={2}'.format(changed_by_field, last.source, last.version))
+ return False, '{0} is not an uploader for {1}={2}'.format(changed_by_field, last.source, last.version)
# check Changed-by is the DM
changed_by = fix_maintainer(changed_by_field)
uid = upload.fingerprint.uid
if uid is None:
- raise Reject('Unknown uid for fingerprint {0}'.format(upload.fingerprint.fingerprint))
+ return False, 'Unknown uid for fingerprint {0}'.format(upload.fingerprint.fingerprint)
if uid.uid != changed_by[3] and uid.name != changed_by[2]:
- raise Reject('DMs are not allowed to sponsor uploads (expected {0} <{1}> as maintainer, but got {2})'.format(uid.name, uid.uid, changed_by_field))
+ return False, 'DMs are not allowed to sponsor uploads (expected {0} <{1}> as maintainer, but got {2})'.format(uid.name, uid.uid, changed_by_field)
- # Try to catch hijacks.
- # This doesn't work correctly. Uploads to experimental can still
- # "hijack" binaries from unstable. Also one can hijack packages
- # via buildds (but people who try this should not be DMs).
- for binary_name in upload.changes.binary_names:
- binaries = session.query(DBBinary).join(DBBinary.source) \
- .filter(DBBinary.suites.contains(suite)) \
- .filter(DBBinary.package == binary_name)
- for binary in binaries:
- if binary.source.source != upload.changes.changes['Source']:
- raise Reject('DMs must not hijack binaries (binary={0}, other-source={1})'.format(binary_name, binary.source.source))
-
- return True
+ return True, None
def check(self, upload):
+ session = upload.session
fingerprint = upload.fingerprint
- source_acl = fingerprint.source_acl
- if source_acl is None:
- if 'source' in upload.changes.architectures:
- raise Reject('Fingerprint {0} must not upload source'.format(fingerprint.fingerprint))
- elif source_acl.access_level == 'dm':
- self._check_dm(upload)
- elif source_acl.access_level != 'full':
- raise Reject('Unknown source_acl access level {0} for fingerprint {1}'.format(source_acl.access_level, fingerprint.fingerprint))
-
- bin_architectures = set(upload.changes.architectures)
- bin_architectures.discard('source')
- binary_acl = fingerprint.binary_acl
- if binary_acl is None:
- if len(bin_architectures) > 0:
- raise Reject('Fingerprint {0} must not upload binary packages'.format(fingerprint.fingerprint))
- elif binary_acl.access_level == 'map':
- query = upload.session.query(BinaryACLMap).filter_by(fingerprint=fingerprint)
- allowed_architectures = [ m.architecture.arch_string for m in query ]
-
- for arch in upload.changes.architectures:
- if arch not in allowed_architectures:
- raise Reject('Fingerprint {0} must not upload binaries for architecture {1}'.format(fingerprint.fingerprint, arch))
- elif binary_acl.access_level != 'full':
- raise Reject('Unknown binary_acl access level {0} for fingerprint {1}'.format(binary_acl.access_level, fingerprint.fingerprint))
+ keyring = fingerprint.keyring
- return True
+ if keyring is None:
+ raise Reject('No keyring for fingerprint {0}'.format(fingerprint.fingerprint))
+ if not keyring.active:
+ raise Reject('Keyring {0} is not active'.format(keyring.name))
-class UploadBlockCheck(Check):
- """check for upload blocks"""
- def check(self, upload):
- session = upload.session
- control = upload.changes.changes
+ acl = fingerprint.acl or keyring.acl
+ if acl is None:
+ raise Reject('No ACL for fingerprint {0}'.format(fingerprint.fingerprint))
+ result, reason = self._check_acl(session, upload, acl)
+ if not result:
+ raise Reject(reason)
- source = re_field_source.match(control['Source']).group('package')
- version = control['Version']
- blocks = session.query(UploadBlock).filter_by(source=source) \
- .filter((UploadBlock.version == version) | (UploadBlock.version == None))
+ for acl in session.query(ACL).filter_by(is_global=True):
+ result, reason = self._check_acl(session, upload, acl)
+ if result == False:
+ raise Reject(reason)
- for block in blocks:
- if block.fingerprint == upload.fingerprint:
- raise Reject('Manual upload block in place for package {0} and fingerprint {1}:\n{2}'.format(source, upload.fingerprint.fingerprint, block.reason))
- if block.uid == upload.fingerprint.uid:
- raise Reject('Manual upload block in place for package {0} and uid {1}:\n{2}'.format(source, block.uid.uid, block.reason))
+ return True
+ def per_suite_check(self, upload, suite):
+ acls = suite.acls
+ if len(acls) != 0:
+ accept = False
+ for acl in acls:
+ result, reason = self._check_acl(upload.session, upload, acl)
+ if result == False:
+ raise Reject(reason)
+ accept = accept or result
+ if not accept:
+ raise Reject('Not accepted by any per-suite acl (suite={0})'.format(suite.suite_name))
return True
class TransitionCheck(Check):
diff --git a/daklib/dbconn.py b/daklib/dbconn.py
index 293f4dc..9617bb7 100644
--- a/daklib/dbconn.py
+++ b/daklib/dbconn.py
@@ -369,6 +369,20 @@ validator = Validator()
################################################################################
+class ACL(ORMObject):
+ def __repr__(self):
+ return "<ACL {0}>".format(self.name)
+
+__all__.append('ACL')
+
+class ACLPerSource(ORMObject):
+ def __repr__(self):
+ return "<ACLPerSource acl={0} fingerprint={1} source={2} reason={3}>".format(self.acl.name, self.fingerprint.fingerprint, self.source, self.reason)
+
+__all__.append('ACLPerSource')
+
+################################################################################
+
class Architecture(ORMObject):
def __init__(self, arch_string = None, description = None):
self.arch_string = arch_string
@@ -642,28 +656,6 @@ __all__.append('get_component_by_package_suite')
################################################################################
-class BinaryACL(object):
- def __init__(self, *args, **kwargs):
- pass
-
- def __repr__(self):
- return '<BinaryACL %s>' % self.binary_acl_id
-
-__all__.append('BinaryACL')
-
-################################################################################
-
-class BinaryACLMap(object):
- def __init__(self, *args, **kwargs):
- pass
-
- def __repr__(self):
- return '<BinaryACLMap %s>' % self.binary_acl_map_id
-
-__all__.append('BinaryACLMap')
-
-################################################################################
-
class BuildQueue(object):
def __init__(self, *args, **kwargs):
pass
@@ -1365,17 +1357,6 @@ __all__.append('get_primary_keyring_path')
################################################################################
-class KeyringACLMap(object):
- def __init__(self, *args, **kwargs):
- pass
-
- def __repr__(self):
- return '<KeyringACLMap %s>' % self.keyring_acl_map_id
-
-__all__.append('KeyringACLMap')
-
-################################################################################
-
class DBChange(object):
def __init__(self, *args, **kwargs):
pass
@@ -2157,17 +2138,6 @@ __all__.append('import_metadata_into_db')
################################################################################
-class SourceACL(object):
- def __init__(self, *args, **kwargs):
- pass
-
- def __repr__(self):
- return '<SourceACL %s>' % self.source_acl_id
-
-__all__.append('SourceACL')
-
-################################################################################
-
class SrcFormat(object):
def __init__(self, *args, **kwargs):
pass
@@ -2417,17 +2387,6 @@ __all__.append('get_uid_from_fingerprint')
################################################################################
-class UploadBlock(object):
- def __init__(self, *args, **kwargs):
- pass
-
- def __repr__(self):
- return '<UploadBlock %s (%s)>' % (self.source, self.upload_block_id)
-
-__all__.append('UploadBlock')
-
-################################################################################
-
class MetadataKey(ORMObject):
def __init__(self, key = None):
self.key = key
@@ -2551,14 +2510,16 @@ class DBConn(object):
def __setuptables(self):
tables = (
+ 'acl',
+ 'acl_architecture_map',
+ 'acl_fingerprint_map',
+ 'acl_per_source',
'architecture',
'archive',
'bin_associations',
'bin_contents',
'binaries',
'binaries_metadata',
- 'binary_acl',
- 'binary_acl_map',
'build_queue',
'changelogs_text',
'changes',
@@ -2571,7 +2532,6 @@ class DBConn(object):
'files_archive_map',
'fingerprint',
'keyrings',
- 'keyring_acl_map',
'maintainer',
'metadata_keys',
'new_comments',
@@ -2585,18 +2545,17 @@ class DBConn(object):
'priority',
'section',
'source',
- 'source_acl',
'source_metadata',
'src_associations',
'src_contents',
'src_format',
'src_uploaders',
'suite',
+ 'suite_acl_map',
'suite_architectures',
'suite_build_queue_copy',
'suite_src_formats',
'uid',
- 'upload_blocks',
'version_check',
)
@@ -2639,6 +2598,20 @@ class DBConn(object):
backref=backref('architectures', order_by=self.tbl_architecture.c.arch_string))),
extension = validator)
+ mapper(ACL, self.tbl_acl,
+ properties = dict(
+ architectures = relation(Architecture, secondary=self.tbl_acl_architecture_map, collection_class=set),
+ fingerprints = relation(Fingerprint, secondary=self.tbl_acl_fingerprint_map, collection_class=set),
+ match_keyring = relation(Keyring, primaryjoin=(self.tbl_acl.c.match_keyring_id == self.tbl_keyrings.c.id)),
+ per_source = relation(ACLPerSource, collection_class=set),
+ ))
+
+ mapper(ACLPerSource, self.tbl_acl_per_source,
+ properties = dict(
+ acl = relation(ACL),
+ fingerprint = relation(Fingerprint),
+ ))
+
mapper(Archive, self.tbl_archive,
properties = dict(archive_id = self.tbl_archive.c.id,
archive_name = self.tbl_archive.c.name))
@@ -2676,14 +2649,6 @@ class DBConn(object):
collection_class=attribute_mapped_collection('key'))),
extension = validator)
- mapper(BinaryACL, self.tbl_binary_acl,
- properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
-
- mapper(BinaryACLMap, self.tbl_binary_acl_map,
- properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id,
- fingerprint = relation(Fingerprint, backref="binary_acl_map"),
- architecture = relation(Architecture)))
-
mapper(Component, self.tbl_component,
properties = dict(component_id = self.tbl_component.c.id,
component_name = self.tbl_component.c.name),
@@ -2717,8 +2682,7 @@ class DBConn(object):
uid = relation(Uid),
keyring_id = self.tbl_fingerprint.c.keyring,
keyring = relation(Keyring),
- source_acl = relation(SourceACL),
- binary_acl = relation(BinaryACL)),
+ acl = relation(ACL)),
extension = validator)
mapper(Keyring, self.tbl_keyrings,
@@ -2738,11 +2702,6 @@ class DBConn(object):
date = self.tbl_changes.c.date,
version = self.tbl_changes.c.version))
- mapper(KeyringACLMap, self.tbl_keyring_acl_map,
- properties = dict(keyring_acl_map_id = self.tbl_keyring_acl_map.c.id,
- keyring = relation(Keyring, backref="keyring_acl_map"),
- architecture = relation(Architecture)))
-
mapper(Maintainer, self.tbl_maintainer,
properties = dict(maintainer_id = self.tbl_maintainer.c.id,
maintains_sources = relation(DBSource, backref='maintainer',
@@ -2821,9 +2780,6 @@ class DBConn(object):
collection_class=attribute_mapped_collection('key'))),
extension = validator)
- mapper(SourceACL, self.tbl_source_acl,
- properties = dict(source_acl_id = self.tbl_source_acl.c.id))
-
mapper(SrcFormat, self.tbl_src_format,
properties = dict(src_format_id = self.tbl_src_format.c.id,
format_name = self.tbl_src_format.c.format_name))
@@ -2831,11 +2787,13 @@ class DBConn(object):
mapper(Suite, self.tbl_suite,
properties = dict(suite_id = self.tbl_suite.c.id,
policy_queue = relation(PolicyQueue, primaryjoin=(self.tbl_suite.c.policy_queue_id == self.tbl_policy_queue.c.id)),
+ new_queue = relation(PolicyQueue, primaryjoin=(self.tbl_suite.c.new_queue_id == self.tbl_policy_queue.c.id)),
copy_queues = relation(BuildQueue,
secondary=self.tbl_suite_build_queue_copy),
srcformats = relation(SrcFormat, secondary=self.tbl_suite_src_formats,
backref=backref('suites', lazy='dynamic')),
- archive = relation(Archive, backref='suites')),
+ archive = relation(Archive, backref='suites'),
+ acls = relation(ACL, secondary=self.tbl_suite_acl_map, collection_class=set)),
extension = validator)
mapper(Uid, self.tbl_uid,
@@ -2843,11 +2801,6 @@ class DBConn(object):
fingerprint = relation(Fingerprint)),
extension = validator)
- mapper(UploadBlock, self.tbl_upload_blocks,
- properties = dict(upload_block_id = self.tbl_upload_blocks.c.id,
- fingerprint = relation(Fingerprint, backref="uploadblocks"),
- uid = relation(Uid, backref="uploadblocks")))
-
mapper(BinContents, self.tbl_bin_contents,
properties = dict(
binary = relation(DBBinary,
--
1.7.2.5
Reply to: