xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
<root> / inc / keys.inc.php (bbe6c5a1ac245d58d8c5b757fd493051d905186b) (4,872B) (mode 100644) [raw]
<?php
require_once($INC . "/sql.inc.php");
require_once($INC . "/state.inc.php");

$rg_keys_error = "";

function rg_keys_set_error($str)
{
	global $rg_keys_error;

	rg_log("\tError: $str");
	$rg_keys_error = $str;
}

function rg_keys_error()
{
	global $rg_keys_error;
	return $rg_keys_error;
}

/*
 * Validate key
 */
function rg_keys_valid($s)
{
	$v = explode(" ", $s);
	if (!isset($v[1])) {
		rg_keys_set_error("Malformed input (missing fields)");
		return FALSE;
	}

	$decoded = base64_decode(trim($v[1]));
	if ($decoded === FALSE) {
		rg_keys_set_error("Malformed input (base64 failed)");
		return FALSE;
	}

	return $decoded;
}

/*
 * Generates the fingerprint of a key
 */
function rg_keys_fingerprint($key)
{
	$decoded = rg_keys_valid($key);
	if ($decoded === FALSE)
		return rg_keys_error();

	$digest = md5($decoded);

	$a = array();
	for ($i = 0; $i < 16; $i++)
		$a[] = substr($digest, $i * 2, 2);

	return implode(":", $a);
}

/*
 * Remove a key from database
 */
function rg_keys_remove($db, $rg_ui, $key_id)
{
	// mark dirty
	if (rg_state_set($db, "authorized_keys", 1) === FALSE) {
		rg_keys_set_error("Cannot make state dirty (" . rg_state_error() . ")!");
		return FALSE;
	}

	// TODO: move this to caller?
	$e_key_id = sprintf("%u", $key_id);

	$sql = "DELETE FROM keys"
		. " WHERE uid = " . $rg_ui['uid']
		. " AND key_id = $e_key_id";
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_keys_set_error("Cannot delete key $key_id (" . rg_sql_error() . ")");
		return FALSE;
	}
	rg_sql_free_result($res);

	return TRUE;
}

/*
 * Add a key
 * Returns the key_id of the key.
 */
function rg_keys_add($db, $rg_ui, $key)
{
	$itime = time();
	$e_key = rg_sql_escape($db, $key);

	if (rg_keys_valid($key) === FALSE)
		return FALSE;

	// set dirty
	if (rg_state_set($db, "authorized_keys", 1) === FALSE) {
		rg_keys_set_error("cannot make state dirty: " . rg_state_error());
		return FALSE;
	}

	$sql = "INSERT INTO keys (itime, uid, key)"
		. " VALUES ($itime, " . $rg_ui['uid'] . ", '$e_key')";
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_keys_set_error("Cannot insert key (" . rg_sql_error() . ")");
		return FALSE;
	}
	rg_sql_free_result($res);

	return rg_sql_last_id($db);
}

/*
 * Regenerates authorized_keys files
 */
function rg_keys_regen($db)
{
	global $rg_keys_file;
	global $rg_scripts;
	global $rg_ssh_paras;

	$dirty = rg_state_get($db, "authorized_keys");
	if ($dirty == 0) {
		// Skip generation because is not dirty
		return TRUE;
	}

	// create .ssh folder if does not exists
	$dir = dirname($rg_keys_file);
	if (!file_exists($dir)) {
		if (!@mkdir($dir, 0700, TRUE)) {
			rg_keys_set_error("cannot create dir $dir ($php_errormsg)");
			return FALSE;
		}
	}

	$tmp = $rg_keys_file . ".tmp";
	$f = @fopen($tmp, "w");
	if ($f === FALSE) {
		rg_keys_set_error("cannot open file $tmp ($php_errormsg)");
		return FALSE;
	}

	if (chmod($tmp, 0600) === FALSE) {
		rg_keys_set_error("cannot chmod tmp file ($php_errmsg)");
		fclose($f);
		return FALSE;
	}

	$sql = "SELECT uid, key FROM keys";
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_keys_set_error("cannot query (" . rg_sql_error() . ")");
		return FALSE;
	}
	while (($row = rg_sql_fetch_array($res))) {
		rg_log("Writing key [" . $row['key'] . "] for uid " . $row['uid']);
		$buf = "command=\"/usr/bin/php " . $rg_scripts . "/scripts/remote.php"
			. " " . $row['uid'] . "\""
			. "," . $rg_ssh_paras
			. " " . $row['key'] . "\n";
		if (@fwrite($f, $buf) === FALSE) {
			rg_keys_set_error("cannot write. Disk space problems? ($php_errormsg)");
			fclose($f);
			unlink($tmp);
			rg_sql_free_result($res);
			return FALSE;
		}
	}
	rg_sql_free_result($res);

	fclose($f);

	if (@rename($tmp, $rg_keys_file) === FALSE) {
		rg_keys_set_error("cannot rename $tmp to $rg_keys_file ($php_errormsg)");
		unlink($tmp);
		return FALSE;
	}

	// mark file as clean
	rg_state_set($db, "authorized_keys", 0);

	return TRUE;
}

/*
 * List keys
 */
function rg_keys_list($db, $rg_ui, $url)
{
	rg_log("keys_list: rg_uid=" . $rg_ui['uid'] . ", url=$url...");

	$sql = "SELECT * FROM keys WHERE uid = " . $rg_ui['uid'];
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_keys_set_error("Cannot query (" . rg_sql_error() . ")");
		return FALSE;
	}

	$ret = "<table>\n";
	$ret .= "<tr>\n";
	$ret .= "	<th>Date (UTC)</th>\n";
	$ret .= "	<th>Fingerprint</th>\n";
	$ret .= "	<th>Operations</th>\n";
	$ret .= "</tr>\n";
	while (($row = rg_sql_fetch_array($res))) {
		$ret .= "<tr>\n";
		$ret .= "	<td>" . gmdate("Y-m-d H:i:s", $row['itime']) . "</td>\n";
		$ret .= "	<td>" . rg_keys_fingerprint($row['key']) . "</td>\n";

		$oper = "";
		$oper = "[<a href=\"$url&amp;key_id=" . $row['key_id']
			. "&amp;delete=1\">Delete!</a>]";
		$ret .= "	<td>" . $oper . "</td>\n";
		$ret .= "</tr>\n";
	}
	$ret .= "</table>\n";
	rg_sql_free_result($res);

	return $ret;
}
?>
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master