1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace tool_brickfield; 18 19 /** 20 * Class registration contains the functions to manage registration validation. 21 * 22 * @package tool_brickfield 23 * @author 2021 Onwards Mike Churchward <mike@brickfieldlabs.ie> 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL 25 */ 26 class registration { 27 28 /** @var int Registration information has not been entered. */ 29 const NOT_ENTERED = 0; 30 31 /** @var int Registration information has been entered but not externally validated. */ 32 const PENDING = 1; 33 34 /** @var int Registration information was entered but was not validated within the defined grace periods. */ 35 const INVALID = 2; 36 37 /** @var int Registration information has been externally validated. */ 38 const VALIDATED = 3; 39 40 /** @var int Registration information has expired and needs to be revalidated. */ 41 const EXPIRED = 4; 42 43 /** @var int Registration validation attempted, but failed. */ 44 const ERROR = 5; 45 46 /** @var string Name of variable storing the registration status. */ 47 const STATUS = 'bfregstatus'; 48 49 /** @var string Name of variable storing the last time the registration information was checked. */ 50 const VALIDATION_CHECK_TIME = 'bfregvalidationchecktime'; 51 52 /** @var string Name of variable storing the time the registration information was validated. */ 53 const VALIDATION_TIME = 'bfregvalidationtime'; 54 55 /** @var string Name of variable storing the time the summary data was last sent. */ 56 const SUMMARY_TIME = 'bfsummarytime'; 57 58 /** @var string Name of variable storing the registration API key. */ 59 const API_KEY = 'key'; 60 61 /** @var string Name of variable storing the registration API key. */ 62 const SECRET_KEY = 'hash'; 63 64 /** @var string Name of the variable storing the site id. */ 65 const SITEID = 'id'; 66 67 /** @var int The current validation status. */ 68 protected $validation; 69 70 /** @var int The last time the validation was checked. */ 71 protected $checktime; 72 73 /** @var int The last time the validation time was confirmed. */ 74 protected $validationtime; 75 76 /** @var int The last time the summary data was sent. */ 77 protected $summarytime; 78 79 /** @var string The API key required for registration. */ 80 protected $apikey; 81 82 /** @var string The secret key required for registration. */ 83 protected $secretkey; 84 85 /** @var string The registered site id. */ 86 protected $siteid; 87 88 /** @var string The URL to register at. */ 89 private static $regurl = 'https://account.mybrickfield.ie/register'; 90 91 /** @var string The URL to view terms at. */ 92 private static $termsurl = 'https://account.mybrickfield.ie/terms'; 93 94 /** 95 * Object registration constructor. 96 * @throws \dml_exception 97 */ 98 public function __construct() { 99 $this->validation = $this->get_status(); 100 $this->checktime = $this->get_check_time(); 101 $this->validationtime = $this->get_validation_time(); 102 $this->summarytime = $this->get_summary_time(); 103 $this->apikey = $this->get_api_key(); 104 $this->secretkey = $this->get_secret_key(); 105 $this->siteid = $this->get_siteid(); 106 } 107 108 /** 109 * System can be used when it has been validated, or when its still awaiting validation. 110 * @return bool 111 */ 112 public function toolkit_is_active(): bool { 113 return $this->status_is_validated() || $this->validation_pending(); 114 } 115 116 /** 117 * The "not validated" state also needs the grace period to still be in effect. 118 * @return bool 119 */ 120 public function validation_pending(): bool { 121 return ($this->status_is_pending() || $this->status_is_error()) && $this->grace_period_valid(); 122 } 123 124 /** 125 * Return true if there was a validation error. 126 * @return bool 127 */ 128 public function validation_error(): bool { 129 return $this->status_is_error(); 130 } 131 132 /** 133 * Perform all necessary steps when new keys are added. Also check that they actually look like keys. 134 * @param string $apikey 135 * @param string $secretkey 136 * @return bool 137 */ 138 public function set_keys_for_registration(string $apikey, string $secretkey): bool { 139 if ($this->keys_are_valid($apikey, $secretkey)) { 140 $this->set_api_key($apikey); 141 $this->set_secret_key($secretkey); 142 $this->set_not_validated(); 143 if ($this->summarytime <= 0) { 144 $this->set_summary_time(); 145 } 146 return true; 147 } else { 148 $this->set_api_key(''); 149 $this->set_secret_key(''); 150 $this->set_not_entered(); 151 return false; 152 } 153 } 154 155 /** 156 * If the registration is not already valid, validate it. This may connect to the registration site. 157 * @return bool 158 * @throws \dml_exception 159 */ 160 public function validate(): bool { 161 // If this is currently valid, return true, unless its time to check again. 162 if ($this->status_is_validated()) { 163 // If the summary data has not been sent in over a week, invalidate the registration. 164 if ($this->summarydata_grace_period_expired()) { 165 $this->set_invalid(); 166 return false; 167 } 168 // Confirm registration after the grace period has expired. 169 if ($this->grace_period_valid()) { 170 return true; 171 } else { 172 // Recheck the registration. 173 return $this->revalidate(); 174 } 175 } 176 177 // Check for valid keys, and possibly move status to validation stage. 178 if (!$this->keys_are_valid()) { 179 // The current stored keys are not valid format, set the status to "not entered". 180 $this->set_not_entered(); 181 return false; 182 } else if ($this->status_is_not_entered()) { 183 // If no keys have previously been seen, move to validation stage. 184 $this->set_not_validated(); 185 } 186 187 // If no validation has been confirmed, check the registration site. 188 if ($this->validation_pending()) { 189 $brickfieldconnect = $this->get_registration_connection(); 190 $this->set_check_time(); 191 if ($brickfieldconnect->is_registered() || $brickfieldconnect->update_registration($this->apikey, $this->secretkey)) { 192 // Keys are present and have been validated. 193 $this->set_valid(); 194 return true; 195 } else { 196 // Keys are present but were not validated. 197 $this->set_error(); 198 } 199 } 200 201 // If any of the grace periods have passed without a validation, invalidate the registration. 202 if (!$this->grace_period_valid() || $this->summarydata_grace_period_expired()) { 203 $this->set_invalid(); 204 return false; 205 } else { 206 return true; 207 } 208 } 209 210 /** 211 * Even if the regisration is currently valid, validate it again. 212 * @return bool 213 * @throws \dml_exception 214 */ 215 public function revalidate(): bool { 216 if ($this->status_is_validated()) { 217 $this->set_not_validated(); 218 } 219 return $this->validate(); 220 } 221 222 /** 223 * Get api key. 224 * @return string 225 * @throws \dml_exception 226 */ 227 public function get_api_key(): string { 228 $key = get_config(manager::PLUGINNAME, self::API_KEY); 229 if ($key === false) { 230 // Not set in config yet, so default it to "". 231 $key = ''; 232 $this->set_api_key($key); 233 } 234 return $key; 235 } 236 237 /** 238 * Get secret key. 239 * @return string 240 * @throws \dml_exception 241 */ 242 public function get_secret_key(): string { 243 $key = get_config(manager::PLUGINNAME, self::SECRET_KEY); 244 if ($key === false) { 245 // Not set in config yet, so default it to "". 246 $key = ''; 247 $this->set_secret_key($key); 248 } 249 return $key; 250 } 251 252 /** 253 * Get the registration URL. 254 * @return string 255 */ 256 public function get_regurl(): string { 257 return self::$regurl; 258 } 259 260 /** 261 * Get the terms and conditions URL. 262 * @return string 263 */ 264 public function get_termsurl(): string { 265 return self::$termsurl; 266 } 267 268 /** 269 * Perform all actions needed to note that the summary data was sent. 270 */ 271 public function mark_summary_data_sent() { 272 $this->set_summary_time(); 273 } 274 275 /** 276 * Set the registered site id. 277 * @param int $id 278 * @return bool 279 */ 280 public function set_siteid(int $id): bool { 281 $this->siteid = $id; 282 return set_config(self::SITEID, $id, manager::PLUGINNAME); 283 } 284 285 /** 286 * Return the registered site id. 287 * @return int 288 * @throws \dml_exception 289 */ 290 public function get_siteid(): int { 291 $siteid = get_config(manager::PLUGINNAME, self::SITEID); 292 if ($siteid === false) { 293 // Not set in config yet, so default it to 0. 294 $siteid = 0; 295 $this->set_siteid($siteid); 296 } 297 return (int)$siteid; 298 } 299 300 /** 301 * Set the status as keys "not entered". 302 * @return bool 303 */ 304 protected function set_not_entered(): bool { 305 return $this->set_status(self::NOT_ENTERED); 306 } 307 308 /** 309 * "Not validated" means we have keys, but have not confirmed them yet. Set the validation time to start the grace period. 310 * @return bool 311 */ 312 protected function set_not_validated(): bool { 313 $this->set_validation_time(); 314 return $this->set_status(self::PENDING); 315 } 316 317 /** 318 * Set the registration as invalid. 319 * @return bool 320 */ 321 protected function set_invalid(): bool { 322 $this->set_api_key(''); 323 $this->set_secret_key(''); 324 $this->set_siteid(0); 325 return $this->set_status(self::INVALID); 326 } 327 328 /** 329 * Set the registration as valid. 330 * @return bool 331 */ 332 protected function set_valid(): bool { 333 $this->set_validation_time(); 334 $this->set_summary_time(); 335 return $this->set_status(self::VALIDATED); 336 } 337 338 /** 339 * Set the status to "expired". 340 * @return bool 341 */ 342 protected function set_expired(): bool { 343 return $this->set_status(self::EXPIRED); 344 } 345 346 /** 347 * Set the status to "error". 348 * @return bool 349 */ 350 protected function set_error(): bool { 351 return $this->set_status(self::ERROR); 352 } 353 354 /** 355 * Set the configured api key value. 356 * @param string $keyvalue 357 * @return bool 358 */ 359 protected function set_api_key(string $keyvalue): bool { 360 $this->apikey = $keyvalue; 361 return set_config(self::API_KEY, $keyvalue, manager::PLUGINNAME); 362 } 363 364 /** 365 * Set the configured secret key value. 366 * @param string $keyvalue 367 * @return bool 368 */ 369 protected function set_secret_key(string $keyvalue): bool { 370 $this->secretkey = $keyvalue; 371 return set_config(self::SECRET_KEY, $keyvalue, manager::PLUGINNAME); 372 } 373 374 /** 375 * Return true if the logic says that the registration is valid. 376 * @return bool 377 */ 378 protected function status_is_validated(): bool { 379 return $this->validation == self::VALIDATED; 380 } 381 382 /** 383 * Return true if the current status is "not entered". 384 * @return bool 385 */ 386 protected function status_is_not_entered(): bool { 387 return $this->validation == self::NOT_ENTERED; 388 } 389 390 /** 391 * Return true if the current status is "pending". 392 * @return bool 393 */ 394 protected function status_is_pending(): bool { 395 return $this->validation == self::PENDING; 396 } 397 398 /** 399 * Return true if the current status is "expired". 400 * @return bool 401 */ 402 protected function status_is_expired(): bool { 403 return $this->validation == self::EXPIRED; 404 } 405 406 /** 407 * Return true if the current status is "invalid". 408 * @return bool 409 */ 410 protected function status_is_invalid(): bool { 411 return $this->validation == self::INVALID; 412 } 413 414 /** 415 * Return true if the current status is "error". 416 * @return bool 417 */ 418 protected function status_is_error() { 419 return $this->validation == self::ERROR; 420 } 421 422 /** 423 * Set the current registration status. 424 * @param int $status 425 * @return bool 426 */ 427 protected function set_status(int $status): bool { 428 $this->validation = $status; 429 return set_config(self::STATUS, $status, manager::PLUGINNAME); 430 } 431 432 /** 433 * Return the current registration status. 434 * @return int 435 * @throws \dml_exception 436 */ 437 protected function get_status(): int { 438 $status = get_config(manager::PLUGINNAME, self::STATUS); 439 if ($status === false) { 440 // Not set in config yet, so default it to "NOT_ENTERED". 441 $status = self::NOT_ENTERED; 442 $this->set_status($status); 443 } 444 return (int)$status; 445 } 446 447 /** 448 * Set the time of the last registration check. 449 * @param int $time 450 * @return bool 451 */ 452 protected function set_check_time(int $time = 0): bool { 453 $time = ($time == 0) ? time() : $time; 454 $this->checktime = $time; 455 return set_config(self::VALIDATION_CHECK_TIME, $time, manager::PLUGINNAME); 456 } 457 458 /** 459 * Get the time of the last registration check. 460 * @return int 461 * @throws \dml_exception 462 */ 463 protected function get_check_time(): int { 464 $time = get_config(manager::PLUGINNAME, self::VALIDATION_CHECK_TIME); 465 if ($time === false) { 466 // Not set in config yet, so default it to 0. 467 $time = 0; 468 $this->set_check_time($time); 469 } 470 return (int)$time; 471 } 472 473 /** 474 * Set the registration validation time. 475 * @param int $time 476 * @return bool 477 */ 478 protected function set_validation_time(int $time = 0): bool { 479 $time = ($time == 0) ? time() : $time; 480 $this->validationtime = $time; 481 return set_config(self::VALIDATION_TIME, $time, manager::PLUGINNAME); 482 } 483 484 /** 485 * Return the time of the registration validation. 486 * @return int 487 * @throws \dml_exception 488 */ 489 protected function get_validation_time(): int { 490 $time = get_config(manager::PLUGINNAME, self::VALIDATION_TIME); 491 if ($time === false) { 492 // Not set in config yet, so default it to 0. 493 $time = 0; 494 $this->set_validation_time($time); 495 } 496 return (int)$time; 497 } 498 499 /** 500 * Set the time of the summary update. 501 * @param int $time 502 * @return bool 503 */ 504 protected function set_summary_time(int $time = 0): bool { 505 $time = ($time == 0) ? time() : $time; 506 $this->summarytime = $time; 507 return set_config(self::SUMMARY_TIME, $time, manager::PLUGINNAME); 508 } 509 510 /** 511 * Return the time of the last summary update. 512 * @return int 513 * @throws \dml_exception 514 */ 515 protected function get_summary_time(): int { 516 $time = get_config(manager::PLUGINNAME, self::SUMMARY_TIME); 517 if ($time === false) { 518 // Not set in config yet, so default it to 0. 519 $time = 0; 520 $this->set_summary_time($time); 521 } 522 return (int)$time; 523 } 524 525 /** 526 * Return true if all keys have valid format. 527 * @param string|null $apikey 528 * @param string|null $secretkey 529 * @return bool 530 */ 531 protected function keys_are_valid(?string $apikey = null, ?string $secretkey = null): bool { 532 $apikey = $apikey ?? $this->apikey; 533 $secretkey = $secretkey ?? $this->secretkey; 534 return $this->apikey_is_valid($apikey) && $this->secretkey_is_valid($secretkey); 535 } 536 537 /** 538 * Validates that the entered API key is in the expected format. 539 * @param string $apikey 540 * @return bool 541 */ 542 protected function apikey_is_valid(string $apikey): bool { 543 return $this->valid_key_format($apikey); 544 } 545 546 /** 547 * Validates that the entered Secret key is in the expected format. 548 * @param string $secretkey 549 * @return bool 550 */ 551 protected function secretkey_is_valid(string $secretkey): bool { 552 return $this->valid_key_format($secretkey); 553 } 554 555 /** 556 * Validates that the passed in key looks like an MD5 hash. 557 * @param string $key 558 * @return bool 559 */ 560 protected function valid_key_format(string $key): bool { 561 return !empty($key) && (preg_match('/^[a-f0-9]{32}$/', $key) === 1); 562 } 563 564 /** 565 * Get the registration grace period. 566 * @return int 567 */ 568 protected function get_grace_period(): int { 569 return WEEKSECS; 570 } 571 572 /** 573 * Check if the unvalidated time is still within the grace period. 574 * @return bool 575 */ 576 protected function grace_period_valid(): bool { 577 return (time() - $this->validationtime) < $this->get_grace_period(); 578 } 579 580 /** 581 * Check if the last time the summary data was sent is within the grace period. 582 * @return bool 583 */ 584 protected function summarydata_grace_period_expired(): bool { 585 return (time() - $this->summarytime) > $this->get_grace_period(); 586 } 587 588 /** 589 * Return an instance of the connection class. 590 * @return brickfieldconnect 591 */ 592 protected function get_registration_connection(): brickfieldconnect { 593 return new brickfieldconnect(); 594 } 595 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body