Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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  // This file is part of BasicLTI4Moodle
  18  //
  19  // BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
  20  // consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
  21  // based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
  22  // specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
  23  // are already supporting or going to support BasicLTI. This project Implements the consumer
  24  // for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
  25  // BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
  26  // at the GESSI research group at UPC.
  27  // SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
  28  // by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
  29  // Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
  30  //
  31  // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
  32  // of the Universitat Politecnica de Catalunya http://www.upc.edu
  33  // Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu.
  34  
  35  namespace mod_lti\local\ltiopenid;
  36  
  37  /**
  38   * Tests for the jwks_helper class.
  39   *
  40   * @coversDefaultClass \mod_lti\local\ltiopenid\jwks_helper
  41   * @package    mod_lti
  42   * @copyright  2023 Jake Dallimore <jrhdallimore@gmail.com>
  43   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   */
  45  class jwks_helper_test extends \basic_testcase {
  46  
  47      /**
  48       * Test the fix_jwks_alg method with a range of inputs.
  49       *
  50       * @dataProvider jwks_alg_provider
  51       * @covers ::fix_jwks_alg
  52       * @param array $jwks the JWKS key set.
  53       * @param string $jwt the JWT.
  54       * @param array $expected the expected outputs/exceptions.
  55       * @return void
  56       */
  57      public function test_fix_jwks_alg(array $jwks, string $jwt, array $expected) {
  58          if (isset($expected['exception'])) {
  59              $this->expectException($expected['exception']);
  60          }
  61          $fixed = jwks_helper::fix_jwks_alg($jwks, $jwt);
  62          $this->assertEquals($expected['jwks'], $fixed);
  63      }
  64  
  65      /**
  66       * Provider for test_fix_jwks_alg.
  67       * @return array test data.
  68       */
  69      public function jwks_alg_provider(): array {
  70          return [
  71              // Algs already present, so no changes to input key array.
  72              'All JWKS keys have algs set' => [
  73                  'jwks' => [
  74                      'keys' => [
  75                          [
  76                              'kty' => 'RSA',
  77                              'use' => 'sig',
  78                              'e' => 'AQAB',
  79                              'n' => '3nVf6',
  80                              'kid' => '41',
  81                              'alg' => 'RS256'
  82                          ],
  83                          [
  84                              'kty' => 'RSA',
  85                              'use' => 'sig',
  86                              'e' => 'AQAB',
  87                              'n' => '3nVf6',
  88                              'kid' => '42',
  89                              'alg' => 'RS256'
  90                          ]
  91                      ]
  92                  ],
  93                  // RS256 JWT with kid = 42.
  94                  'jwt' => 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjQyIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRoZSBjYXQiLCJz'.
  95                      'bG9nYW4iOiJMb3ZlcyBpdCwgbG92ZXMgaXQsIGxvdmVzIE1vb2RsZSIsImlhdCI6MTUxNjIzOTAyMn0.EiqMEqufKJj74JevdTxXqzHvHGIcZ'.
  96                      'EFYhOe9sliL2FmlyiJcf7waObO2ZNwWvVZwTI4DfEGFamheMmTb6-YBODacDvH6BlQNb0H_6ye6AGl1u-3OAQj7i_SKsLuB37k6Lw5YFrwQYr'.
  97                      '7bjujSaQx6BL3kaqkqCdZhFjr2EYcn5-NehGHsevKqpMA-ShBovcndYkD5gfZEbXr59sgpQuJ43qO7gnGPzRbaJAEw_0_6v0r3y0pzDNfarNd'.
  98                      'fHfCZQbcF9T8dpHAeO4JMmuCanV8iJziI8ihVPwH-BwUJmzthyUgy8542FinHVbXo-88wu9xpbdV17VPgeGGBCpYpnVnWaA',
  99                  'expected' => [
 100                      'jwks' => [
 101                          'keys' => [
 102                              [
 103                                  'kty' => 'RSA',
 104                                  'use' => 'sig',
 105                                  'e' => 'AQAB',
 106                                  'n' => '3nVf6',
 107                                  'kid' => '41',
 108                                  'alg' => 'RS256'
 109                              ],
 110                              [
 111                                  'kty' => 'RSA',
 112                                  'use' => 'sig',
 113                                  'e' => 'AQAB',
 114                                  'n' => '3nVf6',
 115                                  'kid' => '42',
 116                                  'alg' => 'RS256'
 117                              ]
 118                          ]
 119                      ]
 120                  ]
 121              ],
 122              // Only the key matching the kid in the JWT header should be fixed.
 123              'All JWKS keys missing alg' => [
 124                  'jwks' => [
 125                      'keys' => [
 126                          [
 127                              'kty' => 'RSA',
 128                              'use' => 'sig',
 129                              'e' => 'AQAB',
 130                              'n' => '3nVf6',
 131                              'kid' => '41',
 132                          ],
 133                          [
 134                              'kty' => 'RSA',
 135                              'use' => 'sig',
 136                              'e' => 'AQAB',
 137                              'n' => '3nVf6',
 138                              'kid' => '42',
 139                          ]
 140                      ]
 141                  ],
 142                  // RS256 JWT with kid = 42.
 143                  'jwt' => 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjQyIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRoZSBjYXQiLCJz'.
 144                      'bG9nYW4iOiJMb3ZlcyBpdCwgbG92ZXMgaXQsIGxvdmVzIE1vb2RsZSIsImlhdCI6MTUxNjIzOTAyMn0.EiqMEqufKJj74JevdTxXqzHvHGIcZ'.
 145                      'EFYhOe9sliL2FmlyiJcf7waObO2ZNwWvVZwTI4DfEGFamheMmTb6-YBODacDvH6BlQNb0H_6ye6AGl1u-3OAQj7i_SKsLuB37k6Lw5YFrwQYr'.
 146                      '7bjujSaQx6BL3kaqkqCdZhFjr2EYcn5-NehGHsevKqpMA-ShBovcndYkD5gfZEbXr59sgpQuJ43qO7gnGPzRbaJAEw_0_6v0r3y0pzDNfarNd'.
 147                      'fHfCZQbcF9T8dpHAeO4JMmuCanV8iJziI8ihVPwH-BwUJmzthyUgy8542FinHVbXo-88wu9xpbdV17VPgeGGBCpYpnVnWaA',
 148                  'expected' => [
 149                      'jwks' => [
 150                          'keys' => [
 151                              [
 152                                  'kty' => 'RSA',
 153                                  'use' => 'sig',
 154                                  'e' => 'AQAB',
 155                                  'n' => '3nVf6',
 156                                  'kid' => '41',
 157                              ],
 158                              [
 159                                  'kty' => 'RSA',
 160                                  'use' => 'sig',
 161                                  'e' => 'AQAB',
 162                                  'n' => '3nVf6',
 163                                  'kid' => '42',
 164                                  'alg' => 'RS256'
 165                              ]
 166                          ]
 167                      ]
 168                  ]
 169              ],
 170              // Exception expected when JWT alg is supported but does not match the family of key in the JWK.
 171              'JWT kty algorithm family mismatch' => [
 172                  'jwks' => [
 173                      'keys' => [
 174                          [
 175                              'kty' => 'RSA',
 176                              'use' => 'sig',
 177                              'e' => 'AQAB',
 178                              'n' => '3nVf6',
 179                              'kid' => '41',
 180                          ],
 181                          [
 182                              'kty' => 'RSA',
 183                              'use' => 'sig',
 184                              'e' => 'AQAB',
 185                              'n' => '3nVf6',
 186                              'kid' => '42',
 187                          ]
 188                      ]
 189                  ],
 190                  // ES256 JWT with kid = 42.
 191                  'jwt' => 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjQyIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwi'.
 192                      'YWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.dbUlZopFo7164JVLD0G4GoZOhoMYWhIXkgtlblBXT6fC3K4lJ38l3LzlEBhfRRKvJlXpe'.
 193                      'NNGmBg8V29jd5J33Q',
 194                  'expected' => [
 195                      'exception' => \moodle_exception::class
 196                  ]
 197              ],
 198              // Exception expected when JWK kid field missing.
 199              'JWT missing kid header field' => [
 200                  'jwks' => [
 201                      'keys' => [
 202                          [
 203                              'kty' => 'RSA',
 204                              'use' => 'sig',
 205                              'e' => 'AQAB',
 206                              'n' => '3nVf6',
 207                              'kid' => '41',
 208                              'alg' => 'RS256'
 209                          ],
 210                          [
 211                              'kty' => 'RSA',
 212                              'use' => 'sig',
 213                              'e' => 'AQAB',
 214                              'n' => '3nVf6',
 215                              'kid' => '42',
 216                              'alg' => 'RS256'
 217                          ]
 218                      ]
 219                  ],
 220                  // RS256 JWT with kid omitted.
 221                  'jwt' => 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWU'.
 222                      'sImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4'.
 223                      'SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygT'.
 224                      'qVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9Riwr'.
 225                      'V7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ',
 226                  'expected' => [
 227                      'exception' => \moodle_exception::class
 228                  ]
 229              ],
 230              // Exception expected when JWT passes unsupported symmetrical alg.
 231              'JWT passes in unsupported alg' => [
 232                  'jwks' => [
 233                      'keys' => [
 234                          [
 235                              'kty' => 'RSA',
 236                              'use' => 'sig',
 237                              'e' => 'AQAB',
 238                              'n' => '3nVf6',
 239                              'kid' => '41',
 240                          ],
 241                          [
 242                              'kty' => 'RSA',
 243                              'use' => 'sig',
 244                              'e' => 'AQAB',
 245                              'n' => '3nVf6',
 246                              'kid' => '42',
 247                          ]
 248                      ]
 249                  ],
 250                  // HS256 JWT with kid = 42.
 251                  'jwt' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjQyIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRoZSBjYXQiLCJz'.
 252                      'bG9nYW4iOiJMb3ZlcyBpdCwgbG92ZXMgaXQsIGxvdmVzIE1vb2RsZSIsImlhdCI6MTUxNjIzOTAyMn0.zBM5Jw0BOig5-C1R7TD-TzH1QVmyD'.
 253                      'yMjbK0KGG76xIE',
 254                  'expected' => [
 255                      'exception' => \moodle_exception::class
 256                  ]
 257              ],
 258          ];
 259      }
 260  }