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 / http_totp.php (f7dd98e8b7a3584fb5244d18f95138de0a4ad7ac) (5,428B) (mode 100644) [raw]
<?php
//
// Will test the login by TOTP
//

error_reporting(E_ALL | E_STRICT);
ini_set("track_errors", "On");

$rg_cache_debug = TRUE;
$test_normal = TRUE;

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

rg_log_set_file("http_totp.log");

require_once("common.php");

$_testns = 'http_totp';


$rg_user_max_len = 60;

prepare_http();

rg_test_create_user($db, $rg_ui);

// Add an totp token to this account
$key = 'ACHCBCCVQ7AK4RGM';

$r = rg_totp_enroll($db, $rg_ui['uid'], 'test', $key, '127.0.0.1', 't');
if ($r !== TRUE) {
	rg_log('cannot enroll!');
	exit(1);
}

// Now test the login without and with login_token
$lt = rg_totp_compute($key, time() / 30, 6);

// First we need to load the form so we can get the token
// We provide an old cookie to test if we generate a new pre-login one
rg_log('');
rg_log_enter('Loading login form...');
$r = do_req($test_url . "/op/login", $data, $headers);
if ($r === FALSE) {
	rg_log("Cannot load login form.");
	exit(1);
}
$good_token = $r['tokens']['login'];
rg_log_exit();


rg_log('');
rg_log_enter("Do the login without login token (must fail)...");
$data = array(
	"doit" => 1,
	"token" => $good_token,
	"user" => $rg_ui['username'],
	"pass" => $rg_ui['pass'],
	"login_token" => '',
	"lock_ip" => 0);
$headers = array();
$r = do_req($test_url . "/op/login", $data, $headers);
if ($r === FALSE) {
	rg_log_ml('r=' . print_r($r, TRUE));
	rg_log("Cannot login!");
	exit(1);
}
if (!strstr($r['body'], "invalid user")) {
	rg_log_ml('r=' . print_r($r, TRUE));
	rg_log("Seems we can login without token!");
	exit(1);
}
$good_token = $r['tokens']['login'];
rg_log_exit();


rg_log('');
rg_log_enter("Do the login (token=$good_token login_token=$lt) (must work)...");
$data = array(
	"doit" => 1,
	"token" => $good_token,
	"user" => $rg_ui['username'],
	"pass" => $rg_ui['pass'],
	"login_token" => $lt,
	"lock_ip" => 0);
$headers = array();
$r = do_req($test_url . "/op/login", $data, $headers);
if ($r === FALSE) {
	rg_log_ml('r=' . print_r($r, TRUE));
	rg_log("Cannot login!");
	exit(1);
}
if (strstr($r['body'], "invalid user")) {
	rg_log_ml('r=' . print_r($r, TRUE));
	rg_log("Login invalid!");
	exit(1);
}
rg_log_exit();


$r = totp_enroll($db);
if ($r['ok'] !== 1)
	exit(1);


rg_log('');
rg_log_enter('Testing the deletion of scratch codes');
$sc1 = rg_test_sc_generate($db, $rg_ui);
sleep(1); // to not have the same itime; TODO: we will add uids to scratch_codes table
$sc2 = rg_test_sc_generate($db, $rg_ui);
$sql = "SELECT DISTINCT itime FROM scratch_codes WHERE uid = " . $rg_ui['uid'];
$res = rg_sql_query($db, $sql);
$list = array();
while (($row = rg_sql_fetch_array($res))) {
	$list[] = $row['itime'];
}
rg_sql_free_result($res);
rg_log_ml('list=' . print_r($list, TRUE));

$r = totp_scratch_delete($list);
if ($r['ok'] !== 1)
	exit(1);
$sql = "SELECT DISTINCT itime FROM scratch_codes WHERE uid = " . $rg_ui['uid'];
$res = rg_sql_query($db, $sql);
$rows = rg_sql_num_rows($res);
rg_sql_free_result($res);
if ($rows != 0) {
	rg_log("Cannot delete scratch codes - sql still returns data!");
	exit(1);
}
$key = 'user::' . $rg_ui['uid'] . '::login_tokens::sc';
rg_cache_core_unset($key); // else we will get data from local mem!
$r = rg_cache_get($key);
if (count($r) != 0) {
	rg_log_ml('cache: ' . print_r($r, TRUE));
	rg_log('Deleted scratch codes are still in cache!');
	exit(1);
}
rg_log_exit();


rg_log('');
rg_log_enter('Testing the deletion of a device');
$key = 'ACHCBCCVQ7AK4RGM';
$r = rg_totp_enroll($db, $rg_ui['uid'], 'test1', $key, 'A', 't');
if ($r !== TRUE) {
	rg_log('cannot enroll!');
	exit(1);
}
$key = 'ACHCBCCVQ7AK4RGX';
$r = rg_totp_enroll($db, $rg_ui['uid'], 'test2', $key, 'A', 't');
if ($r !== TRUE) {
	rg_log('cannot enroll!');
	exit(1);
}
rg_log('Getting ids from database...');
$sql = "SELECT id FROM login_tokens WHERE uid = " . $rg_ui['uid'];
$res = rg_sql_query($db, $sql);
$list = array();
while (($row = rg_sql_fetch_array($res))) {
	$list[] = $row['id'];
}
rg_sql_free_result($res);
if (count($list) < 2) {
	rg_log_ml('list: ' . print_r($list, TRUE));
	rg_log('I cannot find the enrollments in database!');
	exit(1);
}
rg_log('Loading list devices form...');
$data = array();
$r = do_req($test_url . "/op/settings/totp/list", $data, $headers);
if ($r === FALSE) {
	rg_log("Cannot load list devices form.");
	exit(1);
}
$good_token = $r['tokens']['login_tokens_list'];
$data = array( 'delete' => 1, 'token' => $good_token);
foreach ($list as $id)
	$data['delete_list[' . $id . ']'] = 'on';
$headers = array();
$r = do_req($test_url . "/op/settings/totp/list", $data, $headers);
if (!strstr($r['body'], 'success')) {
	rg_log("Cannot delete login tokens!");
	exit(1);
}
$sql = "SELECT DISTINCT id FROM login_tokens WHERE uid = " . $rg_ui['uid'];
$res = rg_sql_query($db, $sql);
$rows = rg_sql_num_rows($res);
rg_sql_free_result($res);
if ($rows != 0) {
	rg_log("Cannot delete device codes - sql still returns data!");
	exit(1);
}
$key = 'user::' . $rg_ui['uid'] . '::login_tokens::device';
rg_cache_core_unset($key); // else we will get data from local mem!
$r = rg_cache_get($key);
if (count($r) != 0) {
	rg_log_ml($key . ': ' . print_r($r, TRUE));
	rg_log('Deleted device codes are still in cache!');
	exit(1);
}
rg_log_exit();


rg_prof_log();
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