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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body