Commit 2915abb9 authored by Lukas Fleischer's avatar Lukas Fleischer
Browse files

git-interface: Add database abstraction layer



Add a new class that connects to the database specified in the
configuration file and provides an interface to execute SQL queries.
Prepared statements with qmark ("?") placeholders are supported.

Replace all direct database accesses with calls to the new abstraction
layer.
Signed-off-by: Lukas Fleischer's avatarLukas Fleischer <lfleischer@archlinux.org>
parent 2cd69bf6
import configparser
import mysql.connector
import os
class Connection:
_conn = None
def __init__(self):
config = configparser.RawConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + "/../conf/config")
aur_db_host = config.get('database', 'host')
aur_db_name = config.get('database', 'name')
aur_db_user = config.get('database', 'user')
aur_db_pass = config.get('database', 'password')
aur_db_socket = config.get('database', 'socket')
self._conn = mysql.connector.connect(host=aur_db_host,
user=aur_db_user,
passwd=aur_db_pass,
db=aur_db_name,
unix_socket=aur_db_socket,
buffered=True)
def execute(self, query, params=()):
query = query.replace('%', '%%').replace('?', '%s')
cur = self._conn.cursor()
cur.execute(query, params)
return cur
def commit(self):
self._conn.commit()
def close(self):
self._conn.close()
#!/usr/bin/python3
import configparser
import mysql.connector
import shlex
import os
import re
import sys
import db
def format_command(env_vars, command, ssh_opts, ssh_key):
environment = ''
......@@ -26,12 +27,6 @@ def format_command(env_vars, command, ssh_opts, ssh_key):
config = configparser.RawConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + "/../conf/config")
aur_db_host = config.get('database', 'host')
aur_db_name = config.get('database', 'name')
aur_db_user = config.get('database', 'user')
aur_db_pass = config.get('database', 'password')
aur_db_socket = config.get('database', 'socket')
valid_keytypes = config.get('auth', 'valid-keytypes').split()
username_regex = config.get('auth', 'username-regex')
git_serve_cmd = config.get('auth', 'git-serve-cmd')
......@@ -42,15 +37,12 @@ keytext = sys.argv[2]
if keytype not in valid_keytypes:
exit(1)
db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
passwd=aur_db_pass, db=aur_db_name,
unix_socket=aur_db_socket, buffered=True)
conn = db.Connection()
cur = db.cursor()
cur.execute("SELECT Users.Username, Users.AccountTypeID FROM Users " +
"INNER JOIN SSHPubKeys ON SSHPubKeys.UserID = Users.ID "
"WHERE SSHPubKeys.PubKey = %s AND Users.Suspended = 0",
(keytype + " " + keytext,))
cur = conn.execute("SELECT Users.Username, Users.AccountTypeID FROM Users " +
"INNER JOIN SSHPubKeys ON SSHPubKeys.UserID = Users.ID "
"WHERE SSHPubKeys.PubKey = ? AND Users.Suspended = 0",
(keytype + " " + keytext,))
if cur.rowcount != 1:
exit(1)
......
#!/usr/bin/python3
import configparser
import mysql.connector
import os
import re
import shlex
import sys
import db
config = configparser.RawConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + "/../conf/config")
aur_db_host = config.get('database', 'host')
aur_db_name = config.get('database', 'name')
aur_db_user = config.get('database', 'user')
aur_db_pass = config.get('database', 'password')
aur_db_socket = config.get('database', 'socket')
repo_path = config.get('serve', 'repo-path')
repo_regex = config.get('serve', 'repo-regex')
git_shell_cmd = config.get('serve', 'git-shell-cmd')
......@@ -27,12 +22,8 @@ maintenance_exc = config.get('options', 'maintenance-exceptions').split()
def pkgbase_from_name(pkgbase):
db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
passwd=aur_db_pass, db=aur_db_name,
unix_socket=aur_db_socket)
cur = db.cursor()
cur.execute("SELECT ID FROM PackageBases WHERE Name = %s", [pkgbase])
db.close()
conn = db.Connection()
cur = conn.execute("SELECT ID FROM PackageBases WHERE Name = ?", [pkgbase])
row = cur.fetchone()
return row[0] if row else None
......@@ -43,21 +34,18 @@ def pkgbase_exists(pkgbase):
def list_repos(user):
db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
passwd=aur_db_pass, db=aur_db_name,
unix_socket=aur_db_socket)
cur = db.cursor()
conn = db.Connection()
cur.execute("SELECT ID FROM Users WHERE Username = %s ", [user])
cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user])
userid = cur.fetchone()[0]
if userid == 0:
die('{:s}: unknown user: {:s}'.format(action, user))
cur.execute("SELECT Name, PackagerUID FROM PackageBases " +
"WHERE MaintainerUID = %s ", [userid])
cur = conn.execute("SELECT Name, PackagerUID FROM PackageBases " +
"WHERE MaintainerUID = ?", [userid])
for row in cur:
print((' ' if row[1] else '*') + row[0])
db.close()
conn.close()
def create_pkgbase(pkgbase, user):
......@@ -66,26 +54,25 @@ def create_pkgbase(pkgbase, user):
if pkgbase_exists(pkgbase):
die('{:s}: package base already exists: {:s}'.format(action, pkgbase))
db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
passwd=aur_db_pass, db=aur_db_name,
unix_socket=aur_db_socket)
cur = db.cursor()
conn = db.Connection()
cur.execute("SELECT ID FROM Users WHERE Username = %s ", [user])
cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user])
userid = cur.fetchone()[0]
if userid == 0:
die('{:s}: unknown user: {:s}'.format(action, user))
cur.execute("INSERT INTO PackageBases (Name, SubmittedTS, ModifiedTS, " +
"SubmitterUID, MaintainerUID) VALUES (%s, UNIX_TIMESTAMP(), " +
"UNIX_TIMESTAMP(), %s, %s)", [pkgbase, userid, userid])
cur = conn.execute("INSERT INTO PackageBases (Name, SubmittedTS, " +
"ModifiedTS, SubmitterUID, MaintainerUID) VALUES " +
"(?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), ?, ?)",
[pkgbase, userid, userid])
pkgbase_id = cur.lastrowid
cur.execute("INSERT INTO PackageNotifications (PackageBaseID, UserID) " +
"VALUES (%s, %s)", [pkgbase_id, userid])
cur = conn.execute("INSERT INTO PackageNotifications " +
"(PackageBaseID, UserID) VALUES (?, ?)",
[pkgbase_id, userid])
db.commit()
db.close()
conn.commit()
conn.close()
def pkgbase_set_keywords(pkgbase, keywords):
......@@ -93,34 +80,29 @@ def pkgbase_set_keywords(pkgbase, keywords):
if not pkgbase_id:
die('{:s}: package base not found: {:s}'.format(action, pkgbase))
db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
passwd=aur_db_pass, db=aur_db_name,
unix_socket=aur_db_socket)
cur = db.cursor()
conn = db.Connection()
cur.execute("DELETE FROM PackageKeywords WHERE PackageBaseID = %s",
[pkgbase_id])
conn.execute("DELETE FROM PackageKeywords WHERE PackageBaseID = ?",
[pkgbase_id])
for keyword in keywords:
cur.execute("INSERT INTO PackageKeywords (PackageBaseID, Keyword) "
"VALUES (%s, %s)", [pkgbase_id, keyword])
conn.execute("INSERT INTO PackageKeywords (PackageBaseID, Keyword) " +
"VALUES (?, ?)", [pkgbase_id, keyword])
db.commit()
db.close()
conn.commit()
conn.close()
def pkgbase_has_write_access(pkgbase, user):
db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
passwd=aur_db_pass, db=aur_db_name,
unix_socket=aur_db_socket, buffered=True)
cur = db.cursor()
cur.execute("SELECT COUNT(*) FROM PackageBases " +
"LEFT JOIN PackageComaintainers " +
"ON PackageComaintainers.PackageBaseID = PackageBases.ID " +
"INNER JOIN Users ON Users.ID = PackageBases.MaintainerUID " +
"OR PackageBases.MaintainerUID IS NULL " +
"OR Users.ID = PackageComaintainers.UsersID " +
"WHERE Name = %s AND Username = %s", [pkgbase, user])
conn = db.Connection()
cur = conn.execute("SELECT COUNT(*) FROM PackageBases " +
"LEFT JOIN PackageComaintainers " +
"ON PackageComaintainers.PackageBaseID = PackageBases.ID " +
"INNER JOIN Users " +
"ON Users.ID = PackageBases.MaintainerUID " +
"OR PackageBases.MaintainerUID IS NULL " +
"OR Users.ID = PackageComaintainers.UsersID " +
"WHERE Name = ? AND Username = ?", [pkgbase, user])
return cur.fetchone()[0] > 0
......
#!/usr/bin/python3
import configparser
import mysql.connector
import os
import pygit2
import re
......@@ -11,15 +10,11 @@ import sys
import srcinfo.parse
import srcinfo.utils
import db
config = configparser.RawConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + "/../conf/config")
aur_db_host = config.get('database', 'host')
aur_db_name = config.get('database', 'name')
aur_db_user = config.get('database', 'user')
aur_db_pass = config.get('database', 'password')
aur_db_socket = config.get('database', 'socket')
notify_cmd = config.get('notifications', 'notify-cmd')
repo_path = config.get('serve', 'repo-path')
......@@ -65,27 +60,27 @@ def parse_dep(depstring):
return (depname, depcond)
def save_metadata(metadata, db, cur, user):
def save_metadata(metadata, conn, user):
# Obtain package base ID and previous maintainer.
pkgbase = metadata['pkgbase']
cur.execute("SELECT ID, MaintainerUID FROM PackageBases "
"WHERE Name = %s", [pkgbase])
cur = conn.execute("SELECT ID, MaintainerUID FROM PackageBases "
"WHERE Name = ?", [pkgbase])
(pkgbase_id, maintainer_uid) = cur.fetchone()
was_orphan = not maintainer_uid
# Obtain the user ID of the new maintainer.
cur.execute("SELECT ID FROM Users WHERE Username = %s", [user])
cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user])
user_id = int(cur.fetchone()[0])
# Update package base details and delete current packages.
cur.execute("UPDATE PackageBases SET ModifiedTS = UNIX_TIMESTAMP(), " +
"PackagerUID = %s, OutOfDateTS = NULL WHERE ID = %s",
[user_id, pkgbase_id])
cur.execute("UPDATE PackageBases SET MaintainerUID = %s " +
"WHERE ID = %s AND MaintainerUID IS NULL",
[user_id, pkgbase_id])
cur.execute("DELETE FROM Packages WHERE PackageBaseID = %s",
[pkgbase_id])
conn.execute("UPDATE PackageBases SET ModifiedTS = UNIX_TIMESTAMP(), " +
"PackagerUID = ?, OutOfDateTS = NULL WHERE ID = ?",
[user_id, pkgbase_id])
conn.execute("UPDATE PackageBases SET MaintainerUID = ? " +
"WHERE ID = ? AND MaintainerUID IS NULL",
[user_id, pkgbase_id])
conn.execute("DELETE FROM Packages WHERE PackageBaseID = ?",
[pkgbase_id])
for pkgname in srcinfo.utils.get_package_names(metadata):
pkginfo = srcinfo.utils.get_merged_package(pkgname, metadata)
......@@ -102,94 +97,94 @@ def save_metadata(metadata, db, cur, user):
pkginfo[field] = None
# Create a new package.
cur.execute("INSERT INTO Packages (PackageBaseID, Name, " +
"Version, Description, URL) " +
"VALUES (%s, %s, %s, %s, %s)",
[pkgbase_id, pkginfo['pkgname'], ver,
pkginfo['pkgdesc'], pkginfo['url']])
db.commit()
cur = conn.execute("INSERT INTO Packages (PackageBaseID, Name, " +
"Version, Description, URL) " +
"VALUES (?, ?, ?, ?, ?)",
[pkgbase_id, pkginfo['pkgname'], ver,
pkginfo['pkgdesc'], pkginfo['url']])
conn.commit()
pkgid = cur.lastrowid
# Add package sources.
for source_info in extract_arch_fields(pkginfo, 'source'):
cur.execute("INSERT INTO PackageSources (PackageID, Source, " +
"SourceArch) VALUES (%s, %s, %s)",
[pkgid, source_info['value'], source_info['arch']])
conn.execute("INSERT INTO PackageSources (PackageID, Source, " +
"SourceArch) VALUES (?, ?, ?)",
[pkgid, source_info['value'], source_info['arch']])
# Add package dependencies.
for deptype in ('depends', 'makedepends',
'checkdepends', 'optdepends'):
cur.execute("SELECT ID FROM DependencyTypes WHERE Name = %s",
[deptype])
cur = conn.execute("SELECT ID FROM DependencyTypes WHERE Name = ?",
[deptype])
deptypeid = cur.fetchone()[0]
for dep_info in extract_arch_fields(pkginfo, deptype):
depname, depcond = parse_dep(dep_info['value'])
deparch = dep_info['arch']
cur.execute("INSERT INTO PackageDepends (PackageID, " +
"DepTypeID, DepName, DepCondition, DepArch) " +
"VALUES (%s, %s, %s, %s, %s)",
[pkgid, deptypeid, depname, depcond, deparch])
conn.execute("INSERT INTO PackageDepends (PackageID, " +
"DepTypeID, DepName, DepCondition, DepArch) " +
"VALUES (?, ?, ?, ?, ?)",
[pkgid, deptypeid, depname, depcond, deparch])
# Add package relations (conflicts, provides, replaces).
for reltype in ('conflicts', 'provides', 'replaces'):
cur.execute("SELECT ID FROM RelationTypes WHERE Name = %s",
[reltype])
cur = conn.execute("SELECT ID FROM RelationTypes WHERE Name = ?",
[reltype])
reltypeid = cur.fetchone()[0]
for rel_info in extract_arch_fields(pkginfo, reltype):
relname, relcond = parse_dep(rel_info['value'])
relarch = rel_info['arch']
cur.execute("INSERT INTO PackageRelations (PackageID, " +
"RelTypeID, RelName, RelCondition, RelArch) " +
"VALUES (%s, %s, %s, %s, %s)",
[pkgid, reltypeid, relname, relcond, relarch])
conn.execute("INSERT INTO PackageRelations (PackageID, " +
"RelTypeID, RelName, RelCondition, RelArch) " +
"VALUES (?, ?, ?, ?, ?)",
[pkgid, reltypeid, relname, relcond, relarch])
# Add package licenses.
if 'license' in pkginfo:
for license in pkginfo['license']:
cur.execute("SELECT ID FROM Licenses WHERE Name = %s",
[license])
cur = conn.execute("SELECT ID FROM Licenses WHERE Name = ?",
[license])
if cur.rowcount == 1:
licenseid = cur.fetchone()[0]
else:
cur.execute("INSERT INTO Licenses (Name) VALUES (%s)",
[license])
db.commit()
cur = conn.execute("INSERT INTO Licenses (Name) " +
"VALUES (?)", [license])
conn.commit()
licenseid = cur.lastrowid
cur.execute("INSERT INTO PackageLicenses (PackageID, " +
"LicenseID) VALUES (%s, %s)",
[pkgid, licenseid])
conn.execute("INSERT INTO PackageLicenses (PackageID, " +
"LicenseID) VALUES (?, ?)",
[pkgid, licenseid])
# Add package groups.
if 'groups' in pkginfo:
for group in pkginfo['groups']:
cur.execute("SELECT ID FROM Groups WHERE Name = %s",
[group])
cur = conn.execute("SELECT ID FROM Groups WHERE Name = ?",
[group])
if cur.rowcount == 1:
groupid = cur.fetchone()[0]
else:
cur.execute("INSERT INTO Groups (Name) VALUES (%s)",
[group])
db.commit()
cur = conn.execute("INSERT INTO Groups (Name) VALUES (?)",
[group])
conn.commit()
groupid = cur.lastrowid
cur.execute("INSERT INTO PackageGroups (PackageID, "
"GroupID) VALUES (%s, %s)", [pkgid, groupid])
conn.execute("INSERT INTO PackageGroups (PackageID, "
"GroupID) VALUES (?, ?)", [pkgid, groupid])
# Add user to notification list on adoption.
if was_orphan:
cur.execute("SELECT COUNT(*) FROM PackageNotifications WHERE " +
"PackageBaseID = %s AND UserID = %s",
[pkgbase_id, user_id])
cur = conn.execute("SELECT COUNT(*) FROM PackageNotifications WHERE " +
"PackageBaseID = ? AND UserID = ?",
[pkgbase_id, user_id])
if cur.fetchone()[0] == 0:
cur.execute("INSERT INTO PackageNotifications " +
"(PackageBaseID, UserID) VALUES (%s, %s)",
[pkgbase_id, user_id])
conn.execute("INSERT INTO PackageNotifications " +
"(PackageBaseID, UserID) VALUES (?, ?)",
[pkgbase_id, user_id])
db.commit()
conn.commit()
def update_notify(db, cur, user, pkgbase_id):
def update_notify(conn, user, pkgbase_id):
# Obtain the user ID of the new maintainer.
cur.execute("SELECT ID FROM Users WHERE Username = %s", [user])
cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user])
user_id = int(cur.fetchone()[0])
# Execute the notification script.
......@@ -233,10 +228,7 @@ else:
if refname != "refs/heads/master":
die("pushing to a branch other than master is restricted")
db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
passwd=aur_db_pass, db=aur_db_name,
unix_socket=aur_db_socket, buffered=True)
cur = db.cursor()
conn = db.Connection()
# Detect and deny non-fast-forwards.
if sha1_old != "0000000000000000000000000000000000000000" and not privileged:
......@@ -339,13 +331,13 @@ if metadata_pkgbase != pkgbase:
# Ensure that packages are neither blacklisted nor overwritten.
pkgbase = metadata['pkgbase']
cur.execute("SELECT ID FROM PackageBases WHERE Name = %s", [pkgbase])
cur = conn.execute("SELECT ID FROM PackageBases WHERE Name = ?", [pkgbase])
pkgbase_id = cur.fetchone()[0] if cur.rowcount == 1 else 0
cur.execute("SELECT Name FROM PackageBlacklist")
cur = conn.execute("SELECT Name FROM PackageBlacklist")
blacklist = [row[0] for row in cur.fetchall()]
cur.execute("SELECT Name, Repo FROM OfficialProviders")
cur = conn.execute("SELECT Name, Repo FROM OfficialProviders")
providers = dict(cur.fetchall())
for pkgname in srcinfo.utils.get_package_names(metadata):
......@@ -358,13 +350,13 @@ for pkgname in srcinfo.utils.get_package_names(metadata):
repo = providers[pkgname]
warn_or_die('package already provided by [{:s}]: {:s}'.format(repo, pkgname))
cur.execute("SELECT COUNT(*) FROM Packages WHERE Name = %s AND " +
"PackageBaseID <> %s", [pkgname, pkgbase_id])
cur = conn.execute("SELECT COUNT(*) FROM Packages WHERE Name = ? AND " +
"PackageBaseID <> ?", [pkgname, pkgbase_id])
if cur.fetchone()[0] > 0:
die('cannot overwrite package: {:s}'.format(pkgname))
# Store package base details in the database.
save_metadata(metadata, db, cur, user)
save_metadata(metadata, conn, user)
# Create (or update) a branch with the name of the package base for better
# accessibility.
......@@ -377,7 +369,7 @@ repo.create_reference('refs/heads/' + pkgbase, sha1_new, True)
repo.create_reference('refs/namespaces/' + pkgbase + '/HEAD', sha1_new, True)
# Send package update notifications.
update_notify(db, cur, user, pkgbase_id)
update_notify(conn, user, pkgbase_id)
# Close the database.
db.close()
conn.close()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment