xref: /dokuwiki/_test/tests/Remote/ApiTest.php (revision cad27e80a983b270a13cd79a42d90d4e82d90c83)
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