1<?php
2
3/**
4 * mysql tests for the authpdo plugin
5 *
6 * @group plugin_authpdo
7 * @group plugins
8 */
9class mysql_plugin_authpdo_test extends DokuWikiTest {
10
11    protected $driver = 'mysql';
12    protected $host = '';
13    protected $database = 'authpdo_testing';
14    protected $user = '';
15    protected $pass = '';
16    protected $port = '';
17
18    public function setUp() : void {
19        parent::setUp();
20        $configuration = DOKU_UNITTEST . "{$this->driver}.conf.php";
21        if(!file_exists($configuration)) {
22            return;
23        }
24        /** @var $conf array */
25        include $configuration;
26        $this->host = $conf['host'];
27        $this->user = $conf['user'];
28        $this->pass = $conf['pass'];
29        $this->port = $conf['port'];
30    }
31
32    /**
33     * try to remove the last set up database
34     *
35     * it might still be there if something went wrong
36     */
37    public function tearDown() : void {
38        parent::tearDown();
39        $this->dropDatabase();
40    }
41
42    /**
43     * Check if database credentials and extensions exist
44     */
45    public function test_requirements() {
46        if(!$this->host || !$this->user) {
47            $this->markTestSkipped("Skipped {$this->driver} tests. Missing configuration");
48        }
49        if(!class_exists('PDO')) {
50            $this->markTestSkipped("Skipped {$this->driver} tests. Missing PDO extension");
51        }
52        if(!in_array($this->driver, pdo_drivers())) {
53            $this->markTestSkipped("Skipped {$this->driver} tests. Missing pdo_{$this->driver} extension");
54        }
55        $this->assertTrue(true); // avoid being marked as risky for having no assertion
56    }
57
58    /**
59     * create the database for testing
60     */
61    protected function createDatabase() {
62        $pdo = new PDO(
63            "{$this->driver}:host={$this->host};port={$this->port}", $this->user, $this->pass,
64            array(
65                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes
66            )
67        );
68        $pdo->exec("DROP DATABASE IF EXISTS {$this->database}");
69        $pdo->exec("CREATE DATABASE {$this->database}");
70        $pdo = null;
71    }
72
73    /**
74     * remove the database
75     */
76    protected function dropDatabase() {
77        $pdo = new PDO(
78            "{$this->driver}:host={$this->host};port={$this->port}", $this->user, $this->pass,
79            array(
80                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes
81            )
82        );
83        try {
84            $pdo->exec("DROP DATABASE IF EXISTS {$this->database}");
85        } catch (PDOException $e) {
86            // ignore - sometimes this fails even though the database was deleted
87        }
88        $pdo = null;
89    }
90
91    /**
92     * imports a database dump
93     *
94     * @param $file
95     */
96    protected function importDatabase($file) {
97        // connect to database and import dump
98        $pdo = null;
99        $pdo = new PDO(
100            "{$this->driver}:dbname={$this->database};host={$this->host};port={$this->port}", $this->user, $this->pass,
101            array(
102                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes
103            )
104        );
105        $sql = file_get_contents($file);
106        $pdo->exec($sql);
107        $pdo = null;
108    }
109
110    /**
111     * Run general tests on all users
112     *
113     * @param auth_plugin_authpdo $auth
114     * @param array $users
115     */
116    protected function runGeneralTests(auth_plugin_authpdo $auth, $users) {
117        global $conf;
118        $info = 'DSN: ' . $auth->getConf('dsn');
119        $this->assertTrue($auth->success, $info);
120
121        if($auth->canDo('getUsers')) {
122            $list = $auth->retrieveUsers();
123            $this->assertGreaterThanOrEqual(count($users), count($list), $info);
124        }
125
126        if($auth->canDo('getGroups')) {
127            $list = $auth->retrieveGroups();
128            $this->assertGreaterThanOrEqual(1, $list, $info);
129        }
130
131        if($auth->canDo('getUserCount')) {
132            $count = $auth->getUserCount();
133            $this->assertGreaterThanOrEqual(count($users), $count);
134        }
135
136        if($auth->canDo('addUser')) {
137            $newuser = array(
138                'user' => 'newuserfoobar',
139                'name' => 'First LastFoobar',
140                'pass' => 'password',
141                'mail' => 'newuserfoobar@example.com',
142                'grps' => array('acompletelynewgroup')
143            );
144            $ok = $auth->createUser(
145                $newuser['user'],
146                $newuser['pass'],
147                $newuser['name'],
148                $newuser['mail'],
149                $newuser['grps']
150            );
151            $this->assertTrue($ok, $info);
152            $check = $auth->getUserData($newuser['user']);
153            $this->assertEquals($newuser['user'], $check['user'], $info);
154            $this->assertEquals($newuser['mail'], $check['mail'], $info);
155            $groups = array_merge($newuser['grps'], array($conf['defaultgroup']));
156            $this->assertEquals($groups, $check['grps'], $info);
157        }
158    }
159
160    /**
161     * run all the tests with the given user, depending on the capabilities
162     *
163     * @param auth_plugin_authpdo $auth
164     * @param $user
165     */
166    protected function runUserTests(auth_plugin_authpdo $auth, $user) {
167        global $conf;
168        $info = 'DSN: ' . $auth->getConf('dsn') . ' User:' . $user['user'];
169
170        // minimal setup
171        $this->assertTrue($auth->checkPass($user['user'], $user['pass']), $info);
172        $check = $auth->getUserData($user['user']);
173        $this->assertEquals($user['user'], $check['user'], $info);
174        $this->assertEquals($user['name'], $check['name'], $info);
175        $this->assertEquals($user['mail'], $check['mail'], $info);
176        $groups = array_merge($user['grps'], array($conf['defaultgroup']));
177        $this->assertEquals($groups, $check['grps'], $info);
178
179        // getUsers
180        if($auth->canDo('getUsers')) {
181            $list = $auth->retrieveUsers(0, -1, array('user' => $user['user']));
182            $this->assertGreaterThanOrEqual(1, count($list));
183            $list = $auth->retrieveUsers(0, -1, array('name' => $user['name']));
184            $this->assertGreaterThanOrEqual(1, count($list));
185            $list = $auth->retrieveUsers(0, -1, array('mail' => $user['mail']));
186            $this->assertGreaterThanOrEqual(1, count($list));
187        }
188
189        // getUserCount
190        if($auth->canDo('getUserCount')) {
191            $count = $auth->getUserCount(array('user' => $user['user']));
192            $this->assertGreaterThanOrEqual(1, $count);
193            $count = $auth->getUserCount(array('name' => $user['name']));
194            $this->assertGreaterThanOrEqual(1, $count);
195            $count = $auth->getUserCount(array('mail' => $user['mail']));
196            $this->assertGreaterThanOrEqual(1, $count);
197        }
198
199        // modGroups
200        if($auth->canDo('modGroups')) {
201            $newgroup = 'foobar';
202            $ok = $auth->modifyUser($user['user'], array('grps' => array($newgroup)));
203            $this->assertTrue($ok, $info);
204            $check = $auth->getUserData($user['user']);
205            $this->assertTrue(in_array($newgroup, $check['grps']), $info);
206        }
207
208        // modPass
209        if($auth->canDo('modPass')) {
210            $newpass = 'foobar';
211            $ok = $auth->modifyUser($user['user'], array('pass' => $newpass));
212            $this->assertTrue($ok, $info);
213            $this->assertTrue($auth->checkPass($user['user'], $newpass), $info);
214        }
215
216        // modMail
217        if($auth->canDo('modMail')) {
218            $newmail = 'foobar@example.com';
219            $ok = $auth->modifyUser($user['user'], array('mail' => $newmail));
220            $this->assertTrue($ok, $info);
221            $check = $auth->getUserData($user['user']);
222            $this->assertEquals($newmail, $check['mail'], $info);
223        }
224
225        // modName
226        if($auth->canDo('modName')) {
227            $newname = 'FirstName Foobar';
228            $ok = $auth->modifyUser($user['user'], array('name' => $newname));
229            $this->assertTrue($ok, $info);
230            $check = $auth->getUserData($user['user']);
231            $this->assertEquals($newname, $check['name'], $info);
232        }
233
234        // modLogin
235        if($auth->canDo('modLogin')) {
236            $newuser = 'foobar' . $user['user'];
237            $ok = $auth->modifyUser($user['user'], array('user' => $newuser));
238            $this->assertTrue($ok, $info);
239            $check = $auth->getUserData($newuser);
240            $this->assertEquals($newuser, $check['user'], $info);
241            // rename back
242            $ok = $auth->modifyUser($newuser, array('user' => $user['user']));
243            $this->assertTrue($ok, $info);
244        }
245
246        // delUser
247        if($auth->canDo('delUser')) {
248            $num = $auth->deleteUsers(array($user['user']));
249            $this->assertEquals(1, $num, $info);
250            $this->assertFalse($auth->getUserData($user['user']), $info);
251        }
252    }
253
254    /**
255     * prepares the individual configurations for testing
256     *
257     * @return array
258     */
259    public function data_provider() {
260        $testdata = array();
261
262        $files = glob(__DIR__ . "/{$this->driver}/*.php");
263        foreach($files as $file) {
264            $dump = preg_replace('/\.php$/', '.sql', $file);
265            $dbname = 'authpdo_testing_' . basename($file, '.php');
266
267            /** @var $data array */
268            include $file;
269
270            $testdata[] = array($dbname, $dump, $data);
271        }
272
273        return $testdata;
274    }
275
276    /**
277     * This triggers all the tests based on the dumps and configurations
278     *
279     * @dataProvider data_provider
280     * @depends      test_requirements
281     * @param string $dbname Name of the database to use
282     * @param string $dump The path to the dump file to import
283     * @param array|string $data config and test user setup. When a string is passed, test is skipped with that msg
284     */
285    public function test_database($dbname, $dump, $data){
286        global $conf;
287
288        if(!is_array($data)) {
289            $this->markTestSkipped($data);
290            return;
291        }
292
293        $this->database = $dbname;
294
295        $this->createDatabase();
296        $this->importDatabase($dump);
297
298        // Setup the configuration and initialize a new auth object
299        $conf['plugin']['authpdo'] = array();
300        $conf['plugin']['authpdo'] = $data['conf'];
301        $conf['plugin']['authpdo']['dsn'] = "{$this->driver}:dbname={$this->database};host={$this->host};port={$this->port}";
302        $conf['plugin']['authpdo']['user'] = $this->user;
303        $conf['plugin']['authpdo']['pass'] = $this->pass;
304        $conf['plugin']['authpdo']['debug'] = 1;
305        if($data['passcrypt']) $conf['passcrypt'] = $data['passcrypt'];
306        $auth = new auth_plugin_authpdo();
307
308        $this->runGeneralTests($auth, $data['users']);
309        foreach($data['users'] as $user) {
310            $this->runUserTests($auth, $user);
311        }
312
313        $this->dropDatabase();
314    }
315
316}
317