1<?php 2 3namespace dokuwiki\test\Remote; 4 5use dokuwiki\Remote\AccessDeniedException; 6use dokuwiki\Remote\ApiCall; 7use dokuwiki\test\mock\AuthPlugin; 8use dokuwiki\Extension\RemotePlugin; 9use dokuwiki\Remote\Api; 10use dokuwiki\Remote\RemoteException; 11 12class ApiTest extends \DokuWikiTest { 13 14 protected $userinfo; 15 16 /** @var Api */ 17 protected $remote; 18 19 public function setUp() : void { 20 parent::setUp(); 21 global $plugin_controller; 22 global $conf; 23 global $USERINFO; 24 global $auth; 25 26 parent::setUp(); 27 28 // mock plugin controller to return our test plugins 29 $pluginManager = $this->createMock('dokuwiki\Extension\PluginController'); 30 $pluginManager->method('getList')->willReturn(array('testplugin', 'testplugin2')); 31 $pluginManager->method('load')->willReturnCallback( 32 function($type, $plugin) { 33 if($plugin == 'testplugin2') { 34 return new remote_plugin_testplugin2(); 35 } else { 36 return new remote_plugin_testplugin(); 37 } 38 } 39 ); 40 $plugin_controller = $pluginManager; 41 42 $conf['remote'] = 1; 43 $conf['remoteuser'] = '!!not set!!'; 44 $conf['useacl'] = 0; 45 46 $this->userinfo = $USERINFO; 47 $this->remote = new Api(); 48 49 $auth = new AuthPlugin(); 50 } 51 52 public function tearDown() : void { 53 global $USERINFO; 54 $USERINFO = $this->userinfo; 55 56 } 57 58 public function testPluginMethods() { 59 $methods = $this->remote->getPluginMethods(); 60 $actual = array_keys($methods); 61 sort($actual); 62 $expect = array( 63 'plugin.testplugin.method1', 64 'plugin.testplugin.method2', 65 'plugin.testplugin.methodString', 66 'plugin.testplugin.method2ext', 67 'plugin.testplugin.publicCall', 68 69 'plugin.testplugin2.commented' 70 ); 71 sort($expect); 72 $this->assertEquals($expect,$actual); 73 } 74 75 public function testPluginDescriptors() { 76 $methods = $this->remote->getPluginMethods(); 77 $this->assertEquals( 78 [ 79 'str' => [ 80 'type' => 'string', 81 'description' => 'some more parameter description', 82 ], 83 'int' => [ 84 'type' => 'int', 85 'description' => '', 86 ], 87 'bool' => [ 88 'type' => 'bool', 89 'description' => '', 90 ], 91 'unknown' => [ 92 'type' => 'string', 93 'description' => '', 94 ], 95 ], 96 $methods['plugin.testplugin2.commented']->getArgs() 97 ); 98 99 $this->assertEquals( 100 [ 101 'type' => 'array', 102 'description' => '', 103 ], 104 $methods['plugin.testplugin2.commented']->getReturn() 105 ); 106 107 $this->assertEquals(0, $methods['plugin.testplugin2.commented']->isPublic()); 108 $this->assertStringContainsString( 109 'This is a dummy method', 110 $methods['plugin.testplugin2.commented']->getSummary() 111 ); 112 } 113 114 public function testHasAccessSuccess() 115 { 116 global $conf; 117 $conf['remoteuser'] = ''; 118 119 $this->remote->ensureAccessIsAllowed(new ApiCall('time')); 120 $this->assertTrue(true); 121 } 122 123 public function testHasAccessFailAcl() { 124 global $conf; 125 $conf['useacl'] = 1; 126 127 $this->expectException(AccessDeniedException::class); 128 $this->remote->ensureAccessIsAllowed(new ApiCall('time')); 129 } 130 131 public function testHasAccessSuccessAclEmptyRemoteUser() { 132 global $conf; 133 $conf['useacl'] = 1; 134 $conf['remoteuser'] = ''; 135 136 $this->remote->ensureAccessIsAllowed(new ApiCall('time')); 137 $this->assertTrue(true); 138 } 139 140 public function testHasAccessSuccessAcl() { 141 global $conf; 142 global $USERINFO; 143 $conf['useacl'] = 1; 144 $conf['remoteuser'] = '@grp,@grp2'; 145 $USERINFO['grps'] = array('grp'); 146 147 $this->remote->ensureAccessIsAllowed(new ApiCall('time')); 148 $this->assertTrue(true); 149 } 150 151 public function testHasAccessFailAcl2() { 152 global $conf; 153 global $USERINFO; 154 $conf['useacl'] = 1; 155 $conf['remoteuser'] = '@grp'; 156 $USERINFO['grps'] = array('grp1'); 157 158 $this->expectException(AccessDeniedException::class); 159 $this->remote->ensureAccessIsAllowed(new ApiCall('time')); 160 } 161 162 public function testIsEnabledFail1() { 163 global $conf; 164 $conf['remote'] = 0; 165 $this->expectException(AccessDeniedException::class); 166 $this->remote->ensureApiIsEnabled(); 167 } 168 169 public function testIsEnabledFail2() { 170 global $conf; 171 $conf['remoteuser'] = '!!not set!!'; 172 $this->expectException(AccessDeniedException::class); 173 $this->remote->ensureApiIsEnabled(); 174 } 175 176 public function testIsEnabledSuccess() { 177 global $conf; 178 $conf['remote'] = 1; 179 $conf['remoteuser'] = ''; 180 $this->remote->ensureApiIsEnabled(); 181 $this->assertTrue(true); 182 } 183 184 185 public function testGeneralCoreFunctionWithoutArguments() { 186 global $conf; 187 global $USERINFO; 188 $conf['remote'] = 1; 189 $conf['remoteuser'] = ''; 190 $conf['useacl'] = 1; 191 $USERINFO['grps'] = array('grp'); 192 $remoteApi = new Api(); 193 $remoteApi->getCoreMethods(new MockApiCore()); 194 195 $this->assertEquals($remoteApi->call('wiki.stringTestMethod'), 'success'); 196 $this->assertEquals($remoteApi->call('wiki.intTestMethod'), 42); 197 $this->assertEquals($remoteApi->call('wiki.floatTestMethod'), 3.14159265); 198 $this->assertEquals($remoteApi->call('wiki.dateTestMethod'), 2623452346); 199 $this->assertEquals($remoteApi->call('wiki.fileTestMethod'), 'file content'); 200 $this->assertEquals($remoteApi->call('wiki.voidTestMethod'), null); 201 } 202 203 public function testGeneralCoreFunctionOnArgumentMismatch() { 204 global $conf; 205 $conf['remote'] = 1; 206 $remoteApi = new Api(); 207 $remoteApi->getCoreMethods(new MockApiCore()); 208 209 try { 210 $remoteApi->call('wiki.voidTestMethod', array('something')); 211 $this->fail('Expects RemoteException to be raised'); 212 } catch (RemoteException $th) { 213 $this->assertEquals(-32604, $th->getCode()); 214 } 215 } 216 217 public function testGeneralCoreFunctionWithArguments() { 218 global $conf; 219 global $USERINFO; 220 $conf['remote'] = 1; 221 $conf['remoteuser'] = ''; 222 $conf['useacl'] = 1; 223 224 $remoteApi = new Api(); 225 $remoteApi->getCoreMethods(new MockApiCore()); 226 227 $this->assertEquals($remoteApi->call('wiki.oneStringArgMethod', array('string')), 'string'); 228 $this->assertEquals($remoteApi->call('wiki.twoArgMethod', array('string', 1)), array('string' , 1)); 229 $this->assertEquals($remoteApi->call('wiki.twoArgWithDefaultArg', array('string')), array('string', 'default')); 230 $this->assertEquals($remoteApi->call('wiki.twoArgWithDefaultArg', array('string', 'another')), array('string', 'another')); 231 } 232 233 public function testGeneralCoreFunctionOnArgumentMissing() { 234 global $conf; 235 $conf['remote'] = 1; 236 $conf['remoteuser'] = ''; 237 $remoteApi = new Api(); 238 $remoteApi->getCoreMethods(new MockApiCore()); 239 240 try { 241 $remoteApi->call('wiki.twoArgWithDefaultArg', array()); 242 $this->fail('Expects RemoteException to be raised'); 243 } catch (RemoteException $th) { 244 $this->assertEquals(-32603, $th->getCode()); 245 } 246 } 247 248 public function testPluginCallMethods() { 249 global $conf; 250 global $USERINFO; 251 $conf['remote'] = 1; 252 $conf['remoteuser'] = ''; 253 $conf['useacl'] = 1; 254 255 $remoteApi = new Api(); 256 $this->assertEquals($remoteApi->call('plugin.testplugin.method1'), null); 257 $this->assertEquals($remoteApi->call('plugin.testplugin.method2', array('string', 7)), array('string', 7, false)); 258 $this->assertEquals($remoteApi->call('plugin.testplugin.method2ext', array('string', 7, true)), array('string', 7, true)); 259 $this->assertEquals($remoteApi->call('plugin.testplugin.methodString'), 'success'); 260 } 261 262 public function testPluginCallMethodsOnArgumentMissing() { 263 global $conf; 264 $conf['remote'] = 1; 265 $conf['remoteuser'] = ''; 266 $remoteApi = new Api(); 267 $remoteApi->getCoreMethods(new MockApiCore()); 268 269 try { 270 $remoteApi->call('plugin.testplugin.method2', array()); 271 $this->fail('Expects RemoteException to be raised'); 272 } catch (RemoteException $th) { 273 $this->assertEquals(-32603, $th->getCode()); 274 } 275 } 276 277 public function testNotExistingCall() { 278 global $conf; 279 $conf['remote'] = 1; 280 $conf['remoteuser'] = ''; 281 282 $this->expectException(RemoteException::class); 283 $this->expectExceptionCode(-32603); 284 285 $remoteApi = new Api(); 286 $remoteApi->call('invalid method'); // no '.' 287 $remoteApi->call('does.not exist'); // unknown method type 288 } 289 290 public function testPublicCallCore() { 291 global $conf; 292 $conf['useacl'] = 1; 293 $conf['remote'] = 1; 294 $conf['remoteuser'] = 'restricted'; 295 $remoteApi = new Api(); 296 $remoteApi->getCoreMethods(new MockApiCore()); 297 $this->assertTrue($remoteApi->call('wiki.publicCall')); 298 } 299 300 public function testPublicCallPlugin() { 301 global $conf; 302 $conf['useacl'] = 1; 303 $conf['remote'] = 1; 304 $conf['remoteuser'] = 'restricted'; 305 $remoteApi = new Api(); 306 $this->assertTrue($remoteApi->call('plugin.testplugin.publicCall')); 307 } 308 309 public function testPublicCallCoreDeny() { 310 global $conf; 311 $conf['useacl'] = 1; 312 $this->expectException(AccessDeniedException::class); 313 $remoteApi = new Api(); 314 $remoteApi->getCoreMethods(new MockApiCore()); 315 $remoteApi->call('wiki.stringTestMethod'); 316 } 317 318 public function testPublicCallPluginDeny() { 319 global $conf; 320 $conf['useacl'] = 1; 321 $this->expectException(AccessDeniedException::class); 322 $remoteApi = new Api(); 323 $remoteApi->call('plugin.testplugin.methodString'); 324 } 325} 326 327 328 329 330class remote_plugin_testplugin extends RemotePlugin { 331 function getMethods() { 332 $methods = parent::getMethods(); // auto add all methods, then adjust 333 334 $methods['method2']->limitArgs(['str', 'int']); // skip optional parameter 335 $methods['method2ext'] = new ApiCall([$this, 'method2']); // add second call with all params 336 $methods['publicCall']->setPublic(); // make this one public 337 338 return $methods; 339 } 340 341 function method1() { return null; } 342 function methodString() { return 'success'; } 343 function method2($str, $int, $bool = false) { return array($str, $int, $bool); } 344 function publicCall() {return true;} 345} 346 347class remote_plugin_testplugin2 extends RemotePlugin { 348 /** 349 * This is a dummy method 350 * 351 * @param string $str some more parameter description 352 * @param int $int 353 * @param bool $bool 354 * @param Object $unknown 355 * @return array 356 */ 357 public function commented($str, $int, $bool, $unknown) { return array($str, $int, $bool); } 358 359 private function privateMethod() {return true;} 360 protected function protectedMethod() {return true;} 361 public function _underscore() {return true;} 362} 363