Test Failed
Push — master ( cf3dbd...f03535 )
by Jean-Christophe
27:09
created

AuthController::connect()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 24
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6.6829

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 20
c 1
b 0
f 1
dl 0
loc 24
ccs 11
cts 15
cp 0.7332
rs 8.9777
cc 6
nc 10
nop 0
crap 6.6829
1
<?php
2
3
namespace Ubiquity\controllers\auth;
4
5
use Ubiquity\utils\http\USession;
6
use Ubiquity\utils\http\URequest;
7
use Ubiquity\utils\flash\FlashMessage;
8
use Ubiquity\controllers\Controller;
9
use Ubiquity\utils\http\UResponse;
10
use Ubiquity\utils\base\UString;
11
use Ubiquity\controllers\Startup;
12
use Ajax\service\Javascript;
13
use Ubiquity\utils\http\UCookie;
14
use Ubiquity\controllers\semantic\InsertJqueryTrait;
15
use Ajax\semantic\html\collections\form\HtmlForm;
16
use Ajax\semantic\components\validation\Rule;
17
use Ajax\php\ubiquity\JsUtils;
18
use Ubiquity\cache\CacheManager;
19
20
/**
21
 * Controller Auth
22
 *
23
 * @property \Ajax\php\ubiquity\JsUtils $jquery
24
 */
25
abstract class AuthController extends Controller {
26
	use AuthControllerCoreTrait,AuthControllerVariablesTrait,AuthControllerOverrideTrait,InsertJqueryTrait,AuthControllerValidationTrait;
1 ignored issue
show
Bug introduced by
The trait Ubiquity\controllers\aut...ControllerOverrideTrait requires the property $s which is not provided by Ubiquity\controllers\auth\AuthController.
Loading history...
27
28
	/**
29
	 *
30
	 * @var AuthFiles
31
	 */
32
	protected $authFiles;
33
	protected $_controller;
34
	protected $_action;
35
	protected $_actionParams;
36
	protected $_noAccessMsg;
37
	protected $_loginCaption;
38
	protected $_attemptsSessionKey = '_attempts';
39
	protected $_controllerInstance;
40
	protected $_compileJS = true;
41
	protected $_invalid=false;
42
43 1
	public function __construct($instance = null) {
44 1
		parent::__construct ();
45 1
		$this->_insertJquerySemantic ();
46 1
		$this->_controller = Startup::getController ();
47 1
		$this->_action = Startup::getAction ();
48 1
		$this->_actionParams = Startup::getActionParams ();
49 1
		$this->_noAccessMsg = new FlashMessage ( 'You are not authorized to access the page <b>{url}</b> !', 'Forbidden access', 'error', 'warning circle' );
50 1
		$this->_loginCaption = 'Log in';
51 1
		$this->_controllerInstance = $instance;
52 1
		if (isset ( $instance )){
53
			Startup::injectDependences ( $instance );
54 1
		}
55
		if($this->useAjax() && !URequest::isAjax()) {
56 1
			$this->_addAjaxBehavior($instance->jquery??$this->jquery);
57 1
		}
58
	}
59
60
	public function index() {
61
		if (($nbAttempsMax = $this->attemptsNumber ()) !== null) {
62
			$nb = USession::getTmp ( $this->_attemptsSessionKey, $nbAttempsMax );
63
			if ($nb <= 0) {
64 1
				$this->badLogin ();
65 1
				return;
66
			}
67 1
		}
68 1
		if($this->useAjax()){
69 1
			$this->_addFrmAjaxBehavior('frm-login');
70 1
		}
71
		$vData=[ 'action' => $this->getBaseUrl () . '/connect','loginInputName' => $this->_getLoginInputName (),'loginLabel' => $this->loginLabel (),'passwordInputName' => $this->_getPasswordInputName (),'passwordLabel' => $this->passwordLabel (),'rememberCaption' => $this->rememberCaption () ];
72
		$this->addAccountCreationViewData($vData,true);
73
		$this->authLoadView ( $this->_getFiles ()->getViewIndex (), $vData );
74
	}
75
	/**
76
	 * Displays the account creation form.
77
	 * Form is submited to /createAccount action
78
	 */
79
	public function addAccount(){
80
		if($this->hasAccountCreation()){
81
			if($this->useAjax()){
82
				$frm=$this->_addFrmAjaxBehavior('frm-create');
83
				$passwordInputName=$this->_getPasswordInputName();
84
				$frm->addExtraFieldRules($passwordInputName.'-conf', ['empty',"match[$passwordInputName]"]);
85
				if($this->_newAccountCreationRule('')!==null){
86
					$this->jquery->exec(Rule::ajax($this->jquery, 'checkAccount', $this->getBaseUrl () . '/newAccountCreationRule', '{}', 'result=data.result;', 'postForm', [
87
									'form' => 'frm-create'
88
							]), true);
89
					$frm->addExtraFieldRule($this->_getLoginInputName(), 'checkAccount','Account {value} is not available!');
90
				}
91
			}
92
			$this->authLoadView ( $this->_getFiles ()->getViewCreate(), [ 'action' => $this->getBaseUrl () . '/createAccount','loginInputName' => $this->_getLoginInputName (),'loginLabel' => $this->loginLabel (),'passwordInputName' => $this->_getPasswordInputName (),'passwordLabel' => $this->passwordLabel (),'passwordConfLabel'=>$this->passwordConfLabel(),'rememberCaption' => $this->rememberCaption () ] );
93
		}
94
	}
95
	
96
97
	/**
98
	 * Submit for a new account creation.
99
	 *
100
	 * @post
101
	 */
102
	#[\Ubiquity\attributes\items\router\Post]
103
	public function createAccount(){
104
		$account=URequest::post($this->_getLoginInputName());
105
		$msgSup='';
106
		if($this->_create($account,URequest::post($this->_getPasswordInputName()))){
107
			if($this->hasEmailValidation()){
108
				$email=$this->getEmailFromNewAccount($account);
109
				$this->prepareEmailValidation($email);
110
				$msgSup="<br>Confirm your email address <b>$email</b> by checking your mailbox.";
111
			}
112 1
			$msg=new FlashMessage ( '<b>{account}</b> account created with success!'.$msgSup, 'Account creation', 'success', 'check square' );
113 1
		}else{
114
			$msg=new FlashMessage ( 'The account <b>{account}</b> was not created!', 'Account creation', 'error', 'warning circle' );
115
		}
116
		$message=$this->fMessage($msg->parseContent(['account'=>$account]));
117
		$this->authLoadView ( $this->_getFiles ()->getViewNoAccess (), [ '_message' => $message,'authURL' => $this->getBaseUrl (),'bodySelector' => $this->_getBodySelector (),'_loginCaption' => $this->_loginCaption ] );
118
	}
119
120
	/**
121 1
	 *
122 1
	 * {@inheritdoc}
123
	 * @see \Ubiquity\controllers\Controller::isValid()
124
	 */
125 1
	public final function isValid($action) {
126 1
		return true;
127 1
	}
128 1
129
	/**
130 1
	 * Action called when the user does not have access rights to a requested resource
131
	 *
132
	 * @param array|string $urlParts
133
	 */
134
	public function noAccess($urlParts) {
135 1
		if (! \is_array ( $urlParts )) {
136 1
			$urlParts = \explode ( '.', $urlParts );
137 1
		}
138 1
		USession::set ( 'urlParts', $urlParts );
139
		$fMessage = $this->_noAccessMsg;
140
		$this->noAccessMessage ( $fMessage );
141
		$message = $this->fMessage ( $fMessage->parseContent ( [ 'url' => \implode ( '/', $urlParts ) ] ) );
142
		
143 1
		if (URequest::isAjax ()) {
144 1
			$this->jquery->get ( $this->_getBaseRoute () . '/info/f', '#_userInfo', [ 'historize' => false,'jqueryDone' => 'replaceWith','hasLoader' => false,'attr' => '' ] );
145 1
			$this->jquery->compile ( $this->view );
146 1
		}
147
		$vData=[ '_message' => $message,'authURL' => $this->getBaseUrl (),'bodySelector' => $this->_getBodySelector (),'_loginCaption' => $this->_loginCaption ];
148
		$this->addAccountCreationViewData($vData);
149 1
		$this->authLoadView ( $this->_getFiles ()->getViewNoAccess (), $vData);
150
	}
151
152 1
	/**
153
	 * Override to implement the complete connection procedure
154
	 *
155
	 * @post
156
	 */
157
	#[\Ubiquity\attributes\items\router\Post]
158
	public function connect() {
159 1
		if (URequest::isPost ()) {
160
			if ($connected = $this->_connect ()) {
161
				if (isset ( $_POST ['ck-remember'] )) {
162 1
					$this->rememberMe ( $connected );
163 1
				}
164 1
				if (USession::exists ( $this->_attemptsSessionKey )) {
165 1
					USession::delete ( $this->_attemptsSessionKey );
166
				}
167
				if($this->has2FA($connected)){
168 1
					$this->initializeAuth();
169
					USession::set($this->_getUserSessionKey().'-2FA', $connected);
170
					$this->send2FACode();
171
					$this->confirm();
172
					$this->finalizeAuth();
173 1
				}else{
174 1
					$this->onConnect ( $connected );
175 1
				}
176 1
			} else {
177 1
				$this->_invalid=true;
178
				$this->initializeAuth();
179
				$this->onBadCreditentials ();
180
				$this->finalizeAuth();
181
			}
182
		}
183
	}
184
185
	/**
186
	 * Default Action for invalid creditentials
187
	 *
188
	 * @noRoute()
189
	 */
190
	#[\Ubiquity\attributes\items\router\NoRoute]
191
	public function badLogin() {
192
		$fMessage = new FlashMessage ( 'Invalid creditentials!', 'Connection problem', 'warning', 'warning circle' );
193
		$this->badLoginMessage ( $fMessage );
194 1
		$attemptsMessage = '';
195 1
		if (($nbAttempsMax = $this->attemptsNumber ()) !== null) {
196 1
			$nb = USession::getTmp ( $this->_attemptsSessionKey, $nbAttempsMax );
197
			$nb --;
198
			if ($nb < 0) {
199
				$nb = 0;
200
			}
201
			if ($nb == 0) {
202 1
				$fAttemptsNumberMessage = $this->noAttempts ();
203 1
			} else {
204 1
				$fAttemptsNumberMessage = new FlashMessage ( '<i class="ui warning icon"></i> You still have {_attemptsCount} attempts to log in.', null, 'bottom attached warning', '' );
205 1
			}
206 1
			USession::setTmp ( $this->_attemptsSessionKey, $nb, $this->attemptsTimeout () );
207 1
			$this->attemptsNumberMessage ( $fAttemptsNumberMessage, $nb );
208 1
			$fAttemptsNumberMessage->parseContent ( [ '_attemptsCount' => $nb,'_timer' => '<span id="timer"></span>' ] );
209
			$attemptsMessage = $this->fMessage ( $fAttemptsNumberMessage, 'timeout-message' );
210
			$fMessage->addType ( "attached" );
211
		}
212
		$message = $this->fMessage ( $fMessage, 'bad-login' ) . $attemptsMessage;
213
		$this->authLoadView ( $this->_getFiles ()->getViewNoAccess (), [ '_message' => $message,'authURL' => $this->getBaseUrl (),'bodySelector' => $this->_getBodySelector (),'_loginCaption' => $this->_loginCaption ] );
214
	}
215
	
216
	/**
217
	 * Logout action
218
	 * Terminate the session and display a logout message
219
	 */
220
	public function terminate() {
221
		USession::terminate ();
222
		$fMessage = new FlashMessage ( 'You have been properly disconnected!', 'Logout', 'success', 'checkmark' );
223
		$this->terminateMessage ( $fMessage );
224
		$message = $this->fMessage ( $fMessage );
225
		$this->authLoadView ( $this->_getFiles ()->getViewNoAccess (), [ '_message' => $message,'authURL' => $this->getBaseUrl (),'bodySelector' => $this->_getBodySelector (),'_loginCaption' => $this->_loginCaption ] );
226
	}
227 1
228 1
	public function _disConnected() {
229
		$fMessage = new FlashMessage ( 'You have been disconnected from the application!', 'Logout', '', 'sign out' );
230
		$this->disconnectedMessage ( $fMessage );
231 1
		$message = $this->fMessage ( $fMessage );
232
		$this->jquery->getOnClick ( '._signin', $this->getBaseUrl (), $this->_getBodySelector (), [ 'stopPropagation' => false,'preventDefault' => false ] );
233 1
		$this->jquery->execOn ( 'click', '._close', "window.open(window.location,'_self').close();" );
234
		return $this->jquery->renderView ( $this->_getFiles ()->getViewDisconnected (), [ "_title" => 'Session ended','_message' => $message ], true );
235
	}
236
237
	/**
238
	 * Action displaying the logged user information
239
	 * if _displayInfoAsString returns true, use _infoUser var in views to display user info
240
	 *
241
	 * @param null|boolean $force
242
	 * @return string|null
243
	 * @throws \Exception
244
	 */
245
	public function info($force = null) {
246
		if (isset ( $force )) {
247
			$displayInfoAsString = $force === true;
248
		} else {
249
			$displayInfoAsString = $this->_displayInfoAsString ();
250
		}
251
		return $this->loadView ( $this->_getFiles ()->getViewInfo (), [ 'connected' => USession::get ( $this->_getUserSessionKey () ),'authURL' => $this->getBaseUrl (),'bodySelector' => $this->_getBodySelector () ], $displayInfoAsString );
252
	}
253
	
254
	public function checkConnection() {
255
		UResponse::asJSON ();
256
		echo \json_encode(['valid'=> UString::getBooleanStr ( $this->_isValidUser () )]);
257
	}
258
259
	/**
260
	 * Sets the default noAccess message
261
	 * Default : "You are not authorized to access the page <b>{url}</b> !"
262
	 *
263
	 * @param string $content
264
	 * @param string $title
265 1
	 * @param string $type
266 1
	 * @param string $icon
267 1
	 */
268
	public function _setNoAccessMsg($content, $title = NULL, $type = NULL, $icon = null) {
269
		$this->_noAccessMsg->setValues ( $content, $title, $type, $icon );
270
	}
271
272
	/**
273 1
	 *
274
	 * @param string $_loginCaption
275
	 */
276
	public function _setLoginCaption($_loginCaption) {
277
		$this->_loginCaption = $_loginCaption;
278
	}
279
280
	/**
281
	 * Auto connect the user
282
	 */
283
	public function _autoConnect() {
284
		$cookie = $this->getCookieUser ();
285
		if (isset ( $cookie )) {
286
			$user = $this->fromCookie ( $cookie );
287
			if (isset ( $user )) {
288 1
				USession::set ( $this->_getUserSessionKey (), $user );
289 1
			}
290 1
		}
291 1
	}
292
293 1
	/**
294 1
	 * Deletes the cookie for auto connection and returns to index
295 1
	 */
296
	public function forgetConnection() {
297
		UCookie::delete ( $this->_getUserSessionKey () );
298 1
		$this->index ();
299
	}
300 1
301 1
	/**
302 1
	 *
303
	 * {@inheritdoc}
304 1
	 * @see \Ubiquity\controllers\ControllerBase::finalize()
305
	 */
306
	public function finalize() {
307
		if (! UResponse::isJSON ()) {
308
			if(Startup::getAction()!=='connect') {
309
				$this->finalizeAuth();
310
			}
311 1
			$this->jquery->execAtLast ( "if($('#_userInfo').length){\$('#_userInfo').replaceWith(" . \preg_replace ( "/$\R?^/m", "", Javascript::prep_element ( $this->info () ) ) . ");}" );
312 1
			if ($this->_compileJS) {
313 1
				echo $this->jquery->compile ();
314
			}
315 1
		}
316
	}
317 1
318 1
	protected function finalizeAuth() {
319 1
		if (!URequest::isAjax()) {
320
			$this->loadView('@activeTheme/main/vFooter.html');
321 1
		}
322
	}
323
324
	/**
325
	 *
326
	 * {@inheritdoc}
327 1
	 * @see \Ubiquity\controllers\ControllerBase::initialize()
328 1
	 */
329 1
	public function initialize() {
330
		if(Startup::getAction()!=='connect') {
331 1
			$this->initializeAuth();
332 1
		}
333
	}
334 1
335 1
	protected function initializeAuth() {
336
		if (!URequest::isAjax()) {
337
			$this->loadView('@activeTheme/main/vHeader.html');
338
		}
339
	}
340
341
	/**
342
	 *
343 1
	 * @param string $url
344 1
	 */
345 1
	public function _forward($url, $initialize = null, $finalize = null) {
346 1
		if (! isset ( $initialize )) {
347 1
			$initialize = (! isset ( $this->_controllerInstance ) || URequest::isAjax ());
348 1
		}
349
		if (! isset ( $finalize )) {
350
			$finalize = $initialize;
351
		}
352
		Startup::forward ( $url, $initialize, $finalize );
353
	}
354
	
355
	public function _addAjaxBehavior(JsUtils $jquery=null,$ajaxParameters=['hasLoader'=>'$(this).children(".button")','historize'=>false,'listenerOn'=>'body']){
356
		$jquery??=$this->jquery;
357
		$jquery->getHref('.ajax[data-target]','', $ajaxParameters);
358
		$jquery->postFormAction('.ui.form',$this->_getBodySelector(),$ajaxParameters);
359
	}
360
361
	public function _addFrmAjaxBehavior($id):HtmlForm{
362
		$frm=$this->jquery->semantic()->htmlForm($id);
363
		$frm->addExtraFieldRule($this->_getLoginInputName(),'empty');
364
		$frm->addExtraFieldRule($this->_getPasswordInputName(),'empty');
365
		$frm->setValidationParams(['inline'=>true,'on'=>'blur']);
366
		return $frm;
367
	}
368
}
369