Passed
Push — master ( a87d59...aa15b4 )
by Jean-Christophe
20:22
created

Auth2FATrait::bad2FACode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Ubiquity\controllers\auth\traits;
4
5
use Ubiquity\controllers\auth\AuthFiles;
6
use Ubiquity\utils\flash\FlashMessage;
7
use Ubiquity\utils\http\USession;
8
use Ubiquity\utils\http\URequest;
9
10
/**
11
 * 
12
 * Ubiquity\controllers\auth\traits$Auth2FATrait
13
 * This class is part of Ubiquity
14
 * @author jc
15
 * @version 1.0.0
16
 * 
17
 * @property bool $_invalid
18
 * @property \Ajax\php\ubiquity\JsUtils $jquery
19
 *
20
 */
21
trait Auth2FATrait {
22
23
	private static $TWO_FA_KEY='2FA-infos';
24
25
26
	abstract protected function fMessage(FlashMessage $fMessage, $id = null):string;
27
28
	abstract protected function _getFiles(): AuthFiles;
29
30
	abstract protected function getBaseUrl():string;
31
32
	abstract protected function authLoadView($viewName, $vars = [ ]):void;
33
34
	abstract protected function useAjax():bool;
35
36
	abstract public function _getBodySelector():string;
37
38
	abstract public function _getUserSessionKey():string;
39
40
	abstract protected function onConnect($connected);
41
42
	abstract protected function initializeAuth();
43
44
	abstract protected function finalizeAuth();
45
46
47
	/**
48
	 * To override
49
	 * Returns true for a two factor authentification for this account.
50
	 * @param mixed $accountValue
51
	 * @return bool
52
	 */
53 1
	protected function has2FA($accountValue=null):bool{
54 1
		return false;
55
	}
56
57
	/**
58
	 * To override for defining a new action when 2FA code is invalid.
59
	 */
60 1
	protected function onBad2FACode():void{
61 1
		$this->bad2FACode();
62
	}
63
64
	/**
65
	 * To override
66
	 * Send the 2FA code to the user (email, sms, phone call...)
67
	 * @param string $code
68
	 * @param mixed $connected
69
	 */
70
	protected function _send2FACode(string $code,$connected):void{
71
72
	}
73
74
	/**
75
	 * To override
76
	 * Returns the default size for generated tokens.
77
	 * @return int
78
	 */
79
	protected function getTokenSize():int{
80
		return 6;
81
	}
82
83
	/**
84
	 * Generates a new random 2FA code.
85
	 * You have to override this basic implementation.
86
	 * @return string
87
	 * @throws \Exception
88
	 */
89 1
	protected function generate2FACode():string{
90 1
		return \bin2hex ( \random_bytes ($this->getTokenSize()));
91
	}
92
93
	/**
94
	 * Returns the code prefix (which should not be entered by the user).
95
	 * @return string
96
	 */
97
	protected function towFACodePrefix():string{
98
		return 'U-';
99
	}
100
101
102
	/**
103
	 * Returns the default validity duration of a generated 2FA code.
104
	 * @return \DateInterval
105
	 */
106 1
	protected function twoFACodeDuration():\DateInterval{
107 1
		return new \DateInterval('PT5M');
108
	}
109
110
	/**
111
	 * To override for modifying the 2FA panel message.
112
	 * @param FlashMessage $fMessage
113
	 */
114
	protected function twoFAMessage(FlashMessage $fMessage){
115
116
	}
117
	/**
118
	 * To override
119
	 * @param FlashMessage $fMessage
120
	 */
121
	protected function newTwoFACodeMessage(FlashMessage $fMessage){
122
123
	}
124
125
	/**
126
	 * To override for modifying the message displayed if the 2FA code is bad.
127
	 * @param FlashMessage $fMessage
128
	 */
129
	protected function twoFABadCodeMessage(FlashMessage $fMessage){
130
131
	}
132
133
	/**
134
	 * To override for a more secure 2FA code.
135
	 * @param string $secret
136
	 * @param string $userInput
137
	 * @return bool
138
	 */
139 1
	protected function check2FACode(string $secret,string $userInput):bool{
140 1
		return $secret===$userInput;
141
	}
142
143
	/**
144
	 * @noRoute
145
	 */
146
	#[\Ubiquity\attributes\items\router\NoRoute]
147 1
	public function bad2FACode():void{
148 1
		$this->confirm();
149 1
		$fMessage = new FlashMessage ( 'Invalid 2FA code!', 'Two Factor Authentification', 'warning', 'warning circle' );
150 1
		$this->twoFABadCodeMessage( $fMessage );
151 1
		$message = $this->fMessage ( $fMessage, 'bad-code' );
152 1
		$this->authLoadView ( $this->_getFiles ()->getViewBadTwoFACode(), [ '_message' => $message,'url' => $this->getBaseUrl ().'/sendNew2FACode','bodySelector' => '#bad-two-fa','_btCaption' => 'Send new code' ] );
153
	}
154
155
	/**
156
	 * @noRoute
157
	 */
158
	#[\Ubiquity\attributes\items\router\NoRoute]
159 1
	public function confirm(){
160 1
		$fMessage = new FlashMessage( 'Enter the rescue code and validate.', 'Two factor Authentification', 'info', 'key' );
161 1
		$this->twoFAMessage ( $fMessage );
162 1
		$message = $this->fMessage ( $fMessage );
163 1
		if($this->useAjax()){
164 1
			$frm=$this->jquery->semantic()->htmlForm('frm-valid-code');
165 1
			$frm->addExtraFieldRule('code','empty');
166 1
			$frm->setValidationParams(['inline'=>true,'on'=>'blur']);
167
		}
168 1
		$this->authLoadView ( $this->_getFiles ()->getViewStepTwo(), [ '_message' => $message,'submitURL' => $this->getBaseUrl ().'/submitCode','bodySelector' => $this->_getBodySelector(),'prefix'=>$this->towFACodePrefix() ] );
169
	}
170
	
171 1
	protected function save2FACode():array{
172 1
		$code=$this->generate2FACode();
173 1
		$expire=(new \DateTime())->add($this->twoFACodeDuration());
174 1
		$codeInfos=USession::get(self::$TWO_FA_KEY,compact('code','expire'));
175 1
		USession::set(self::$TWO_FA_KEY,$codeInfos);
176 1
		return $codeInfos;
177
	}
178
	
179
	/**
180
	 * Submits the 2FA code in post request.
181
	 * 
182
	 * @post
183
	 */
184
	#[\Ubiquity\attributes\items\router\Post]
185 1
	public function submitCode(){
186 1
		if(URequest::isPost() && USession::exists(self::$TWO_FA_KEY)){
187 1
			$twoFAInfos=USession::get(self::$TWO_FA_KEY);
188 1
			$expired=$twoFAInfos['expire']<new \DateTime();
189 1
			if(!$expired && $this->check2FACode($twoFAInfos['code'],URequest::post('code'))){
190 1
				$this->onConnect(USession::get($this->_getUserSessionKey().'-2FA'));
191
			}
192
			else{
193 1
				$this->_invalid=true;
194 1
				$this->initializeAuth();
195 1
				$this->onBad2FACode();
196 1
				$this->finalizeAuth();
197
			}
198
		}
199
	}
200
201 1
	protected function send2FACode(){
202 1
		$codeInfos=$this->save2FACode();
203 1
		$this->_send2FACode($codeInfos['code'], USession::get($this->_getUserSessionKey().'-2FA'));
204
	}
205
	
206 1
	public function sendNew2FACode(){
207 1
		if(USession::exists( $this->_getUserSessionKey().'-2FA')) {
208 1
			$this->send2FACode();
209 1
			$fMessage = new FlashMessage ('A new code was submited.', 'Two factor Authentification', 'success', 'key');
210 1
			$this->newTwoFACodeMessage($fMessage);
211 1
			echo $this->fMessage($fMessage);
212
		}
213
	}
214
215
}
216
217