Passed
Push — master ( df4f71...b35ad3 )
by Jean-Christophe
18:08
created

Auth2FATrait   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Test Coverage

Coverage 3.77%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 22
eloc 47
c 1
b 0
f 0
dl 0
loc 195
ccs 2
cts 53
cp 0.0377
rs 10

17 Methods

Rating   Name   Duplication   Size   Complexity  
A sendNew2FACode() 0 5 1
A send2FACode() 0 4 1
A getTokenSize() 0 2 1
A towFACodePrefix() 0 2 1
A has2FA() 0 2 1
A generate2FACode() 0 2 1
A twoFABadCodeMessage() 0 1 1
A twoFAMessage() 0 1 1
A onBad2FACode() 0 2 1
A newTwoFACodeMessage() 0 1 1
A save2FACode() 0 6 1
A submitCode() 0 13 5
A twoFACodeDuration() 0 2 1
A _send2FACode() 0 1 1
A bad2FACode() 0 7 1
A confirm() 0 11 2
A check2FACode() 0 2 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
	abstract public function info($force = null);
47
48
	/**
49
	 * To override for defining a new action when 2FA code is invalid.
50
	 */
51
	protected function onBad2FACode():void{
52
		$this->bad2FACode();
53
	}
54
55
	/**
56
	 * To override
57
	 * Send the 2FA code to the user (email, sms, phone call...)
58
	 * @param string $code
59
	 * @param mixed $connected
60
	 */
61
	protected function _send2FACode(string $code,$connected):void{
62
63
	}
64
65
	/**
66
	 * To override
67
	 * Returns true for a two factor authentification for this account.
68
	 * @param mixed $accountValue
69
	 * @return bool
70
	 */
71 1
	protected function has2FA($accountValue=null):bool{
72 1
		return false;
73
	}
74
75
	/**
76
	 * To override
77
	 * Returns the default size for generated tokens.
78
	 * @return int
79
	 */
80
	protected function getTokenSize():int{
81
		return 6;
82
	}
83
84
	/**
85
	 * Generates a new random 2FA code.
86
	 * You have to override this basic implementation.
87
	 * @return string
88
	 * @throws \Exception
89
	 */
90
	protected function generate2FACode():string{
91
		return \bin2hex ( \random_bytes ($this->getTokenSize()));
92
	}
93
94
	/**
95
	 * Returns the code prefix (which should not be entered by the user).
96
	 * @return string
97
	 */
98
	protected function towFACodePrefix():string{
99
		return 'U-';
100
	}
101
102
103
	/**
104
	 * Returns the default validity duration of a generated 2FA code.
105
	 * @return \DateInterval
106
	 */
107
	protected function twoFACodeDuration():\DateInterval{
108
		return new \DateInterval('PT5M');
109
	}
110
111
	/**
112
	 * To override for modifying the 2FA panel message.
113
	 * @param FlashMessage $fMessage
114
	 */
115
	protected function twoFAMessage(FlashMessage $fMessage){
116
117
	}
118
	/**
119
	 * To override
120
	 * @param FlashMessage $fMessage
121
	 */
122
	protected function newTwoFACodeMessage(FlashMessage $fMessage){
123
124
	}
125
126
	/**
127
	 * To override for modifying the message displayed if the 2FA code is bad.
128
	 * @param FlashMessage $fMessage
129
	 */
130
	protected function twoFABadCodeMessage(FlashMessage $fMessage){
131
132
	}
133
134
	/**
135
	 * To override for a more secure 2FA code.
136
	 * @param string $secret
137
	 * @param string $userInput
138
	 * @return bool
139
	 */
140
	protected function check2FACode(string $secret,string $userInput):bool{
141
		return $secret===$userInput;
142
	}
143
144
	/**
145
	 * @noRoute
146
	 */
147
	#[\Ubiquity\attributes\items\router\NoRoute]
148
	public function bad2FACode():void{
149
		$this->confirm();
150
		$fMessage = new FlashMessage ( 'Invalid 2FA code!', 'Two Factor Authentification', 'warning', 'warning circle' );
151
		$this->twoFABadCodeMessage( $fMessage );
152
		$message = $this->fMessage ( $fMessage, 'bad-code' );
153
		$this->authLoadView ( $this->_getFiles ()->getViewBadTwoFACode(), [ '_message' => $message,'url' => $this->getBaseUrl ().'/sendNew2FACode','bodySelector' => '#bad-two-fa','_btCaption' => 'Send new code' ] );
154
	}
155
156
	/**
157
	 * @noRoute
158
	 */
159
	#[\Ubiquity\attributes\items\router\NoRoute]
160
	public function confirm(){
161
		$fMessage = new FlashMessage( 'Enter the rescue code and validate.', 'Two factor Authentification', 'info', 'key' );
162
		$this->twoFAMessage ( $fMessage );
163
		$message = $this->fMessage ( $fMessage );
164
		if($this->useAjax()){
165
			$frm=$this->jquery->semantic()->htmlForm('frm-valid-code');
166
			$frm->addExtraFieldRule('code','empty');
167
			$frm->setValidationParams(['inline'=>true,'on'=>'blur']);
168
		}
169
		$this->authLoadView ( $this->_getFiles ()->getViewStepTwo(), [ '_message' => $message,'submitURL' => $this->getBaseUrl ().'/submitCode','bodySelector' => $this->_getBodySelector(),'prefix'=>$this->towFACodePrefix() ] );
170
	}
171
	
172
	protected function save2FACode():array{
173
		$code=$this->generate2FACode();
174
		$expire=(new \DateTime())->add($this->twoFACodeDuration());
175
		$codeInfos=USession::get(self::$TWO_FA_KEY,compact('code','expire'));
176
		USession::set(self::$TWO_FA_KEY,$codeInfos);
177
		return $codeInfos;
178
	}
179
	
180
	/**
181
	 * Submits the 2FA code in post request.
182
	 * 
183
	 * @post
184
	 */
185
	#[\Ubiquity\attributes\items\router\Post]
186
	public function submitCode(){
187
		if(URequest::isPost() && USession::exists(self::$TWO_FA_KEY)){
188
			$twoFAInfos=USession::get(self::$TWO_FA_KEY);
189
			$expired=$twoFAInfos['expire']<new \DateTime();
190
			if(!$expired && $this->check2FACode($twoFAInfos['code'],URequest::post('code'))){
1 ignored issue
show
Bug introduced by
It seems like Ubiquity\utils\http\URequest::post('code') can also be of type null; however, parameter $userInput of Ubiquity\controllers\aut...FATrait::check2FACode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

190
			if(!$expired && $this->check2FACode($twoFAInfos['code'],/** @scrutinizer ignore-type */ URequest::post('code'))){
Loading history...
191
				$this->onConnect(USession::get($this->_getUserSessionKey().'-2FA'));
192
			}
193
			else{
194
				$this->_invalid=true;
195
				$this->initializeAuth();
196
				$this->onBad2FACode();
197
				$this->finalizeAuth();
198
			}
199
		}
200
	}
201
202
	/**
203
	 * @noRoute
204
	 */
205
	#[\Ubiquity\attributes\items\router\NoRoute]
206
	public function send2FACode(){
207
		$codeInfos=$this->save2FACode();
208
		$this->_send2FACode($codeInfos['code'], USession::get($this->_getUserSessionKey().'-2FA'));
209
	}
210
	
211
	public function sendNew2FACode(){
212
		$this->send2FACode();
213
		$fMessage = new FlashMessage ( 'A new code was submited.', 'Two factor Authentification', 'success', 'key' );
214
		$this->newTwoFACodeMessage ( $fMessage );
215
		echo $this->fMessage ( $fMessage );
216
	}
217
218
}
219
220