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> / tests / ssh.php (cca977c466d7a5e1e3d255717a11ebc898600b81) (6,978B) (mode 100644) [raw]
<?php
error_reporting(E_ALL | E_STRICT);
ini_set("track_errors", "On");

$test_normal = TRUE;

$INC = dirname(__FILE__) . "/../inc";
require_once(dirname(__FILE__) . "/config.php");
require_once($INC . "/init.inc.php");
require_once($INC . "/user.inc.php");
require_once("helpers.inc.php");
require_once("http.inc.php");

rg_log_set_file("ssh.log");

require_once("common.php");

$_testns = 'ssh';


prepare_http();

// This test makes sense only on my devel machine
if (php_uname("n") != "r1.embedromix.ro") {
	// TODO: start a sshd daemon?
	rg_log("OK!");
	exit(0);
}


rg_log('');
rg_log_enter("Creating a user...");
rg_test_create_user($db, $rg_ui);
rg_test_create_repo($db, $rg_ui, $repo);
$r = test_login($test_url, $rg_ui);
if ($r === FALSE) {
	rg_log("Cannot login!");
	exit(1);
}
rg_log_exit();


rg_test_upload_ssh_key($db, $rg_ui, 'ssh', $kn);


rg_log('');
rg_log_enter('Testing ssh help...');
$list = array('', 'status', 'repos', 'repo', 'totp');
foreach ($list as $s) {
	rg_log('Connecting for [' . $s . ']');
	$r = test_ssh($rg_ui['uid'], $s);
	if ($r['ok'] != 1) {
		rg_log_ml('error: ' . $r['stderr']);
		exit(1);
	}
	if (!strstr($r['stderr'], "Welcome to RocketGit")) {
		rg_log_ml('stderr: ' . $r['stderr']);
		rg_log("Trying to get the help detected missing welcome!");
		exit(1);
	}
}
rg_log_exit();


rg_log('');
rg_log_enter('Testing ssh help for totp commands...');
$list = array('remove-device', 'unenroll');
foreach ($list as $s) {
	rg_log('Connecting for [totp ' . $s . ']');
	$r = test_ssh($rg_ui['uid'], ' totp ' . $s);
	if ($r['ok'] != 1) {
		rg_log_ml('error: ' . $r['stderr']);
		exit(1);
	}
}
rg_log_exit();


rg_log('');
rg_log_enter('Testing wrong command');
$r = test_ssh($rg_ui['uid'], ' wrongcmd');
if ($r['ok'] != 0) {
	rg_log_ml('error: ' . $r['stderr']);
	rg_log('We should receive error code 0 not ' . $r['ok'] . '!');
	exit(1);
}
if (!strstr($r['stderr'], "nknown command")) {
	rg_log('stderr: ' . $r['stderr']);
	rg_log("Wrong answer for a wrong command!");
	exit(1);
}
rg_log_exit();


rg_log('');
rg_log('Testing enroll procedure');
$r = test_ssh($rg_ui['uid'], ' totp enroll', '', FALSE, FALSE);
$t = explode('enter the following code: ', $r['data']);
$t = explode('.', $t[1]);
$key = trim($t[0]);
rg_log("key=$key");

$tc = intval(time() / 30) - 1; // we try one in the past
$token = rg_totp_compute($key, $tc, 6);
$r = test_ssh($rg_ui['uid'], ' totp enroll ' . $token);
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['data'], 'Success!')) {
	rg_log('data: ' . $r['data']);
	rg_log('Cannot enroll!');
	exit(1);
}


rg_log('');
rg_log('Testing \'val\' command');
$tc = intval(time() / 30);
$token = rg_totp_compute($key, $tc, 6);
$r = test_ssh($rg_ui['uid'], ' totp val ' . $token . ' 2m');
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['data'], 'Success!')) {
	rg_log('data: ' . $r['data']);
	rg_log('Cannot validate ip!');
	exit(1);
}
$t = explode('valid till ', $r['data']);
$t = explode(' (', $t[1]);
$exp = trim($t[0]);
rg_log('exp=' . $exp);

rg_log('');
rg_log('Reuse of the token must be forbidden (device)');
$r = test_ssh($rg_ui['uid'], ' totp val ' . $token . ' 2m');
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['stderr'], 'cannot reuse')) {
	rg_log('stderr: ' . $r['data']);
	rg_log('we get no error on token reuse!');
	exit(1);
}


rg_log('');
rg_log('Testing \'list-val\' command');
$r = test_ssh($rg_ui['uid'], ' totp list-val');
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['data'], $exp)) {
	rg_log('data: ' . $r['data']);
	rg_log('Invalid output for list-val!');
	exit(1);
}


rg_log('');
rg_log('Testing \'inval\' command - wrong ip');
$tc = intval(time() / 30) + 1; // we try one in the future
$token = rg_totp_compute($key, $tc, 6);
$r = test_ssh($rg_ui['uid'], ' totp inval 1.1.1.1');
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['stderr'], 'ip not found')) {
	rg_log('stderr: ' . $r['stderr']);
	rg_log('Cannot invalidate ip!');
	exit(1);
}


rg_log('');
rg_log('Testing \'inval\' command - all');
$tc = intval(time() / 30) + 1; // we try one in the future
$token = rg_totp_compute($key, $tc, 6);
$r = test_ssh($rg_ui['uid'], ' totp inval all');
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['data'], 'Success!')) {
	rg_log('data: ' . $r['data']);
	rg_log('Cannot invalidate all!');
	exit(1);
}


rg_log('');
rg_log('Testing \'remove-device\'');
$tc = intval(time() / 30) + 2;
$token = rg_totp_compute($key, $tc, 6);
$_cmd = ' totp remove-device ' . $token;
$r = test_ssh($rg_ui['uid'], $_cmd);
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['data'], 'Success!')) {
	rg_log('data: ' . $r['data']);
	rg_log('Cannot remove device!');
	exit(1);
}


$sc = rg_test_sc_generate($db, $rg_ui);
$sql = "UPDATE scratch_codes SET sc = '0' || substring(sc from 1 for 7)"
	. " WHERE uid = " . $rg_ui['uid'];
$res = rg_sql_query($db, $sql);
if ($res === FALSE) {
	rg_log('Cannot do query!');
	exit(1);
}
rg_sql_free_result($res);
// we want to test with a short code, so insert one and flush cache
$key = 'user::' . $rg_ui['uid'] . '::login_tokens::sc';
rg_cache_unset($key, 0);
foreach ($sc as &$t)
	$t = substr($t, 0, 7);


rg_log('');
rg_log_enter('Testing \'unenroll\' (must work)');
$token = array_pop($sc);
$token = ltrim($token, '0');
$_cmd = ' totp unenroll ' . $token;
$r = test_ssh($rg_ui['uid'], $_cmd);
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['data'], 'You are now unenrolled')) {
	rg_log('data: ' . $r['data']);
	rg_log('Cannot unenroll!');
	exit(1);
}
rg_log_exit();


rg_log('');
rg_log_enter('After enroll we should not be able to use the scratch codes');
$token = array_pop($sc);
$r = test_ssh($rg_ui['uid'], ' totp val ' . $token . ' 2m');
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (strstr($r['data'], 'Success!')) {
	rg_log('data: ' . $r['data']);
	rg_log('Seems we are able to use scratch codes after unenroll!');
	exit(1);
}
rg_log_exit();


$sc = rg_test_sc_generate($db, $rg_ui);


rg_log('');
rg_log_enter('sc: testing \'val\' cmd...');
$token = array_pop($sc);
$_cmd = ' totp val ' . $token . ' 2m';
$r = test_ssh($rg_ui['uid'], $_cmd);
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['data'], 'Success!')) {
	rg_log('data: ' . $r['data']);
	rg_log('Cannot validate ip!');
	exit(1);
}
rg_log_exit();


rg_log('');
rg_log_enter('Reuse of the scratch code must be forbidden (sc)');
$_cmd = ' totp val ' . $token . ' 2m';
$r = test_ssh($rg_ui['uid'], $_cmd);
if ($r['ok'] != 1) {
	rg_log_ml('error: ' . $r['stderr']);
	exit(1);
}
if (!strstr($r['stderr'], 'invalid token')) {
	rg_log('stderr: ' . $r['stderr']);
	rg_log('we get no error on token reuse!');
	exit(1);
}
rg_log_exit();

rg_log("OK!");
?>
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