<?php
//
// Will test the login by TOTP
//
error_reporting(E_ALL | E_STRICT);
ini_set("track_errors", "On");
$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");
$rg_sql = "host=localhost user=rocketgit dbname=rocketgit connect_timeout=10";
$rg_no_db = TRUE;
require_once("common.php");
$_testns = 'http_totp';
$rg_cache_enable = TRUE;
$rg_cache_debug = TRUE;
$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!");
?>