OTP : 2FA Authentication
Improve : tighten the security + cosmetic set double authentication : create, send link, usable with Google Authenticator and FreeOTP 1. OTP : prevent direct access without going throught the 2FA mechanism 2. Go back to the url where you were disconnected 3. Cosmetic
This commit is contained in:
parent
4bb0a46e6b
commit
4cee79c1f2
31 changed files with 1867 additions and 270 deletions
|
|
@ -36,6 +36,12 @@ $cn=Dossier::connect();
|
||||||
$g_parameter=new Noalyss_Parameter_Folder($cn);
|
$g_parameter=new Noalyss_Parameter_Folder($cn);
|
||||||
$g_user=new Noalyss_user($cn);
|
$g_user=new Noalyss_user($cn);
|
||||||
$g_user->check(true);
|
$g_user->check(true);
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
IDate::set_firstDate($g_user->get_first_week_day());
|
IDate::set_firstDate($g_user->get_first_week_day());
|
||||||
ITva_Popup::set_vat_code($g_user->get_vat_code_preference());
|
ITva_Popup::set_vat_code($g_user->get_vat_code_preference());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,12 @@ else
|
||||||
$g_user = new Noalyss_user($cn);
|
$g_user = new Noalyss_user($cn);
|
||||||
$g_user->check(true);
|
$g_user->check(true);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
IDate::set_firstDate($g_user->get_first_week_day());
|
IDate::set_firstDate($g_user->get_first_week_day());
|
||||||
ITva_Popup::set_vat_code($g_user->get_vat_code_preference());
|
ITva_Popup::set_vat_code($g_user->get_vat_code_preference());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,12 @@ $q=noalyss_str_replace(",",".",$q);
|
||||||
$cn=Dossier::connect();
|
$cn=Dossier::connect();
|
||||||
$User=new Noalyss_user($cn);
|
$User=new Noalyss_user($cn);
|
||||||
$User->Check();
|
$User->Check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $User->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$User->check_dossier(Dossier::id());
|
$User->check_dossier(Dossier::id());
|
||||||
|
|
||||||
// Retrieve the rate of vat, it $t == -1 it means no VAT
|
// Retrieve the rate of vat, it $t == -1 it means no VAT
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,12 @@ global $g_user;
|
||||||
$http=new \HttpInput();
|
$http=new \HttpInput();
|
||||||
$g_user=new Noalyss_user($cn);
|
$g_user=new Noalyss_user($cn);
|
||||||
$g_user->Check();
|
$g_user->Check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$g_user->check_dossier($http->get('gDossier'));
|
$g_user->check_dossier($http->get('gDossier'));
|
||||||
$res=$cn->exec_sql("select distinct code,coalesce(description,code) description from get_profile_menu($1) where code ~* $2 or description ~* $2 order by code limit 5 ",array($g_user->get_profile(),$http->post("acs")));
|
$res=$cn->exec_sql("select distinct code,coalesce(description,code) description from get_profile_menu($1) where code ~* $2 or description ~* $2 order by code limit 5 ",array($g_user->get_profile(),$http->post("acs")));
|
||||||
$nb=Database::num_row($res);
|
$nb=Database::num_row($res);
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,13 @@ if ( DEBUGNOALYSS > 1 ) {
|
||||||
$g_parameter=new Noalyss_Parameter_Folder($cn);
|
$g_parameter=new Noalyss_Parameter_Folder($cn);
|
||||||
|
|
||||||
$g_user->Check();
|
$g_user->Check();
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
echo "<h2>"._('Vous êtes déconnecté')."</h2>";
|
||||||
|
$backurl=$_SERVER['REQUEST_URI'];
|
||||||
|
$url="index.php?".http_build_query(array('reconnect'=>1,'backurl'=>urlencode($backurl)));
|
||||||
|
redirect($url);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$g_user->check_dossier(Dossier::id());
|
$g_user->check_dossier(Dossier::id());
|
||||||
load_all_script();
|
load_all_script();
|
||||||
/* Check Browser version if < IE6 then unsupported */
|
/* Check Browser version if < IE6 then unsupported */
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,12 @@ $gDossier=dossier::id();
|
||||||
$g_parameter=new Noalyss_Parameter_Folder($cn);
|
$g_parameter=new Noalyss_Parameter_Folder($cn);
|
||||||
mb_internal_encoding("UTF-8");
|
mb_internal_encoding("UTF-8");
|
||||||
$g_user->Check();
|
$g_user->Check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$action=$g_user->check_dossier($gDossier);
|
$action=$g_user->check_dossier($gDossier);
|
||||||
|
|
||||||
$hi=new HttpInput();
|
$hi=new HttpInput();
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,12 @@ $http=new HttpInput();
|
||||||
$cn=Dossier::connect();
|
$cn=Dossier::connect();
|
||||||
$g_user=new Noalyss_user($cn);
|
$g_user=new Noalyss_user($cn);
|
||||||
$g_user->check();
|
$g_user->check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$only_plugin=$g_user->check_dossier(dossier::id());
|
$only_plugin=$g_user->check_dossier(dossier::id());
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,12 @@ $cn=Dossier::connect();
|
||||||
global $g_user;
|
global $g_user;
|
||||||
$g_user=new Noalyss_User($cn);
|
$g_user=new Noalyss_User($cn);
|
||||||
$g_user->check();
|
$g_user->check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$g_user->check_dossier(dossier::id());
|
$g_user->check_dossier(dossier::id());
|
||||||
set_language();
|
set_language();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,12 @@ require_once('class/noalyss_user.class.php');
|
||||||
global $g_user;
|
global $g_user;
|
||||||
$g_user=new Noalyss_user($cn);
|
$g_user=new Noalyss_user($cn);
|
||||||
$g_user->check();
|
$g_user->check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$g_user->check_dossier(dossier::id());
|
$g_user->check_dossier(dossier::id());
|
||||||
set_language();
|
set_language();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -211,11 +211,21 @@ if (strlen(domaine)>0)
|
||||||
{
|
{
|
||||||
$my_domain=sprintf(_("Domaine")." : %s", domaine);
|
$my_domain=sprintf(_("Domaine")." : %s", domaine);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* if request to recover password
|
||||||
|
*/
|
||||||
if (defined("RECOVER")&&isset($_REQUEST['recover']))
|
if (defined("RECOVER")&&isset($_REQUEST['recover']))
|
||||||
{
|
{
|
||||||
require_once '../include/recover.php';
|
require_once '../include/recover.php';
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* if request to see OTP qrcode
|
||||||
|
*/
|
||||||
|
if ( isset($_GET['otp'])){
|
||||||
|
|
||||||
|
require_once '../include/otp-link.php';
|
||||||
|
return;
|
||||||
|
}
|
||||||
// reconnect , create a variable to reconnect properly in login.php
|
// reconnect , create a variable to reconnect properly in login.php
|
||||||
$goto="";
|
$goto="";
|
||||||
if (isset($_REQUEST['reconnect'])&&isset($_REQUEST['backurl']))
|
if (isset($_REQUEST['reconnect'])&&isset($_REQUEST['backurl']))
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
104
html/login.php
104
html/login.php
|
|
@ -72,9 +72,34 @@ if ( isset ($_POST["p_user"] ) )
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if auth method = 1 send an email with number but only for PC access
|
||||||
|
if ($User->get_access_mode() =='PC' && $User->get_authent_method() == 1) {
|
||||||
|
// send an email and get the uuid of the request
|
||||||
|
$uuid=$User->send_code_otp ();
|
||||||
|
|
||||||
|
// var $backurl (string url) url before being disconnected
|
||||||
|
$backurl=(isset($_POST['backurl']))?$_POST['backurl']:"";
|
||||||
|
|
||||||
|
// display form to enter digit from email
|
||||||
|
$User->input_otp($uuid,$backurl);
|
||||||
|
// end
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if auth method = 2 , only for PC access ask code from freeOTP
|
||||||
|
if ($User->get_access_mode() =='PC' && $User->get_authent_method() ==2 ) {
|
||||||
|
// var $backurl (string url) url before being disconnected
|
||||||
|
$backurl=(isset($_POST['backurl']))?$_POST['backurl']:"";
|
||||||
|
// display form to enter digit from email
|
||||||
|
$User->input_otp(url:$backurl);
|
||||||
|
// end
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($User->get_access_mode()=='PC')
|
if ($User->get_access_mode()=='PC')
|
||||||
{
|
{
|
||||||
// force the nocache
|
// retrieve the previous locationforce the nocache
|
||||||
$backurl=NOALYSS_URL.'/user_login.php?v='.microtime(true);
|
$backurl=NOALYSS_URL.'/user_login.php?v='.microtime(true);
|
||||||
if ( isset ($_POST['backurl'])) {
|
if ( isset ($_POST['backurl'])) {
|
||||||
$backurl=urldecode($_POST['backurl']);
|
$backurl=urldecode($_POST['backurl']);
|
||||||
|
|
@ -107,8 +132,83 @@ else
|
||||||
exit();
|
exit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$User=new Noalyss_user($rep);
|
$User=new Noalyss_user($rep);
|
||||||
|
/**
|
||||||
|
* OTP is asked and authentication method is via OTP
|
||||||
|
*/
|
||||||
|
if (isset($_POST['to_validate']) || $User->get_authent_method() != 0) {
|
||||||
|
|
||||||
|
// remove also old one
|
||||||
|
$rep->exec_sql("delete from otp_send_secret where os_valid_time < now()");
|
||||||
|
try {
|
||||||
|
$request = $http->post("rq", "string", "");
|
||||||
|
$vrf_code = $http->post("vrf_code");
|
||||||
|
|
||||||
|
// if code was sent by email
|
||||||
|
if ($request != "") {
|
||||||
|
// find the row concerning this request
|
||||||
|
$os_id = $rep->get_value("select os_id from otp_send_secret
|
||||||
|
where os_request=$1
|
||||||
|
and use_id=$2
|
||||||
|
",
|
||||||
|
[$request, $User->id]);
|
||||||
|
|
||||||
|
if ($os_id == "") {
|
||||||
|
echo "Désolé, votre code a expiré";
|
||||||
|
echo "<META HTTP-EQUIV=\"REFRESH\" content=\"0;url=index.php?v=".microtime(true)."\">";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$otp_send_secret = new Otp_Send_Secret_SQL($rep, $os_id);
|
||||||
|
if (
|
||||||
|
$vrf_code == $otp_send_secret->get('os_code')
|
||||||
|
|| $User->check_otp($vrf_code)
|
||||||
|
) {
|
||||||
|
$User->set_identified();
|
||||||
|
// var $backurl (string url) url before being disconnected
|
||||||
|
$backurl=NOALYSS_URL.'/user_login.php?v='.microtime(true);
|
||||||
|
if ( isset ($_POST['backurl'])) {
|
||||||
|
$backurl=urldecode($_POST['backurl']);
|
||||||
|
// check that backurl is valid
|
||||||
|
$backurl=preg_replace('/^.*\?/','',$backurl);
|
||||||
|
$backurl=NOALYSS_URL."/do.php?$backurl";
|
||||||
|
}
|
||||||
|
header("Location: $backurl");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// var $backurl (string url) url before being disconnected
|
||||||
|
$backurl=(isset($_POST['backurl']))?$_POST['backurl']:"";
|
||||||
|
$User->input_otp(uuid:$request,url:$backurl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// connection avec freeOTP / Google Authenticator
|
||||||
|
if ($User->check_otp($vrf_code) == true) {
|
||||||
|
$User->set_identified();
|
||||||
|
// var $backurl (string url) url before being disconnected
|
||||||
|
$backurl=NOALYSS_URL.'/user_login.php?v='.microtime(true);
|
||||||
|
if ( isset ($_POST['backurl'])) {
|
||||||
|
// var $backurl (string url) url before being disconnected
|
||||||
|
$backurl=urldecode($_POST['backurl']);
|
||||||
|
// check that backurl is valid
|
||||||
|
|
||||||
|
$backurl=preg_replace('/^.*\?/','',$backurl);
|
||||||
|
$backurl=NOALYSS_URL."/do.php?$backurl";
|
||||||
|
}
|
||||||
|
header("Location: $backurl");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// var $backurl (string url) url before being disconnected
|
||||||
|
$backurl=NOALYSS_URL.'/user_login.php?v='.microtime(true);
|
||||||
|
$User->input_otp(url:$backurl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception $exc) {
|
||||||
|
|
||||||
|
record_log($exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
$User->Check();
|
$User->Check();
|
||||||
|
|
||||||
echo "<META HTTP-EQUIV=\"REFRESH\" content=\"0;url=user_login.php?v=".microtime(true)."\">";
|
echo "<META HTTP-EQUIV=\"REFRESH\" content=\"0;url=user_login.php?v=".microtime(true)."\">";
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,12 @@ global $g_user;
|
||||||
$cn=Dossier::connect();
|
$cn=Dossier::connect();
|
||||||
$g_user=new Noalyss_user($cn);
|
$g_user=new Noalyss_user($cn);
|
||||||
$g_user->Check();
|
$g_user->Check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$g_user->check_dossier(Dossier::id());
|
$g_user->check_dossier(Dossier::id());
|
||||||
|
|
||||||
if ( basename($_GET['op']) == 'history' )
|
if ( basename($_GET['op']) == 'history' )
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,12 @@ include_once NOALYSS_INCLUDE . '/class/noalyss_user.class.php';
|
||||||
global $g_user;
|
global $g_user;
|
||||||
$g_user=new Noalyss_user($cn);
|
$g_user=new Noalyss_user($cn);
|
||||||
$g_user->Check();
|
$g_user->Check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $g_user->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$act=$g_user->check_dossier($gDossier);
|
$act=$g_user->check_dossier($gDossier);
|
||||||
// AC CODE = SEARCH
|
// AC CODE = SEARCH
|
||||||
if ($act =='P')
|
if ($act =='P')
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ require_once NOALYSS_INCLUDE . '/class/noalyss_user.class.php';
|
||||||
$User=new Noalyss_user($rep);
|
$User=new Noalyss_user($rep);
|
||||||
|
|
||||||
$User->Check();
|
$User->Check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $User->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
/* Check Browser version if < IE6 then unsupported */
|
/* Check Browser version if < IE6 then unsupported */
|
||||||
$browser=$_SERVER['HTTP_USER_AGENT'];
|
$browser=$_SERVER['HTTP_USER_AGENT'];
|
||||||
if ( strpos($browser,'MSIE 6')!=false ||
|
if ( strpos($browser,'MSIE 6')!=false ||
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,12 @@ $action = $http->request("action","string", "");
|
||||||
$rep=new Database();
|
$rep=new Database();
|
||||||
$User=new Noalyss_user($rep);
|
$User=new Noalyss_user($rep);
|
||||||
$User->Check();
|
$User->Check();
|
||||||
|
/**
|
||||||
|
* check if 2FA is completed
|
||||||
|
*/
|
||||||
|
if ( ! $User->is_double_identified()) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
$audit=true;
|
$audit=true;
|
||||||
|
|
||||||
if ($User->admin != 1)
|
if ($User->admin != 1)
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,8 @@ class Dossier
|
||||||
use_login,
|
use_login,
|
||||||
use_active,
|
use_active,
|
||||||
use_admin,
|
use_admin,
|
||||||
ag_dossier
|
ag_dossier,
|
||||||
|
use_auth_method
|
||||||
from
|
from
|
||||||
ac_users as ac
|
ac_users as ac
|
||||||
left join
|
left join
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
use chillerlan\Authenticator\{Authenticator, AuthenticatorOptions};
|
||||||
|
use chillerlan\Authenticator\Authenticators\AuthenticatorInterface;
|
||||||
/*
|
/*
|
||||||
* This file is part of NOALYSS.
|
* This file is part of NOALYSS.
|
||||||
*
|
*
|
||||||
|
|
@ -48,7 +49,8 @@ class Noalyss_User
|
||||||
var $access_mode; //!< MOBILE or PC depending if when connecting $login contains @mobile
|
var $access_mode; //!< MOBILE or PC depending if when connecting $login contains @mobile
|
||||||
var $lang ; //!< user's language
|
var $lang ; //!< user's language
|
||||||
var $theme ; //!< user's CSS Theme
|
var $theme ; //!< user's CSS Theme
|
||||||
|
var $authent_method; //!< authentication method use for this user
|
||||||
|
private $otp_secret; //!< string use as secret for OTP
|
||||||
/**
|
/**
|
||||||
* @brief Create an user , load an existing one or if p_id == -1 search for the connected user. To have an empty
|
* @brief Create an user , load an existing one or if p_id == -1 search for the connected user. To have an empty
|
||||||
* user, give a p_id smaller than -1 or zero.
|
* user, give a p_id smaller than -1 or zero.
|
||||||
|
|
@ -377,7 +379,9 @@ class Noalyss_User
|
||||||
use_active,
|
use_active,
|
||||||
use_admin,
|
use_admin,
|
||||||
use_pass,
|
use_pass,
|
||||||
use_email
|
use_email,
|
||||||
|
use_auth_method,
|
||||||
|
use_otp_secret
|
||||||
from ac_users ";
|
from ac_users ";
|
||||||
$cn=new Database();
|
$cn=new Database();
|
||||||
$Res=$cn->exec_sql($sql.$sql_cond, $sql_array);
|
$Res=$cn->exec_sql($sql.$sql_cond, $sql_array);
|
||||||
|
|
@ -393,18 +397,32 @@ class Noalyss_User
|
||||||
$this->admin=$row['use_admin'];
|
$this->admin=$row['use_admin'];
|
||||||
$this->password=$row['use_pass'];
|
$this->password=$row['use_pass'];
|
||||||
$this->email=$row['use_email'];
|
$this->email=$row['use_email'];
|
||||||
|
$this->authent_method=$row['use_auth_method'];
|
||||||
|
$this->otp_secret=$row['use_otp_secret'];
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save()
|
function save()
|
||||||
{
|
{
|
||||||
|
if ( $this->authent_method != 0 && $this->otp_secret == null) {
|
||||||
|
$this->generate_otp();
|
||||||
|
}
|
||||||
$Sql="update ac_users set use_first_name=$1, use_name=$2
|
$Sql="update ac_users set use_first_name=$1, use_name=$2
|
||||||
,use_active=$3,use_admin=$4,use_pass=$5 ,use_email = $7 where use_id=$6";
|
,use_active=$3,use_admin=$4,use_pass=$5 ,use_email = $7
|
||||||
|
, use_auth_method=$8,use_otp_secret=$9
|
||||||
|
where use_id=$6";
|
||||||
$cn=new Database();
|
$cn=new Database();
|
||||||
$Res=$cn->exec_sql($Sql,
|
$Res=$cn->exec_sql($Sql,
|
||||||
array($this->first_name, $this->last_name, $this->active, $this->admin, $this->password,
|
array($this->first_name //1
|
||||||
$this->id, $this->email));
|
, $this->last_name // 2
|
||||||
|
, $this->active //3
|
||||||
|
, $this->admin //4
|
||||||
|
, $this->password //5
|
||||||
|
, $this->id //6
|
||||||
|
, $this->email //7
|
||||||
|
, $this->authent_method //8
|
||||||
|
, $this->otp_secret //9
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert()
|
function insert()
|
||||||
|
|
@ -1867,6 +1885,185 @@ class Noalyss_User
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @brief generate OTP
|
||||||
|
*/
|
||||||
|
function generate_otp()
|
||||||
|
{
|
||||||
|
$otp=new \Noalyss\OTP();
|
||||||
|
$this->otp_secret=$otp->build_secret();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_authent_method() {
|
||||||
|
return $this->authent_method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_otp_secret() {
|
||||||
|
return $this->otp_secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function set_authent_method($authent_method) {
|
||||||
|
$this->authent_method = $authent_method;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_otp_secret($otp_secret) {
|
||||||
|
$this->otp_secret = $otp_secret;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function set_identified()
|
||||||
|
{
|
||||||
|
$_SESSION[SESSION_KEY."db_auth"]='ok';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief check is the double authentication has been successful
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function is_double_identified() {
|
||||||
|
|
||||||
|
if ( $this->authent_method == 0 )
|
||||||
|
{
|
||||||
|
$_SESSION[SESSION_KEY."db_auth"]='ok';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( ! isset($_SESSION[SESSION_KEY."db_auth"])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($_SESSION[SESSION_KEY."db_auth"] == "ok") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief send a code to the user and save the expected code + UUID in
|
||||||
|
* a table OTP_SEND_SECRET
|
||||||
|
* @return bool false if fails true if succeeds
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function send_code_otp()
|
||||||
|
{
|
||||||
|
if ( $this->authent_method !=1 ) { return false; }
|
||||||
|
$mail=new \Sendmail();
|
||||||
|
$mail->set_from(ADMIN_WEB);
|
||||||
|
$mail->mailto($this->getEmail());
|
||||||
|
$mail->set_subject(_("NOALYSS : votre code secret "));
|
||||||
|
$noalyss_url=NOALYSS_URL;
|
||||||
|
|
||||||
|
|
||||||
|
if ( strlen(trim($this->otp_secret??"")) == 0 ) {
|
||||||
|
throw new \Exception("noalyss_user.send_code_otp:secret empty",1945);
|
||||||
|
}
|
||||||
|
$otp=new \Noalyss\OTP();
|
||||||
|
$code=$otp->compute_code($this->otp_secret);
|
||||||
|
$message="Bonjour,
|
||||||
|
|
||||||
|
Voici votre code secret pour NOALYSS : $code
|
||||||
|
|
||||||
|
|
||||||
|
";
|
||||||
|
try {
|
||||||
|
$uuid= guidv4();
|
||||||
|
$repository=new \Database();
|
||||||
|
// remove old for this user
|
||||||
|
$repository->exec_sql("delete from otp_send_secret where use_id=$1"
|
||||||
|
,[$this->id]);
|
||||||
|
// remove also old one
|
||||||
|
$repository->exec_sql("delete from otp_send_secret where os_valid_time < now()");
|
||||||
|
$now=new \DateTime();
|
||||||
|
$valid=new \DateTime();
|
||||||
|
$valid->modify('+20 minutes');
|
||||||
|
|
||||||
|
$otp_send_secret=new Otp_Send_Secret_SQL($repository);
|
||||||
|
$otp_send_secret->set("use_id",$this->id)
|
||||||
|
->set('os_request',$uuid)
|
||||||
|
->set("os_code",$code)
|
||||||
|
->set('os_valid_time',$valid->format('d.m.Y H:i:s'));
|
||||||
|
$otp_send_secret->save();
|
||||||
|
$mail->set_message($message);
|
||||||
|
$mail->compose();
|
||||||
|
$mail->send();
|
||||||
|
return $uuid;
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
\record_log ($ex);
|
||||||
|
throw new \Exception("noalyss_user.send_code_otp",1963,$ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief send an email with link to the user
|
||||||
|
*/
|
||||||
|
function send_link_otp() {
|
||||||
|
$mail = new \Sendmail();
|
||||||
|
$mail->set_from(ADMIN_WEB);
|
||||||
|
$mail->mailto($this->getEmail());
|
||||||
|
$mail->set_subject(_("NOALYSS : Double authentification lien pour 2FA: OTP"));
|
||||||
|
$noalyss_url = NOALYSS_URL;
|
||||||
|
$uuid = guidv4();
|
||||||
|
$id = $this->getId();
|
||||||
|
/**
|
||||||
|
* save in DB first
|
||||||
|
*/
|
||||||
|
$message = "Bonjour,
|
||||||
|
|
||||||
|
Afin de pouvoir utiliser la double authentification avec 2FA: OTP, pourriez-vous
|
||||||
|
suivre ce lien et scanner le QRCode avec votre application android freeOTP ou Google Authenticator.
|
||||||
|
|
||||||
|
Ce lien ne sera actif que 12 heures.
|
||||||
|
|
||||||
|
|
||||||
|
{$noalyss_url}/index.php?otp={$uuid}
|
||||||
|
|
||||||
|
Merci d'utiliser NOALYSS
|
||||||
|
|
||||||
|
Bien cordialement,
|
||||||
|
|
||||||
|
|
||||||
|
";
|
||||||
|
try {
|
||||||
|
$repository = new \Database();
|
||||||
|
// remove old for this user
|
||||||
|
$repository->exec_sql("delete from otp_send_secret where use_id=$1"
|
||||||
|
,[$this->id]);
|
||||||
|
// remove also old one
|
||||||
|
$repository->exec_sql("delete from otp_send_secret where os_valid_time < now()");
|
||||||
|
$valid_time=new \DateTime();
|
||||||
|
$valid_time->add(new \DateInterval('PT12H'));
|
||||||
|
$otp_send_secret_sql = new \Otp_Send_Secret_SQL($repository);
|
||||||
|
$otp_send_secret_sql->set('use_id', $id)
|
||||||
|
->set('os_valid_time',$valid_time->format('d-m-Y H:i'))
|
||||||
|
->set('os_request', $uuid);
|
||||||
|
|
||||||
|
$otp_send_secret_sql->save();
|
||||||
|
$mail->set_message($message);
|
||||||
|
$mail->compose();
|
||||||
|
$mail->send();
|
||||||
|
return $uuid;
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
\record_log($ex);
|
||||||
|
throw new \Exception("noalyss_user.send_link_otp",1998,$ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief FORM to enter the 6 digit enter by OTP
|
||||||
|
* @param $uuid (string UUID) UUID in the message, null if there is no message
|
||||||
|
*/
|
||||||
|
function input_otp($uuid="",$url="")
|
||||||
|
{
|
||||||
|
require_once NOALYSS_TEMPLATE."/noalyss_user-input_otp.php" ;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief check that the OTP code is the one on smartphone
|
||||||
|
* returns false if the given does not match the OTP
|
||||||
|
*/
|
||||||
|
function check_otp($code)
|
||||||
|
{
|
||||||
|
$otp=new \Noalyss\OTP();
|
||||||
|
|
||||||
|
if ( $otp->compute_code($this->otp_secret) == $code ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
||||||
|
|
@ -380,14 +380,13 @@ define ("VATCHECK_URL","https://ec.europa.eu/taxation_customs/vies/rest-api/");
|
||||||
function noalyss_class_autoloader($class)
|
function noalyss_class_autoloader($class)
|
||||||
{
|
{
|
||||||
$class = strtolower($class);
|
$class = strtolower($class);
|
||||||
|
|
||||||
foreach (array("class","lib","database") as $path) {
|
foreach (array("class","lib","database") as $path) {
|
||||||
if ( file_exists(NOALYSS_INCLUDE.'/'.$path.'/'.$class.'.class.php')) {
|
if ( file_exists(NOALYSS_INCLUDE.'/'.$path.'/'.$class.'.class.php')) {
|
||||||
require_once NOALYSS_INCLUDE.'/'.$path.'/'.$class.'.class.php';
|
require_once NOALYSS_INCLUDE.'/'.$path.'/'.$class.'.class.php';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$aClass = array(
|
$aClass = array(
|
||||||
"database" => "class/database.class.php",
|
"database" => "class/database.class.php",
|
||||||
"acc_detail" => "class/acc_operation.class.php",
|
"acc_detail" => "class/acc_operation.class.php",
|
||||||
|
|
@ -410,7 +409,8 @@ function noalyss_class_autoloader($class)
|
||||||
'noalyss\dbg'=>"lib/dbg.php",
|
'noalyss\dbg'=>"lib/dbg.php",
|
||||||
'noalyss\file_cache'=>"lib/file_cache.class.php",
|
'noalyss\file_cache'=>"lib/file_cache.class.php",
|
||||||
"pdfland"=>"class/pdf_land.class.php",
|
"pdfland"=>"class/pdf_land.class.php",
|
||||||
"noalyss\widget\widget"=>"widget/widget.php"
|
"noalyss\widget\widget"=>"widget/widget.php",
|
||||||
|
"noalyss\otp"=>"lib/otp.class.php"
|
||||||
);
|
);
|
||||||
if (isset ($aClass[$class])) {
|
if (isset ($aClass[$class])) {
|
||||||
require_once NOALYSS_INCLUDE . "/" . $aClass[$class];
|
require_once NOALYSS_INCLUDE . "/" . $aClass[$class];
|
||||||
|
|
@ -419,3 +419,5 @@ function noalyss_class_autoloader($class)
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_autoload_register('\noalyss_class_autoloader', true);
|
spl_autoload_register('\noalyss_class_autoloader', true);
|
||||||
|
|
||||||
|
require_once NOALYSS_BASE.'/vendor/autoload.php';
|
||||||
|
|
@ -40,9 +40,11 @@ class Otp_Send_Secret_SQL extends \Table_Data_SQL {
|
||||||
*/
|
*/
|
||||||
$this->name = array(
|
$this->name = array(
|
||||||
"os_id" => "os_id"
|
"os_id" => "os_id"
|
||||||
, "use_email" => "use_email"
|
|
||||||
, "os_timestamp" => "os_timestamp"
|
, "os_timestamp" => "os_timestamp"
|
||||||
, "os_request" => "os_request"
|
, "os_request" => "os_request"
|
||||||
|
, "use_id" => "use_id"
|
||||||
|
, 'os_valid_time'=>'os_valid_time'
|
||||||
|
,'os_code'=>'os_code'
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -50,12 +52,14 @@ class Otp_Send_Secret_SQL extends \Table_Data_SQL {
|
||||||
*/
|
*/
|
||||||
$this->type = array(
|
$this->type = array(
|
||||||
"os_id" => "numeric"
|
"os_id" => "numeric"
|
||||||
, "use_email" => "text"
|
|
||||||
, "os_timestamp" => "date"
|
, "os_timestamp" => "date"
|
||||||
, "os_request" => "text"
|
, "os_request" => "text"
|
||||||
|
, "use_id" => "numeric"
|
||||||
|
, 'os_valid_time'=>'date'
|
||||||
|
,'os_code'=>'text'
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->default = array("os_id" => "auto");
|
$this->default = array("os_id" => "auto", "os_timestamp" => "auto");
|
||||||
|
|
||||||
$this->date_format = "DD.MM.YYYY HH24:MI:SS";
|
$this->date_format = "DD.MM.YYYY HH24:MI:SS";
|
||||||
parent::__construct($p_cn, $p_id);
|
parent::__construct($p_cn, $p_id);
|
||||||
|
|
|
||||||
|
|
@ -1502,9 +1502,9 @@ function generate_random_string($p_length,$special=1)
|
||||||
*/
|
*/
|
||||||
function confirm_with_string($p_ctl_name,$p_car)
|
function confirm_with_string($p_ctl_name,$p_car)
|
||||||
{
|
{
|
||||||
$code=generate_random_string($p_car );
|
$code=generate_random_string($p_car ,0);
|
||||||
$r = HtmlInput::hidden("ctlcode",$code);
|
$r = HtmlInput::hidden("ctlcode",$code);
|
||||||
$r.='<span style="margin-left:1.2em;margin-right:1.2em;font-size:112%;font-weight:bold;border:navy solid 1px ; padding:0.5rem">'. $code.'</span>';
|
$r.='<span style="margin-left:1.2em;margin-right:1.2em;font-size:120%;font-weight:bold;border:navy solid 1px ; padding:0.5rem">'. $code.'</span>';
|
||||||
$ctl=new IText($p_ctl_name);
|
$ctl=new IText($p_ctl_name);
|
||||||
$r.=$ctl->input();
|
$r.=$ctl->input();
|
||||||
return $r;
|
return $r;
|
||||||
|
|
@ -1860,3 +1860,21 @@ function sanitize_filename($filename)
|
||||||
$new_filename=strtolower($filename_no)."-".date("Ymd-Hi").$filename_suff;
|
$new_filename=strtolower($filename_no)."-".date("Ymd-Hi").$filename_suff;
|
||||||
return $new_filename;
|
return $new_filename;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @brief generate an UUID
|
||||||
|
* @param $data(string) if null use randow
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function guidv4($data = null) {
|
||||||
|
// Generate 16 bytes (128 bits) of random data or use the data passed into the function.
|
||||||
|
$data = $data ?? random_bytes(16);
|
||||||
|
|
||||||
|
|
||||||
|
// Set version to 0100
|
||||||
|
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||||
|
// Set bits 6-7 to 10
|
||||||
|
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||||
|
|
||||||
|
// Output the 36 character UUID.
|
||||||
|
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -496,10 +496,11 @@ class HtmlInput
|
||||||
* @param type $div_name
|
* @param type $div_name
|
||||||
* @return type
|
* @return type
|
||||||
*/
|
*/
|
||||||
static function button_hide($div_name)
|
static function button_hide($div_name,$class='smallbutton')
|
||||||
{
|
{
|
||||||
$a=new IButton('Fermer');
|
$a=new IButton('Fermer');
|
||||||
$a->label=_("Fermer");
|
$a->label=_("Fermer");
|
||||||
|
$a->class=$class;
|
||||||
$a->javascript="$('".$div_name."').hide()";
|
$a->javascript="$('".$div_name."').hide()";
|
||||||
$html=$a->input();
|
$html=$a->input();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,65 +39,35 @@ use chillerlan\Authenticator\Authenticators\AuthenticatorInterface;
|
||||||
|
|
||||||
class OTP {
|
class OTP {
|
||||||
|
|
||||||
|
private $authenticator;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
$options = new AuthenticatorOptions;
|
||||||
|
$options->secret_length = 32;
|
||||||
|
$options->algorithm = AuthenticatorInterface::ALGO_SHA512;
|
||||||
|
$options->digits=6;
|
||||||
|
$this->authenticator = new Authenticator($options);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief build a secret key and returns it
|
* @brief build a secret key and returns it
|
||||||
* @return string random string of 32
|
* @return string random string of 32
|
||||||
*/
|
*/
|
||||||
function build_secret() {
|
function build_secret() {
|
||||||
$options = new AuthenticatorOptions;
|
|
||||||
$options->secret_length = 32;
|
$secret = $this->authenticator->createSecret();
|
||||||
$options->algorithm = AuthenticatorInterface::ALGO_SHA512;
|
|
||||||
$options->digits=6;
|
|
||||||
$authenticator = new Authenticator($options);
|
|
||||||
// create a secret (stored somewhere in a *safe* place on the server. safe... hahaha jk)
|
|
||||||
$secret = $authenticator->createSecret();
|
|
||||||
return $secret;
|
return $secret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief send an email with link to the user
|
* @brief compute a code for auth. for the user passed in parameter
|
||||||
* @param $user (\Noalyss_User)
|
* @param $user (\Noalyss_User)
|
||||||
*/
|
*/
|
||||||
function send_mail(\Noalyss_User $user) {
|
function compute_code($secret)
|
||||||
$mail=new \Sendmail();
|
{
|
||||||
$mail->set_from(ADMIN_WEB);
|
|
||||||
$mail->mailto($user->getEmail());
|
|
||||||
$mail->set_subject(_("NOALYSS : Double authentification lien pour freeOTP"));
|
|
||||||
$noalyss_url=NOALYSS_URL;
|
|
||||||
$uuid=guidv4();
|
|
||||||
$id=$user->getId();
|
|
||||||
/**
|
|
||||||
* save in DB first
|
|
||||||
*/
|
|
||||||
$message="Bonjour,
|
|
||||||
|
|
||||||
Afin de pouvoir utiliser la double authentification avec freeOTP, pourriez-vous
|
$this->authenticator->setSecret($secret);
|
||||||
suivre ce lien et scanner le QRCode avec votre application android freeOTP.
|
return $this->authenticator->code();
|
||||||
|
|
||||||
Ce lien ne sera actif que 24 heures.
|
|
||||||
|
|
||||||
|
|
||||||
{$noalyss_url}/index.php?otp={$uuid}
|
|
||||||
|
|
||||||
Merci d'utiliser NOALYSS
|
|
||||||
|
|
||||||
Cordialement,
|
|
||||||
|
|
||||||
Noalyss team
|
|
||||||
";
|
|
||||||
try {
|
|
||||||
$repository=new \Database();
|
|
||||||
$otp_send_secret_sql=new \Otp_Send_Secret_SQL($repository);
|
|
||||||
$otp_send_secret_sql->set('use_id',$id)
|
|
||||||
->set('os_request',$uuid);
|
|
||||||
$otp_send_secret_sql->save();
|
|
||||||
$mail->set_message($message);
|
|
||||||
$mail->compose();
|
|
||||||
$mail->send();
|
|
||||||
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,97 @@ if (!file_exists($dirname . '/config.inc.php')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once __DIR__ . '/constant.php';
|
require_once __DIR__ . '/constant.php';
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
/***
|
||||||
|
* LOGO
|
||||||
|
*************************************/
|
||||||
|
#logo_id {
|
||||||
|
position:static;
|
||||||
|
top : 0px;
|
||||||
|
left:0px;
|
||||||
|
width:60px;
|
||||||
|
height:auto;
|
||||||
|
margin-left:10px;
|
||||||
|
}
|
||||||
|
/** small **/
|
||||||
|
@media (min-width : 576px) {
|
||||||
|
#logo_id {
|
||||||
|
top : 10px;
|
||||||
|
left:20px;
|
||||||
|
width: 90px;
|
||||||
|
margin-left:20px;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** medium**/
|
||||||
|
@media (min-width : 768px) {
|
||||||
|
#logo_id {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/** large**/
|
||||||
|
@media (min-width : 992px) {
|
||||||
|
#logo_id {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** XL **/
|
||||||
|
@media (min-width : 1200px) {
|
||||||
|
#logo_id {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
|
||||||
|
}
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* MeDium */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* LarGe */
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 64px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* eXtraLarge */
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#vrf_code {
|
||||||
|
font-size:200%;
|
||||||
|
width:9rem;
|
||||||
|
padding:0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]{
|
||||||
|
font-size:200%;
|
||||||
|
width:9rem;
|
||||||
|
padding:0.5rem;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
width:70%;
|
||||||
|
margin-left:15%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<img id="logo_id" src="image/logo9000.png" >
|
||||||
|
<div class="content">
|
||||||
|
<?php
|
||||||
use Endroid\QrCode\Color\Color;
|
use Endroid\QrCode\Color\Color;
|
||||||
use Endroid\QrCode\Encoding\Encoding;
|
use Endroid\QrCode\Encoding\Encoding;
|
||||||
use Endroid\QrCode\ErrorCorrectionLevel;
|
use Endroid\QrCode\ErrorCorrectionLevel;
|
||||||
|
|
@ -94,8 +184,20 @@ try {
|
||||||
$result = $writer->write($qrCode);
|
$result = $writer->write($qrCode);
|
||||||
echo '<div style="margin:4rem">';
|
echo '<div style="margin:4rem">';
|
||||||
// generate the QRCode
|
// generate the QRCode
|
||||||
echo '<h1>',_("Scanner ceci avec freeOTP"),'</h1>';
|
echo '<h1>',_("Scanner ceci avec votre application OTP"),'</h1>';
|
||||||
|
?>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<a href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp" target="_blank">FreeOTP (libre)</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://play.google.com/store/apps/details?id=com.beemdevelopment.aegis" target="_blank">AEgis Authenticator (libre)</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank">Google Authenticator</a>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<?php
|
||||||
echo '<p>';
|
echo '<p>';
|
||||||
echo _("Scanner ce QRCode avec votre application OTP afin de l'ajouter");
|
echo _("Scanner ce QRCode avec votre application OTP afin de l'ajouter");
|
||||||
|
|
||||||
|
|
@ -104,13 +206,12 @@ try {
|
||||||
|
|
||||||
printf('<img src="data:image/png;base64,%s">', base64_encode($result->getString()));
|
printf('<img src="data:image/png;base64,%s">', base64_encode($result->getString()));
|
||||||
|
|
||||||
echo '</div>';
|
|
||||||
} catch (Exception $exc) {
|
} catch (Exception $exc) {
|
||||||
record_log($e);
|
record_log($e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
155
include/template/noalyss_user-input_otp.php
Normal file
155
include/template/noalyss_user-input_otp.php
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of NOALYSS.
|
||||||
|
*
|
||||||
|
* NOALYSS is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* NOALYSS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with NOALYSS; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
// Copyright Author Dany De Bontridder danydb@aevalys.eu 22/10/23
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief input the 6 digit
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* var $this (Noalyss_User)
|
||||||
|
*/
|
||||||
|
global $g_user;
|
||||||
|
$repository = new \Database();
|
||||||
|
|
||||||
|
// $uuid is set only when a message with the code
|
||||||
|
$str="";
|
||||||
|
if ( $uuid != "")
|
||||||
|
{
|
||||||
|
$request=new IHidden("rq", $uuid);
|
||||||
|
$str=$request->input();
|
||||||
|
}
|
||||||
|
|
||||||
|
$date=new \DateTime();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
/***
|
||||||
|
* LOGO
|
||||||
|
*************************************/
|
||||||
|
#logo_id {
|
||||||
|
position:static;
|
||||||
|
top : 0px;
|
||||||
|
left:0px;
|
||||||
|
width:60px;
|
||||||
|
height:auto;
|
||||||
|
margin-left:10px;
|
||||||
|
}
|
||||||
|
/** small **/
|
||||||
|
@media (min-width : 576px) {
|
||||||
|
#logo_id {
|
||||||
|
top : 10px;
|
||||||
|
left:20px;
|
||||||
|
width: 90px;
|
||||||
|
margin-left:20px;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** medium**/
|
||||||
|
@media (min-width : 768px) {
|
||||||
|
#logo_id {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/** large**/
|
||||||
|
@media (min-width : 992px) {
|
||||||
|
#logo_id {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** XL **/
|
||||||
|
@media (min-width : 1200px) {
|
||||||
|
#logo_id {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
|
||||||
|
}
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* MeDium */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* LarGe */
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 64px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* eXtraLarge */
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
div.content {
|
||||||
|
margin-left: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#vrf_code {
|
||||||
|
font-size:200%;
|
||||||
|
width:9rem;
|
||||||
|
padding:0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]{
|
||||||
|
font-size:200%;
|
||||||
|
width:9rem;
|
||||||
|
padding:0.5rem;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
width:70%;
|
||||||
|
margin-left:15%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<img id="logo_id" src="image/logo9000.png" >
|
||||||
|
<div class="content">
|
||||||
|
<h1>Double authentification</h1>
|
||||||
|
<p>
|
||||||
|
Date et heure :
|
||||||
|
<?=$date->format('d/m/Y h:i')?>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Entrez le code que vous avez reçu par email ou sur votre application OTP.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Rafraichissez la page pour recevoir un nouveau code.
|
||||||
|
</p>
|
||||||
|
<FORM method="post" action="login.php" >
|
||||||
|
<input type="text" placeholder="999999" id="vrf_code" name="vrf_code" autocomplete="off">
|
||||||
|
<input type="hidden" name="auth" value="to_validate">
|
||||||
|
<?=$str?>
|
||||||
|
<input type="hidden" name="backurl" value="<?=$url?>">
|
||||||
|
<p>
|
||||||
|
<input type="submit" value="valider">
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</FORM>
|
||||||
|
</div>
|
||||||
|
|
@ -40,13 +40,14 @@ if ( isset ($_POST["ADD"]) )
|
||||||
$new_user=new Noalyss_user($cn,0);
|
$new_user=new Noalyss_user($cn,0);
|
||||||
$new_user->first_name=$http->post('FNAME');
|
$new_user->first_name=$http->post('FNAME');
|
||||||
$new_user->last_name=$http->post('LNAME');
|
$new_user->last_name=$http->post('LNAME');
|
||||||
|
$new_user->setAdmin(0);
|
||||||
$login=$http->post('LOGIN');
|
$login=$http->post('LOGIN');
|
||||||
$login=str_replace("'","",$login);
|
$login=str_replace("'","",$login);
|
||||||
$login=str_replace('"',"",$login);
|
$login=str_replace('"',"",$login);
|
||||||
$login=str_replace(" ","",$login);
|
$login=str_replace(" ","",$login);
|
||||||
$login=strtolower($login);
|
$login=strtolower($login);
|
||||||
$new_user->login=$login;
|
$new_user->login=$login;
|
||||||
|
//$new_user->auth_method=
|
||||||
$new_user->email=$http->post('EMAIL',"string",'');
|
$new_user->email=$http->post('EMAIL',"string",'');
|
||||||
if ( trim($login)=="" || strlen($login)<5)
|
if ( trim($login)=="" || strlen($login)<5)
|
||||||
{
|
{
|
||||||
|
|
@ -104,6 +105,7 @@ if ($sbaction == "save")
|
||||||
$UserChange->active = $http->post('Actif');
|
$UserChange->active = $http->post('Actif');
|
||||||
$UserChange->admin = $http->post('Admin');
|
$UserChange->admin = $http->post('Admin');
|
||||||
$UserChange->email = $http->post('email');
|
$UserChange->email = $http->post('email');
|
||||||
|
$UserChange->authent_method = $http->post('auth_method',"number");
|
||||||
if ($UserChange->active ==-1 || $UserChange->admin ==-1)
|
if ($UserChange->active ==-1 || $UserChange->admin ==-1)
|
||||||
{
|
{
|
||||||
die ('Missing data');
|
die ('Missing data');
|
||||||
|
|
@ -118,14 +120,35 @@ if ($sbaction == "save")
|
||||||
$msg.="$result <br/>";
|
$msg.="$result <br/>";
|
||||||
}
|
}
|
||||||
alert($msg);
|
alert($msg);
|
||||||
|
require_once NOALYSS_INCLUDE.'/user_detail.inc.php';
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
$UserChange->setPassword(md5($_POST['password']));
|
$UserChange->setPassword(md5($_POST['password']));
|
||||||
$UserChange->save();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
// if request new secret for OTP
|
||||||
{
|
if ( $http->post("generate_otp","number",0)==1){
|
||||||
|
|
||||||
|
$UserChange->generate_otp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// save user change
|
||||||
$UserChange->save();
|
$UserChange->save();
|
||||||
|
if ( $UserChange->authent_method !=0 && filter_var($UserChange->email, FILTER_VALIDATE_EMAIL) === false ) {
|
||||||
|
echo_warning(_("Email invalide"));
|
||||||
|
require_once NOALYSS_INCLUDE.'/user_detail.inc.php';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// send a email with link if authen_method !=0 (freeOTP)
|
||||||
|
if ( $UserChange->authent_method !=0
|
||||||
|
&& filter_var($UserChange->email, FILTER_VALIDATE_EMAIL) == true
|
||||||
|
&& $http->post('email_otp','number',0) == 1
|
||||||
|
) {
|
||||||
|
// delete previous send for this user
|
||||||
|
$cn->exec_sql('delete from otp_send_secret where use_id=$1',[$UserChange->getId()]);
|
||||||
|
// send a email with a link to the qcode
|
||||||
|
$UserChange->send_link_otp();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -185,9 +208,9 @@ if ( isset($_REQUEST['det']) && $sbaction=="")
|
||||||
<div style="text-align: center">
|
<div style="text-align: center">
|
||||||
|
|
||||||
<TABLE class="result" >
|
<TABLE class="result" >
|
||||||
<TR><TD style="text-align: right"> <?php echo _('login')?></TD><TD><INPUT id="input_login" class="input_text" TYPE="TEXT" NAME="LOGIN"></TD></tr>
|
<TR><TD style="text-align: right"> <?php echo _('login')?></TD><TD><INPUT id="input_login" class="input_text" TYPE="TEXT" NAME="LOGIN" autocomplete=“off” required></TD></tr>
|
||||||
<TR><TD style="text-align: right"> <?php echo _('Prénom')?></TD><TD><INPUT class="input_text" TYPE="TEXT" NAME="FNAME"></TD></tr>
|
<TR><TD style="text-align: right"> <?php echo _('Prénom')?></TD><TD><INPUT class="input_text" TYPE="TEXT" NAME="FNAME" autocomplete=“off”></TD></tr>
|
||||||
<TR><TD style="text-align: right"> <?php echo _('Nom')?></TD><TD><INPUT class="input_text" TYPE="TEXT" NAME="LNAME"></TD></TR>
|
<TR><TD style="text-align: right"> <?php echo _('Nom')?></TD><TD><INPUT class="input_text" TYPE="TEXT" NAME="LNAME" autocomplete=“off” required></TD></TR>
|
||||||
<TR>
|
<TR>
|
||||||
<TD style="text-align: right"> <?php echo _('Mot de passe')?>
|
<TD style="text-align: right"> <?php echo _('Mot de passe')?>
|
||||||
<?=\Icon_Action::tips("Mot de passe : longueur minimale = 8 dont au moins 1 majuscule, 1 minuscule,1 chiffre et 1 car.spécial")?>
|
<?=\Icon_Action::tips("Mot de passe : longueur minimale = 8 dont au moins 1 majuscule, 1 minuscule,1 chiffre et 1 car.spécial")?>
|
||||||
|
|
@ -195,10 +218,16 @@ if ( isset($_REQUEST['det']) && $sbaction=="")
|
||||||
</TD>
|
</TD>
|
||||||
<TD> <INPUT id="input_password" class="input_text" TYPE="TEXT" NAME="PASS"
|
<TD> <INPUT id="input_password" class="input_text" TYPE="TEXT" NAME="PASS"
|
||||||
onkeyup="check_password_strength('input_password','info_passid',true)"
|
onkeyup="check_password_strength('input_password','info_passid',true)"
|
||||||
|
autocomplete=“off”
|
||||||
|
required
|
||||||
>
|
>
|
||||||
|
|
||||||
</TD></TR>
|
</TD></TR>
|
||||||
<TR><TD style="text-align: right"> <?php echo _('Email')?></TD><TD> <INPUT class="input_text" TYPE="TEXT" NAME="EMAIL"></TD></TR>
|
<TR><TD style="text-align: right"> <?php echo _('Email')?></TD><TD> <INPUT class="input_text" TYPE="TEXT" NAME="EMAIL"></TD></TR>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<tr>
|
||||||
</TABLE>
|
</TABLE>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
@ -246,15 +275,15 @@ $repo=new Dossier(0);
|
||||||
$compteur=0;
|
$compteur=0;
|
||||||
$header=new Sort_Table();
|
$header=new Sort_Table();
|
||||||
$url=basename($_SERVER['PHP_SELF'])."?action=".$_REQUEST['action'];
|
$url=basename($_SERVER['PHP_SELF'])."?action=".$_REQUEST['action'];
|
||||||
$header->add(_("Login"), $url," order by use_login asc", "order by use_login desc","la", "ld");
|
$header->add(_("Login"), $url," order by use_login asc", "order by use_login desc","la", "ld"); // 0
|
||||||
$header->add(_("Nom"), $url," order by use_name asc,use_first_name asc", "order by use_name desc,use_first_name desc","na", "nd");
|
$header->add(_("Nom"), $url," order by use_name asc,use_first_name asc", "order by use_name desc,use_first_name desc","na", "nd"); // 1
|
||||||
$header->add(_('Dossier'),$url,' order by ag_dossier asc','order by ag_dossier desc',
|
$header->add(_("Authentification"), $url," order by use_auth_method asc,use_login asc", "order by use_auth_method desc ,use_login asc","auth", "nauth"); // 2
|
||||||
|
$header->add(_('Dossier'),$url,' order by ag_dossier asc','order by ag_dossier desc', //3
|
||||||
'da','dd');
|
'da','dd');
|
||||||
$header->add(_("Actif"), $url," order by use_active asc", "order by use_active desc","aa", "ad");
|
$header->add(_("Actif"), $url," order by use_active asc", "order by use_active desc","aa", "ad"); // 4
|
||||||
$header->add(_("Email"), $url," order by use_email asc,use_name asc,use_first_name asc", "order by use_email desc,use_name desc,use_first_name desc","na", "nd");
|
$header->add(_("Email"), $url," order by use_email asc,use_name asc,use_first_name asc", "order by use_email desc,use_name desc,use_first_name desc","na", "nd"); //5
|
||||||
$ord=(isset($_REQUEST['ord']))?$_REQUEST['ord']:'la';
|
$ord=$http->request("ord","string","la");
|
||||||
$sql=$header->get_sql_order($ord);
|
$sql=$header->get_sql_order($ord);
|
||||||
|
|
||||||
$a_user=$repo->get_user_folder($sql);
|
$a_user=$repo->get_user_folder($sql);
|
||||||
|
|
||||||
if ( !empty ($a_user) )
|
if ( !empty ($a_user) )
|
||||||
|
|
@ -268,11 +297,13 @@ if ( !empty ($a_user) )
|
||||||
echo '<th>'.$header->get_header(0).'</th>';
|
echo '<th>'.$header->get_header(0).'</th>';
|
||||||
echo '<th>'.$header->get_header(1).'</th>';
|
echo '<th>'.$header->get_header(1).'</th>';
|
||||||
echo th(_("Prénom"));
|
echo th(_("Prénom"));
|
||||||
|
echo '<th>'.$header->get_header(5).'</th>';
|
||||||
|
echo '<th>'.$header->get_header(2).'</th>';
|
||||||
echo '<th>'.$header->get_header(4).'</th>';
|
echo '<th>'.$header->get_header(4).'</th>';
|
||||||
echo '<th>'.$header->get_header(3).'</th>';
|
|
||||||
echo "<th>"._('Type')."</th>";
|
echo "<th>"._('Type')."</th>";
|
||||||
echo '<th>'.$header->get_header(2).'</th>';
|
echo '<th>'.$header->get_header(2).'</th>';
|
||||||
echo '</tr>';
|
echo '</tr>';
|
||||||
|
$a_auth=[0=>_("Mot de passe"),1=>'Email et OTP',2=>'OTP'];
|
||||||
|
|
||||||
foreach ( $a_user as $r_user)
|
foreach ( $a_user as $r_user)
|
||||||
{
|
{
|
||||||
|
|
@ -296,6 +327,8 @@ if ( !empty ($a_user) )
|
||||||
echo td($r_user['use_name']);
|
echo td($r_user['use_name']);
|
||||||
echo td($r_user['use_first_name']);
|
echo td($r_user['use_first_name']);
|
||||||
echo td($r_user['use_email']);
|
echo td($r_user['use_email']);
|
||||||
|
|
||||||
|
echo td($a_auth[$r_user['use_auth_method']]);
|
||||||
echo td($Active);
|
echo td($Active);
|
||||||
$type=($r_user['use_admin']==1)?_("Administrateur"):_("Utilisateur");
|
$type=($r_user['use_admin']==1)?_("Administrateur"):_("Utilisateur");
|
||||||
echo "<td>".$type."</td>";
|
echo "<td>".$type."</td>";
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,40 @@ $it_pass->value="";
|
||||||
?>
|
?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?php echo _('Double authentification');?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$auth_method=new ISelect('auth_method');
|
||||||
|
$auth_method->value=array(
|
||||||
|
array('value'=>0,'label'=>_('Aucune')),
|
||||||
|
array('value'=>1,'label'=>_('email + OTP')),
|
||||||
|
array('value'=>2,'label'=>_('OTP')),
|
||||||
|
);
|
||||||
|
$auth_method->selected=$UserChange->authent_method;
|
||||||
|
echo $auth_method->input();
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?=_("Action Double Authentification")?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span style="display:block">
|
||||||
|
<input type="checkbox" name="generate_otp" value="1"><?=_("Renouveller clef secrète")?>
|
||||||
|
</span>
|
||||||
|
<span style="display:block">
|
||||||
|
<input type="checkbox" name="email_otp" value="1"><?=_("envoyer email pour 2FA avec OTP ")?>
|
||||||
|
<span class="text-muted"><?=_("Un email sera envoyé avec un lien pour initialiser 2FA avec OTP")?>>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<input type="hidden" name="sbaction" id="sbaction" value="save">
|
<input type="hidden" name="sbaction" id="sbaction" value="save">
|
||||||
|
|
@ -136,7 +170,7 @@ $it_pass->value="";
|
||||||
<input type="hidden" name="sbaction" value="delete">
|
<input type="hidden" name="sbaction" value="delete">
|
||||||
<p class="info" id="codedel_div">
|
<p class="info" id="codedel_div">
|
||||||
<?php
|
<?php
|
||||||
echo _("Pour effacer , confirmez en retapant le code");
|
echo _("Pour effacer , confirmez en retapant le code, respectez les majuscules et minuscules");
|
||||||
echo confirm_with_string('userdel','5');
|
echo confirm_with_string('userdel','5');
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
@ -146,7 +180,7 @@ $it_pass->value="";
|
||||||
<input type="Submit" class="button" NAME="DELETE" VALUE="<?=_("Confirmer")?>">
|
<input type="Submit" class="button" NAME="DELETE" VALUE="<?=_("Confirmer")?>">
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<?=HtmlInput::button_hide('delete_user_div')?>
|
<?=HtmlInput::button_hide('delete_user_div','button')?>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</FORM>
|
</FORM>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,30 @@
|
||||||
|
ALTER TABLE public.ac_users ADD use_auth_method int2 DEFAULT 0 NULL;
|
||||||
|
COMMENT ON COLUMN public.ac_users.use_auth_method IS '0 = plain text, 1 = OTP , digit sent by email, 2=freeOTP';
|
||||||
|
ALTER TABLE public.ac_users ADD use_otp_secret text NULL;
|
||||||
|
COMMENT ON COLUMN public.ac_users.use_otp_secret IS 'string base32 generated for OTP';
|
||||||
|
ALTER TABLE public.ac_users ADD CONSTRAINT ac_users_unique UNIQUE (use_otp_secret);
|
||||||
|
update public.ac_users set use_auth_method=0;
|
||||||
|
ALTER TABLE public.ac_users ALTER COLUMN use_auth_method SET NOT NULL;
|
||||||
|
|
||||||
begin;
|
|
||||||
ALTER TABLE ac_dossier drop COLUMN dos_jnt_user ;
|
|
||||||
delete from jnt_use_dos where jnt_id in (select priv_jnt from priv_user where priv_priv='X');
|
|
||||||
delete from jnt_use_dos where use_id in (select use_id from ac_users where use_admin=1 or use_active=0);
|
|
||||||
ALTER TABLE ac_users ADD COLUMN use_email text;
|
|
||||||
COMMENT ON COLUMN ac_users.use_email IS 'Email of the user';
|
|
||||||
|
|
||||||
drop table priv_user;
|
|
||||||
select upgrade_repo(16);
|
CREATE TABLE public.otp_send_secret (
|
||||||
alter table
|
os_id int8 GENERATED ALWAYS AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, -- PK
|
||||||
rollback;
|
os_timestamp timestamptz DEFAULT now() NOT NULL, -- Timestamp of the email sent
|
||||||
|
os_request text NULL, -- Unique identifier when request to scan qrcode
|
||||||
|
use_id int4 NOT NULL, -- FK to ac_users
|
||||||
|
os_code varchar(8) NULL,
|
||||||
|
os_valid_time timestamp NOT NULL,
|
||||||
|
CONSTRAINT otp_send_secret_pk PRIMARY KEY (os_id),
|
||||||
|
CONSTRAINT otp_send_secret_unique UNIQUE (use_id)
|
||||||
|
);
|
||||||
|
COMMENT ON TABLE public.otp_send_secret IS 'sent to user for scanning a QRCODE for FreeOTP
|
||||||
|
or digit to connect, depends of ac_users use_auth_method.';
|
||||||
|
|
||||||
|
-- Column comments
|
||||||
|
|
||||||
|
COMMENT ON COLUMN public.otp_send_secret.os_id IS 'PK';
|
||||||
|
COMMENT ON COLUMN public.otp_send_secret.os_timestamp IS 'Timestamp of the email sent';
|
||||||
|
COMMENT ON COLUMN public.otp_send_secret.os_request IS 'Unique identifier when request to scan qrcode';
|
||||||
|
COMMENT ON COLUMN public.otp_send_secret.os_code IS 'contains code sent by email';
|
||||||
|
COMMENT ON COLUMN public.otp_send_secret.use_id IS 'FK to ac_users';
|
||||||
|
|
@ -23,6 +23,7 @@ class NoalyssUserTest extends TestCase
|
||||||
* @var User
|
* @var User
|
||||||
*/
|
*/
|
||||||
protected $object;
|
protected $object;
|
||||||
|
private $cn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the fixture, for example, opens a network connection.
|
* Sets up the fixture, for example, opens a network connection.
|
||||||
|
|
@ -239,4 +240,21 @@ class NoalyssUserTest extends TestCase
|
||||||
$_SESSION[SESSION_KEY.'use_admin']=1;
|
$_SESSION[SESSION_KEY.'use_admin']=1;
|
||||||
$user->admin=1;
|
$user->admin=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox Generate OTP : check key length and uniqueness
|
||||||
|
*/
|
||||||
|
function testGenerateOTP()
|
||||||
|
{
|
||||||
|
$this->object->generate_otp();
|
||||||
|
$secret=$this->object->get_otp_secret();
|
||||||
|
$this->assertTrue(strlen($secret) == 52,"Fails to generate OTP 32 char ".$secret." len = ".strlen($secret));
|
||||||
|
|
||||||
|
for ($i=0;$i<100;$i++){
|
||||||
|
$this->object->generate_otp();
|
||||||
|
$this->assertTrue($secret != $this->object->get_otp_secret(),"Generate twice the same secret");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue