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/** 18 * Tests for oauth2 apis (\core\oauth2\*). 19 * 20 * @package core 21 * @copyright 2017 Damyon Wiese 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. 23 */ 24 25defined('MOODLE_INTERNAL') || die(); 26 27/** 28 * Tests for oauth2 apis (\core\oauth2\*). 29 * 30 * @package core 31 * @copyright 2017 Damyon Wiese 32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. 33 */ 34class core_oauth2_testcase extends advanced_testcase { 35 36 /** 37 * Tests the crud operations on oauth2 issuers. 38 */ 39 public function test_create_and_delete_standard_issuers() { 40 $this->resetAfterTest(); 41 $this->setAdminUser(); 42 \core\oauth2\api::create_standard_issuer('google'); 43 \core\oauth2\api::create_standard_issuer('facebook'); 44 \core\oauth2\api::create_standard_issuer('microsoft'); 45 \core\oauth2\api::create_standard_issuer('nextcloud', 'https://dummy.local/nextcloud/'); 46 47 $issuers = \core\oauth2\api::get_all_issuers(); 48 49 $this->assertEquals($issuers[0]->get('name'), 'Google'); 50 $this->assertEquals($issuers[1]->get('name'), 'Facebook'); 51 $this->assertEquals($issuers[2]->get('name'), 'Microsoft'); 52 $this->assertEquals($issuers[3]->get('name'), 'Nextcloud'); 53 54 \core\oauth2\api::move_down_issuer($issuers[0]->get('id')); 55 56 $issuers = \core\oauth2\api::get_all_issuers(); 57 58 $this->assertEquals($issuers[0]->get('name'), 'Facebook'); 59 $this->assertEquals($issuers[1]->get('name'), 'Google'); 60 $this->assertEquals($issuers[2]->get('name'), 'Microsoft'); 61 $this->assertEquals($issuers[3]->get('name'), 'Nextcloud'); 62 63 \core\oauth2\api::delete_issuer($issuers[1]->get('id')); 64 65 $issuers = \core\oauth2\api::get_all_issuers(); 66 67 $this->assertEquals($issuers[0]->get('name'), 'Facebook'); 68 $this->assertEquals($issuers[1]->get('name'), 'Microsoft'); 69 $this->assertEquals($issuers[2]->get('name'), 'Nextcloud'); 70 } 71 72 /** 73 * Tests the crud operations on oauth2 issuers. 74 */ 75 public function test_create_nextcloud_without_url() { 76 $this->resetAfterTest(); 77 $this->setAdminUser(); 78 79 $this->expectException(\moodle_exception::class); 80 \core\oauth2\api::create_standard_issuer('nextcloud'); 81 } 82 83 /** 84 * Tests we can list and delete each of the persistents related to an issuer. 85 */ 86 public function test_getters() { 87 $this->resetAfterTest(); 88 $this->setAdminUser(); 89 $issuer = \core\oauth2\api::create_standard_issuer('microsoft'); 90 91 $same = \core\oauth2\api::get_issuer($issuer->get('id')); 92 93 foreach ($same->properties_definition() as $name => $def) { 94 $this->assertTrue($issuer->get($name) == $same->get($name)); 95 } 96 97 $endpoints = \core\oauth2\api::get_endpoints($issuer); 98 $same = \core\oauth2\api::get_endpoint($endpoints[0]->get('id')); 99 $this->assertEquals($endpoints[0]->get('id'), $same->get('id')); 100 $this->assertEquals($endpoints[0]->get('name'), $same->get('name')); 101 102 $todelete = $endpoints[0]; 103 \core\oauth2\api::delete_endpoint($todelete->get('id')); 104 $endpoints = \core\oauth2\api::get_endpoints($issuer); 105 $this->assertNotEquals($endpoints[0]->get('id'), $todelete->get('id')); 106 107 $userfields = \core\oauth2\api::get_user_field_mappings($issuer); 108 $same = \core\oauth2\api::get_user_field_mapping($userfields[0]->get('id')); 109 $this->assertEquals($userfields[0]->get('id'), $same->get('id')); 110 111 $todelete = $userfields[0]; 112 \core\oauth2\api::delete_user_field_mapping($todelete->get('id')); 113 $userfields = \core\oauth2\api::get_user_field_mappings($issuer); 114 $this->assertNotEquals($userfields[0]->get('id'), $todelete->get('id')); 115 } 116 117 /** 118 * Data provider for \core_oauth2_testcase::test_get_system_oauth_client(). 119 * 120 * @return array 121 */ 122 public function system_oauth_client_provider() { 123 return [ 124 [ 125 (object) [ 126 'access_token' => 'fdas...', 127 'token_type' => 'Bearer', 128 'expires_in' => '3600', 129 'id_token' => 'llfsd..', 130 ], HOURSECS - 10 131 ], 132 [ 133 (object) [ 134 'access_token' => 'fdas...', 135 'token_type' => 'Bearer', 136 'id_token' => 'llfsd..', 137 ], WEEKSECS 138 ], 139 ]; 140 } 141 142 /** 143 * Tests we can get a logged in oauth client for a system account. 144 * 145 * @dataProvider system_oauth_client_provider 146 * @param stdClass $responsedata The response data to be mocked. 147 * @param int $expiresin The expected expiration time. 148 */ 149 public function test_get_system_oauth_client($responsedata, $expiresin) { 150 $this->resetAfterTest(); 151 $this->setAdminUser(); 152 153 $issuer = \core\oauth2\api::create_standard_issuer('microsoft'); 154 155 $requiredscopes = \core\oauth2\api::get_system_scopes_for_issuer($issuer); 156 // Fake a system account. 157 $data = (object) [ 158 'issuerid' => $issuer->get('id'), 159 'refreshtoken' => 'abc', 160 'grantedscopes' => $requiredscopes, 161 'email' => 'sys@example.com', 162 'username' => 'sys' 163 ]; 164 $sys = new \core\oauth2\system_account(0, $data); 165 $sys->create(); 166 167 // Fake a response with an access token. 168 $response = json_encode($responsedata); 169 curl::mock_response($response); 170 $client = \core\oauth2\api::get_system_oauth_client($issuer); 171 $this->assertTrue($client->is_logged_in()); 172 173 // Check token expiry. 174 $accesstoken = \core\oauth2\access_token::get_record(['issuerid' => $issuer->get('id')]); 175 176 // Get the difference between the actual and expected expiry times. 177 // They might differ by a couple of seconds depending on the timing when the token gets actually processed. 178 $expiresdifference = time() + $expiresin - $accesstoken->get('expires'); 179 180 // Assert that the actual token expiration is more or less the same as the expected. 181 $this->assertGreaterThanOrEqual(0, $expiresdifference); 182 $this->assertLessThanOrEqual(3, $expiresdifference); 183 } 184 185 /** 186 * Tests we can enable and disable an issuer. 187 */ 188 public function test_enable_disable_issuer() { 189 $this->resetAfterTest(); 190 $this->setAdminUser(); 191 192 $issuer = \core\oauth2\api::create_standard_issuer('microsoft'); 193 194 $issuerid = $issuer->get('id'); 195 196 \core\oauth2\api::enable_issuer($issuerid); 197 $check = \core\oauth2\api::get_issuer($issuer->get('id')); 198 $this->assertTrue((boolean)$check->get('enabled')); 199 200 \core\oauth2\api::enable_issuer($issuerid); 201 $check = \core\oauth2\api::get_issuer($issuer->get('id')); 202 $this->assertTrue((boolean)$check->get('enabled')); 203 204 \core\oauth2\api::disable_issuer($issuerid); 205 $check = \core\oauth2\api::get_issuer($issuer->get('id')); 206 $this->assertFalse((boolean)$check->get('enabled')); 207 208 \core\oauth2\api::enable_issuer($issuerid); 209 $check = \core\oauth2\api::get_issuer($issuer->get('id')); 210 $this->assertTrue((boolean)$check->get('enabled')); 211 } 212 213 /** 214 * Test the alloweddomains for an issuer. 215 */ 216 public function test_issuer_alloweddomains() { 217 $this->resetAfterTest(); 218 $this->setAdminUser(); 219 220 $issuer = \core\oauth2\api::create_standard_issuer('microsoft'); 221 222 $issuer->set('alloweddomains', ''); 223 224 // Anything is allowed when domain is empty. 225 $this->assertTrue($issuer->is_valid_login_domain('')); 226 $this->assertTrue($issuer->is_valid_login_domain('a@b')); 227 $this->assertTrue($issuer->is_valid_login_domain('longer.example@example.com')); 228 229 $issuer->set('alloweddomains', 'example.com'); 230 231 // One domain - must match exactly - no substrings etc. 232 $this->assertFalse($issuer->is_valid_login_domain('')); 233 $this->assertFalse($issuer->is_valid_login_domain('a@b')); 234 $this->assertFalse($issuer->is_valid_login_domain('longer.example@example')); 235 $this->assertTrue($issuer->is_valid_login_domain('longer.example@example.com')); 236 237 $issuer->set('alloweddomains', 'example.com,example.net'); 238 // Multiple domains - must match any exactly - no substrings etc. 239 $this->assertFalse($issuer->is_valid_login_domain('')); 240 $this->assertFalse($issuer->is_valid_login_domain('a@b')); 241 $this->assertFalse($issuer->is_valid_login_domain('longer.example@example')); 242 $this->assertFalse($issuer->is_valid_login_domain('invalid@email@example.net')); 243 $this->assertTrue($issuer->is_valid_login_domain('longer.example@example.net')); 244 $this->assertTrue($issuer->is_valid_login_domain('longer.example@example.com')); 245 246 $issuer->set('alloweddomains', '*.example.com'); 247 // Wildcard. 248 $this->assertFalse($issuer->is_valid_login_domain('')); 249 $this->assertFalse($issuer->is_valid_login_domain('a@b')); 250 $this->assertFalse($issuer->is_valid_login_domain('longer.example@example')); 251 $this->assertFalse($issuer->is_valid_login_domain('longer.example@example.com')); 252 $this->assertTrue($issuer->is_valid_login_domain('longer.example@sub.example.com')); 253 } 254 255} 256