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