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 (d7746afecc4cbdc76cbc959eeed2dd185f58c29f) (5,908B) (mode 100644) [raw]
<?php
require_once($INC . "/sql.inc.php");
require_once($INC . "/state.inc.php");
require_once($INC . "/prof.inc.php");

$rg_keys_error = "";

function rg_keys_set_error($str)
{
	global $rg_keys_error;

	$rg_keys_error = $str;
}

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

/*
 * Extracts info about a ssh key
 */
function rg_keys_info($key)
{
	$t = explode(" ", $key, 3);

	$ret = array();
	$ret['ok'] = 0;

	if (!isset($t[1])) {
		rg_keys_set_error("malformed ssh key (missing fields)");
		return $ret;
	}

	$ret['type'] = $t[0];
	$ret['key'] = isset($t[1]) ? $t[1] : "";
	$ret['comment'] = isset($t[2]) ? $t[2] : "";

	$d = base64_decode($ret['key']);
	if ($d === FALSE) {
		rg_keys_set_error("malformed input (base64 failed)");
		return $ret;
	}
	$digest = md5($d);

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

	$ret['fingerprint'] = implode(":", $a);

	$ret['ok'] = 1;
	return $ret;
}

/*
 * Remove a key from database
 */
function rg_keys_remove($db, $rg_ui, $key_id)
{
	rg_prof_start("keys_remove");

	// 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);

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

	rg_prof_end("keys_remove");

	return TRUE;
}

/*
 * Count the number of keys per user
 */
function rg_keys_count($db, $uid)
{
	rg_prof_start("keys_count");

	$sql = "SELECT COUNT(*) AS count FROM keys WHERE uid = $uid";
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_keys_set_error("cannot query (" . rg_sql_error() . ")");
		return FALSE;
	}
	$row = rg_sql_fetch_array($res);
	rg_sql_free_result($res);

	rg_prof_end("keys_count");

	return $row['count'];
}

/*
 * Add a key
 * Returns the key_id of the key.
 */
function rg_keys_add($db, $rg_ui, $key)
{
	global $rg_max_ssh_keys;

	rg_prof_start("keys_add");

	$itime = time();
	$e_key = rg_sql_escape($db, $key);

	$ki = rg_keys_info($key);
	if ($ki['ok'] != 1)
		return FALSE;

	// check if we are over the maximum
	// the config after update may not have this defined.
	if ($rg_max_ssh_keys == 0)
		$rg_max_ssh_keys = 10;
	$no_of_keys = rg_keys_count($db, $rg_ui['uid']);
	if ($no_of_keys === FALSE)
		return FALSE;

	if ($no_of_keys >= $rg_max_ssh_keys) {
		rg_keys_set_error("too many keys; please delete some");
		return FALSE;
	}

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

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

	rg_prof_end("keys_add");

	return $id;
}

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

	rg_prof_start("keys_regen");

	$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;
	}

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

	$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;
	}

	rg_prof_end("keys_regen");

	return TRUE;
}

/*
 * List keys
 */
function rg_keys_list($db, $rg_ui, $url)
{
	rg_prof_start("keys_list");

	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;
	}

	$data = array();
	while (($row = rg_sql_fetch_array($res))) {
		$ki = rg_keys_info($row['key']);
		if ($ki['ok'] != 1) {
			rg_internal_error("Invalid key in db!");
			continue;
		}

		$t = $ki;
		$t['itime'] = gmdate("Y-m-d H:i", $row['itime']);

		$t['HTML:operations'] = "[<a href=\"$url"
			. "&amp;key_id=" . $row['key_id']
			. "&amp;delete=1\">Delete</a>]";

		$data[] = $t;
	}
	rg_sql_free_result($res);

	$ret = rg_template_table("user/keys/list", $data, array());

	rg_prof_end("keys_list");

	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