Add support for Terms of Service documents
diff mbox

Message ID 20170427181219.13098-1-lfleischer@archlinux.org
State Accepted, archived
Headers show

Commit Message

Lukas Fleischer April 27, 2017, 6:12 p.m. UTC
This allows for adding Terms of Service documents to the database that
registered users need to accept before using the AUR. A revision field
can be used to indicate whether a document was updated. If it is
increased, all users are again asked to accept the new terms.

Signed-off-by: Lukas Fleischer <lfleischer@archlinux.org>
---
 schema/aur-schema.sql     | 20 +++++++++++++
 upgrading/4.6.0.txt       | 20 +++++++++++++
 web/html/tos.php          | 50 ++++++++++++++++++++++++++++++++
 web/lib/acctfuncs.inc.php | 74 +++++++++++++++++++++++++++++++++++++++++++++++
 web/lib/aur.inc.php       | 23 +++++++++++++++
 web/lib/routing.inc.php   |  1 +
 6 files changed, 188 insertions(+)
 create mode 100644 web/html/tos.php

Comments

Lukas Fleischer April 30, 2017, 2:47 p.m. UTC | #1
On Thu, 27 Apr 2017 at 20:12:19, Lukas Fleischer wrote:
> This allows for adding Terms of Service documents to the database that
> registered users need to accept before using the AUR. A revision field
> can be used to indicate whether a document was updated. If it is
> increased, all users are again asked to accept the new terms.
> 
> Signed-off-by: Lukas Fleischer <lfleischer@archlinux.org>
> ---
>  schema/aur-schema.sql     | 20 +++++++++++++
>  upgrading/4.6.0.txt       | 20 +++++++++++++
>  web/html/tos.php          | 50 ++++++++++++++++++++++++++++++++
>  web/lib/acctfuncs.inc.php | 74 +++++++++++++++++++++++++++++++++++++++++++++++
>  web/lib/aur.inc.php       | 23 +++++++++++++++
>  web/lib/routing.inc.php   |  1 +
>  6 files changed, 188 insertions(+)
>  create mode 100644 web/html/tos.php
> [...]
> diff --git a/web/html/tos.php b/web/html/tos.php
> new file mode 100644
> index 0000000..135fa1e
> --- /dev/null
> +++ b/web/html/tos.php
> [...]
> +                       <ul>
> +                       <?php foreach($terms as $row): ?>
> +                               <li><a href="<?= urlencode(sprintf($row["URL"], $row["Revision"])) ?>"><?= htmlspecialchars($row["Description"]) ?></a> (<?= __('revision %d', $row["Revision"]) ?>)</li>
> [...]

The urlencode() call should be replaced by htmlspecialchars() with
ENT_QUOTES here. I fixed this on the pu branch.

Patch
diff mbox

diff --git a/schema/aur-schema.sql b/schema/aur-schema.sql
index e584165..45272bb 100644
--- a/schema/aur-schema.sql
+++ b/schema/aur-schema.sql
@@ -379,3 +379,23 @@  CREATE TABLE Bans (
 	BanTS TIMESTAMP NOT NULL,
 	PRIMARY KEY (IPAddress)
 ) ENGINE = InnoDB;
+
+-- Terms and Conditions
+--
+CREATE TABLE Terms (
+	ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+	Description VARCHAR(255) NOT NULL,
+	URL VARCHAR(8000) NOT NULL,
+	Revision INTEGER UNSIGNED NOT NULL DEFAULT 1,
+	PRIMARY KEY (ID)
+) ENGINE = InnoDB;
+
+-- Terms and Conditions accepted by users
+--
+CREATE TABLE AcceptedTerms (
+	UsersID INTEGER UNSIGNED NOT NULL,
+	TermsID INTEGER UNSIGNED NOT NULL,
+	Revision INTEGER UNSIGNED NOT NULL DEFAULT 0,
+	FOREIGN KEY (UsersID) REFERENCES Users(ID) ON DELETE CASCADE,
+	FOREIGN KEY (TermsID) REFERENCES Terms(ID) ON DELETE CASCADE
+) ENGINE = InnoDB;
diff --git a/upgrading/4.6.0.txt b/upgrading/4.6.0.txt
index b051bac..816409d 100644
--- a/upgrading/4.6.0.txt
+++ b/upgrading/4.6.0.txt
@@ -15,3 +15,23 @@  UPDATE PackageDepends
 ---
 ALTER TABLE PackageComments ADD COLUMN RenderedComment TEXT NOT NULL;
 ---
+
+3. Add Terms and AcceptedTerms tables:
+
+---
+CREATE TABLE Terms (
+	ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+	Description VARCHAR(255) NOT NULL,
+	URL VARCHAR(8000) NOT NULL,
+	Revision INTEGER UNSIGNED NOT NULL DEFAULT 1,
+	PRIMARY KEY (ID)
+) ENGINE = InnoDB;
+
+CREATE TABLE AcceptedTerms (
+	UsersID INTEGER UNSIGNED NOT NULL,
+	TermsID INTEGER UNSIGNED NOT NULL,
+	Revision INTEGER UNSIGNED NOT NULL DEFAULT 0,
+	FOREIGN KEY (UsersID) REFERENCES Users(ID) ON DELETE CASCADE,
+	FOREIGN KEY (TermsID) REFERENCES Terms(ID) ON DELETE CASCADE
+) ENGINE = InnoDB;
+---
diff --git a/web/html/tos.php b/web/html/tos.php
new file mode 100644
index 0000000..135fa1e
--- /dev/null
+++ b/web/html/tos.php
@@ -0,0 +1,50 @@ 
+<?php
+set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
+
+include_once("aur.inc.php");
+
+$uid = uid_from_sid($_COOKIE["AURSID"]);
+
+if (isset($_POST["accept"]) && $_POST["accept"]) {
+	accept_terms($uid, $_POST["rev"]);
+	header("Location: " . get_uri('/'));
+}
+
+$terms = fetch_updated_terms($uid);
+if (!$terms) {
+	header("Location: " . get_uri('/'));
+}
+
+html_header('AUR ' . __("Terms of Service"));
+?>
+<div id="dev-login" class="box">
+	<h2>AUR <?= __('Terms of Service') ?></h2>
+	<?php if (isset($_COOKIE["AURSID"])): ?>
+	<form method="post" action="<?= get_uri('/tos') ?>">
+		<fieldset>
+			<p>
+				<?= __("Logged-in as: %s", '<strong>' . username_from_sid($_COOKIE["AURSID"]) . '</strong>'); ?>
+			</p>
+			<p>
+				<?= __("The following documents have been updated. Please review them carefully:"); ?>
+			</p>
+			<ul>
+			<?php foreach($terms as $row): ?>
+				<li><a href="<?= urlencode(sprintf($row["URL"], $row["Revision"])) ?>"><?= htmlspecialchars($row["Description"]) ?></a> (<?= __('revision %d', $row["Revision"]) ?>)</li>
+			<?php endforeach; ?>
+			</ul>
+			<p>
+				<?php foreach($terms as $row): ?>
+					<input type="hidden" name="rev[<?= $row["ID"] ?>]" value="<?= $row["Revision"] ?>" />
+				<?php endforeach; ?>
+				<input type="checkbox" name="accept" /> <?= __("I accept the terms and conditions above."); ?>
+			</p>
+			<p>
+				<input type="submit" name="submit" value="<?= __("Continue") ?>" />
+			</p>
+		</fieldset>
+	</form>
+	<?php endif; ?>
+</div>
+<?php
+html_footer(AURWEB_VERSION);
diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php
index 22b3ca8..e45d735 100644
--- a/web/lib/acctfuncs.inc.php
+++ b/web/lib/acctfuncs.inc.php
@@ -1325,3 +1325,77 @@  function notify($params) {
 
 	return proc_close($p);
 }
+
+/*
+ * Obtain a list of terms a given user has not yet accepted.
+ *
+ * @param int $uid The ID of the user to obtain terms for.
+ *
+ * @return array A list of terms the user has not yet accepted.
+ */
+function fetch_updated_terms($uid) {
+	$dbh = DB::connect();
+
+	$q = "SELECT ID, Terms.Revision, Description, URL ";
+	$q .= "FROM Terms LEFT JOIN AcceptedTerms ";
+	$q .= "ON AcceptedTerms.TermsID = Terms.ID ";
+	$q .= "AND AcceptedTerms.UsersID = " . intval($uid) . " ";
+	$q .= "WHERE AcceptedTerms.Revision IS NULL OR ";
+	$q .= "AcceptedTerms.Revision < Terms.Revision";
+
+	$result = $dbh->query($q);
+
+	if ($result) {
+		return $result->fetchAll();
+	} else {
+		return array();
+	}
+}
+
+/*
+ * Accept a list of given terms.
+ *
+ * @param int $uid The ID of the user to accept the terms.
+ * @param array $termrev An array mapping each term to the accepted revision.
+ *
+ * @return void
+ */
+function accept_terms($uid, $termrev) {
+	$dbh = DB::connect();
+
+	$q = "SELECT TermsID, Revision FROM AcceptedTerms ";
+	$q .= "WHERE UsersID = " . intval($uid);
+
+	$result = $dbh->query($q);
+
+	if (!$result) {
+		return;
+	}
+
+	$termrev_update = array();
+	while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+		$id = $row['TermsID'];
+		if (!array_key_exists($id, $termrev)) {
+			continue;
+		}
+		if ($row['Revision'] < $termrev[$id]) {
+			$termrev_update[$id] = $termrev[$id];
+		}
+	}
+	$termrev_add = array_diff_key($termrev, $termrev_update);
+
+	foreach ($termrev_add as $id => $rev) {
+		$q = "INSERT INTO AcceptedTerms (TermsID, UsersID, Revision) ";
+		$q .= "VALUES (" . intval($id) . ", " . intval($uid) . ", ";
+		$q .= intval($rev) . ")";
+		$dbh->exec($q);
+	}
+
+	foreach ($termrev_update as $id => $rev) {
+		$q = "UPDATE AcceptedTerms ";
+		$q .= "SET Revision = " . intval($rev) . " ";
+		$q .= "WHERE TermsID = " . intval($id) . " AND ";
+		$q .= "UsersID = " . intval($uid);
+		$dbh->exec($q);
+	}
+}
diff --git a/web/lib/aur.inc.php b/web/lib/aur.inc.php
index 063de8f..ce569ea 100644
--- a/web/lib/aur.inc.php
+++ b/web/lib/aur.inc.php
@@ -22,6 +22,7 @@  include_once('timezone.inc.php');
 set_tz();
 
 check_sid();
+check_tos();
 
 /**
  * Check if a visitor is logged in
@@ -92,6 +93,28 @@  function check_sid() {
 }
 
 /**
+ * Redirect user to the Terms of Service agreement if there are updated terms.
+ *
+ * @return void
+ */
+function check_tos() {
+	if (!isset($_COOKIE["AURSID"])) {
+		return;
+	}
+
+	$path = $_SERVER['PATH_INFO'];
+	$route = get_route($path);
+	if (!$route || $route == "tos.php") {
+		return;
+	}
+
+	if (count(fetch_updated_terms(uid_from_sid($_COOKIE["AURSID"]))) > 0) {
+		header('Location: ' . get_uri('/tos'));
+		exit();
+	}
+}
+
+/**
  * Verify the supplied CSRF token matches expected token
  *
  * @return bool True if the CSRF token is the same as the cookie SID, otherwise false
diff --git a/web/lib/routing.inc.php b/web/lib/routing.inc.php
index 8c45c62..7d9750a 100644
--- a/web/lib/routing.inc.php
+++ b/web/lib/routing.inc.php
@@ -16,6 +16,7 @@  $ROUTES = array(
 	'/passreset' => 'passreset.php',
 	'/rpc' => 'rpc.php',
 	'/rss' => 'rss.php',
+	'/tos' => 'tos.php',
 	'/tu' => 'tu.php',
 	'/addvote' => 'addvote.php',
 );