Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  /*
   6   * The MIT License (MIT)
   7   *
   8   * Copyright (c) 2014-2018 Spomky-Labs
   9   *
  10   * This software may be modified and distributed under the terms
  11   * of the MIT license.  See the LICENSE file for details.
  12   */
  13  
  14  namespace OTPHP;
  15  
  16  use Assert\Assertion;
  17  use ParagonIE\ConstantTime\Base32;
  18  
  19  trait ParameterTrait
  20  {
  21      /**
  22       * @var array
  23       */
  24      private $parameters = [];
  25  
  26      /**
  27       * @var string|null
  28       */
  29      private $issuer = null;
  30  
  31      /**
  32       * @var string|null
  33       */
  34      private $label = null;
  35  
  36      /**
  37       * @var bool
  38       */
  39      private $issuer_included_as_parameter = true;
  40  
  41      /**
  42       * @return array
  43       */
  44      public function getParameters(): array
  45      {
  46          $parameters = $this->parameters;
  47  
  48          if (null !== $this->getIssuer() && $this->isIssuerIncludedAsParameter() === true) {
  49              $parameters['issuer'] = $this->getIssuer();
  50          }
  51  
  52          return $parameters;
  53      }
  54  
  55      /**
  56       * @return string
  57       */
  58      public function getSecret(): string
  59      {
  60          return $this->getParameter('secret');
  61      }
  62  
  63      /**
  64       * @param string|null $secret
  65       */
  66      private function setSecret($secret)
  67      {
  68          $this->setParameter('secret', $secret);
  69      }
  70  
  71      /**
  72       * @return string|null
  73       */
  74      public function getLabel()
  75      {
  76          return $this->label;
  77      }
  78  
  79      /**
  80       * @param string $label
  81       */
  82      public function setLabel(string $label)
  83      {
  84          $this->setParameter('label', $label);
  85      }
  86  
  87      /**
  88       * @return string|null
  89       */
  90      public function getIssuer()
  91      {
  92          return $this->issuer;
  93      }
  94  
  95      /**
  96       * @param string $issuer
  97       */
  98      public function setIssuer(string $issuer)
  99      {
 100          $this->setParameter('issuer', $issuer);
 101      }
 102  
 103      /**
 104       * @return bool
 105       */
 106      public function isIssuerIncludedAsParameter(): bool
 107      {
 108          return $this->issuer_included_as_parameter;
 109      }
 110  
 111      /**
 112       * @param bool $issuer_included_as_parameter
 113       */
 114      public function setIssuerIncludedAsParameter(bool $issuer_included_as_parameter)
 115      {
 116          $this->issuer_included_as_parameter = $issuer_included_as_parameter;
 117      }
 118  
 119      /**
 120       * @return int
 121       */
 122      public function getDigits(): int
 123      {
 124          return $this->getParameter('digits');
 125      }
 126  
 127      /**
 128       * @param int $digits
 129       */
 130      private function setDigits(int $digits)
 131      {
 132          $this->setParameter('digits', $digits);
 133      }
 134  
 135      /**
 136       * @return string
 137       */
 138      public function getDigest(): string
 139      {
 140          return $this->getParameter('algorithm');
 141      }
 142  
 143      /**
 144       * @param string $digest
 145       */
 146      private function setDigest(string $digest)
 147      {
 148          $this->setParameter('algorithm', $digest);
 149      }
 150  
 151      /**
 152       * @param string $parameter
 153       *
 154       * @return bool
 155       */
 156      public function hasParameter(string $parameter): bool
 157      {
 158          return array_key_exists($parameter, $this->parameters);
 159      }
 160  
 161      /**
 162       * @param string $parameter
 163       *
 164       * @return mixed
 165       */
 166      public function getParameter(string $parameter)
 167      {
 168          if ($this->hasParameter($parameter)) {
 169              return $this->getParameters()[$parameter];
 170          }
 171  
 172          throw new \InvalidArgumentException(sprintf('Parameter "%s" does not exist', $parameter));
 173      }
 174  
 175      /**
 176       * @param string $parameter
 177       * @param mixed  $value
 178       */
 179      public function setParameter(string $parameter, $value)
 180      {
 181          $map = $this->getParameterMap();
 182  
 183          if (true === array_key_exists($parameter, $map)) {
 184              $callback = $map[$parameter];
 185              $value = $callback($value);
 186          }
 187  
 188          if (property_exists($this, $parameter)) {
 189              $this->$parameter = $value;
 190          } else {
 191              $this->parameters[$parameter] = $value;
 192          }
 193      }
 194  
 195      /**
 196       * @return array
 197       */
 198      protected function getParameterMap(): array
 199      {
 200          return [
 201              'label'     => function ($value) {
 202                  Assertion::false($this->hasColon($value), 'Label must not contain a colon.');
 203  
 204                  return $value;
 205              },
 206              'secret'    => function ($value) {
 207                  if (null === $value) {
 208                      $value = Base32::encodeUpper(random_bytes(64));
 209                  }
 210                  $value = trim(strtoupper($value), '=');
 211  
 212                  return $value;
 213              },
 214              'algorithm' => function ($value) {
 215                  $value = strtolower($value);
 216                  Assertion::inArray($value, hash_algos(), sprintf('The "%s" digest is not supported.', $value));
 217  
 218                  return $value;
 219              },
 220              'digits'    => function ($value) {
 221                  Assertion::greaterThan($value, 0, 'Digits must be at least 1.');
 222  
 223                  return (int) $value;
 224              },
 225              'issuer'    => function ($value) {
 226                  Assertion::false($this->hasColon($value), 'Issuer must not contain a colon.');
 227  
 228                  return $value;
 229              },
 230          ];
 231      }
 232  
 233      /**
 234       * @param string $value
 235       *
 236       * @return bool
 237       */
 238      private function hasColon($value)
 239      {
 240          $colons = [':', '%3A', '%3a'];
 241          foreach ($colons as $colon) {
 242              if (false !== strpos($value, $colon)) {
 243                  return true;
 244              }
 245          }
 246  
 247          return false;
 248      }
 249  }