1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * A framework for authentication and authorization in PHP applications
6 *
7 * LiveUser_Admin is meant to be used with the LiveUser package.
8 * It is composed of all the classes necessary to administrate
9 * data used by LiveUser.
10 *
11 * You'll be able to add/edit/delete/get things like:
12 * * Rights
13 * * Users
14 * * Groups
15 * * Areas
16 * * Applications
17 * * Subgroups
18 * * ImpliedRights
19 *
20 * And all other entities within LiveUser.
21 *
22 * At the moment we support the following storage containers:
23 * * DB
24 * * MDB
25 * * MDB2
26 *
27 * But it takes no time to write up your own storage container,
28 * so if you like to use native mysql functions straight, then it's possible
29 * to do so in under a hour!
30 *
31 * PHP version 4 and 5
32 *
33 * LICENSE: This library is free software; you can redistribute it and/or
34 * modify it under the terms of the GNU Lesser General Public
35 * License as published by the Free Software Foundation; either
36 * version 2.1 of the License, or (at your option) any later version.
37 *
38 * This library is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41 * Lesser General Public License for more details.
42 *
43 * You should have received a copy of the GNU Lesser General Public
44 * License along with this library; if not, write to the Free Software
45 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
46 * MA  02111-1307  USA
47 *
48 *
49 * @category authentication
50 * @package LiveUser_Admin
51 * @author  Markus Wolff <wolff@21st.de>
52 * @author  Helgi �ormar �orbj�rnsson <dufuz@php.net>
53 * @author  Lukas Smith <smith@pooteeweet.org>
54 * @author  Arnaud Limbourg <arnaud@php.net>
55 * @author  Christian Dickmann <dickmann@php.net>
56 * @author  Matt Scifo <mscifo@php.net>
57 * @author  Bjoern Kraus <krausbn@php.net>
58 * @copyright 2002-2006 Markus Wolff
59 * @license http://www.gnu.org/licenses/lgpl.txt
60 * @version $Id: Medium.php 211313 2006-04-13 09:29:20Z lsmith $
61 * @link http://pear.php.net/LiveUser_Admin
62 */
63
64define('LIVEUSER_GROUP_TYPE_ALL',   1);
65define('LIVEUSER_GROUP_TYPE_ROLE',  2);
66define('LIVEUSER_GROUP_TYPE_USER',  3);
67
68 /**
69 * Require parent class definition.
70 */
71require_once 'LiveUser/Admin/Perm/Simple.php';
72
73/**
74 * Medium permission administration class that extends the Simple class with the
75 * ability to create, update, remove and assign groups.
76 *
77 * This class provides a set of functions for implementing a user
78 * permission management system on live websites. All authorisation
79 * backends/containers must be extensions of this base class.
80 *
81 * @category authentication
82 * @package LiveUser_Admin
83 * @author  Markus Wolff <wolff@21st.de>
84 * @author  Bjoern Kraus <krausbn@php.net>
85 * @author  Helgi �ormar �orbj�rnsson <dufuz@php.net>
86 * @copyright 2002-2006 Markus Wolff
87 * @license http://www.gnu.org/licenses/lgpl.txt
88 * @version Release: @package_version@
89 * @link http://pear.php.net/LiveUser_Admin
90 */
91class LiveUser_Admin_Perm_Medium extends LiveUser_Admin_Perm_Simple
92{
93    /**
94     * Constructor
95     *
96     * @return void
97     *
98     * @access protected
99     */
100    function LiveUser_Admin_Perm_Medium()
101    {
102        // Define the required tables for the Medium container. Used by the query builder
103        $this->LiveUser_Admin_Perm_Simple();
104        $this->selectable_tables['getUsers'][] = 'groupusers';
105        $this->selectable_tables['getGroups'] = array('groups', 'groupusers', 'grouprights', 'rights', 'translations');
106        $this->withFieldMethodMap['group_id'] = 'getGroups';
107    }
108
109    /**
110     * Add a group
111     *
112     * @param array containing atleast the key-value-pairs of all required
113     *              columns in the group table
114     * @return int|bool false on error, true (or new id) on success
115     *
116     * @access public
117     */
118    function addGroup($data)
119    {
120        $result = $this->_storage->insert('groups', $data);
121        // todo: notify observer
122        return $result;
123    }
124
125    /**
126     * Update groups
127     *
128     * @param array containing the key value pairs of columns to update
129     * @param array key values pairs (value may be a string or an array)
130     *                      This will construct the WHERE clause of your update
131     *                      Be careful, if you leave this blank no WHERE clause
132     *                      will be used and all groups will be affected by the update
133     * @return int|bool false on error, the affected rows on success
134     *
135     * @access public
136     */
137    function updateGroup($data, $filters)
138    {
139        $result = $this->_storage->update('groups', $data, $filters);
140        // todo: notify observer
141        return $result;
142    }
143
144    /**
145     * Remove groups and all their relevant relations
146     *
147     * @param array key values pairs (value may be a string or an array)
148     *                      This will construct the WHERE clause of your update
149     *                      Be careful, if you leave this blank no WHERE clause
150     *                      will be used and all groups will be affected by the removed
151     * @return int|bool false on error, the affected rows on success
152     *
153     * @access public
154     */
155    function removeGroup($filters)
156    {
157        // Prepare the filters. Based on the provided filters a new array will be
158        // created with the corresponding group_id's. If the filters are empty,
159        // cause an error or just have no result 0 or false will be returned
160        $filters = $this->_makeRemoveFilter($filters, 'group_id', 'getGroups');
161        if (!$filters) {
162            return $filters;
163        }
164
165        // Clean up the database so no unnessacary information is left behind (members, granted rights)
166        // Remove all the users that are members of this group.
167        $result = $this->removeUserFromGroup($filters);
168        if ($result === false) {
169            return false;
170        }
171
172        // Remove the group.
173        $result = $this->revokeGroupRight($filters);
174        if ($result === false) {
175            return false;
176        }
177
178        $result = $this->_storage->delete('groups', $filters);
179        // todo: notify observer
180        return $result;
181    }
182
183    /**
184     * Grant group a right
185     *
186     * <code>
187     * // grant user id 13 the right NEWS_CHANGE
188     * $data = array(
189     *      'right_id'     => NEWS_CHANGE,
190     *      'group_id' => 13
191     * );
192     * $lua->perm->grantGroupRight($data);
193     * </code>
194     *
195     * @param array containing the group_id and right_id and optionally a right_level
196     * @return
197     *
198     * @access public
199     */
200    function grantGroupRight($data)
201    {
202        // Sanity check on the right level, if not set, use the default
203        if (!array_key_exists('right_level', $data)) {
204            $data['right_level'] = LIVEUSER_MAX_LEVEL;
205        }
206
207        // check if the group has already been granted that right
208        $filters = array(
209            'group_id' => $data['group_id'],
210            'right_id' => $data['right_id'],
211        );
212
213        $count = $this->_storage->selectCount('grouprights', 'right_id', $filters);
214
215        // It did already.. Add an error to the stack.
216        if ($count > 0) {
217            $this->stack->push(
218                LIVEUSER_ADMIN_ERROR, 'exception',
219                array('msg' => 'This group with id '.$data['group_id'].
220                    ' has already been granted the right id '.$data['right_id'])
221            );
222            return false;
223        }
224
225        $result = $this->_storage->insert('grouprights', $data);
226        // todo: notify observer
227        return $result;
228    }
229
230    /**
231     * Update right(s) for the given group(s)
232     *
233     * @param array containing the key value pairs of columns to update
234     * @param array key values pairs (value may be a string or an array)
235     *                      This will construct the WHERE clause of your update
236     *                      Be careful, if you leave this blank no WHERE clause
237     *                      will be used and all groups will be affected by the update
238     * @return int|bool false on error, the affected rows on success
239     *
240     * @access public
241     */
242    function updateGroupRight($data, $filters)
243    {
244        $result = $this->_storage->update('grouprights', $data, $filters);
245        // todo: notify observer
246        return $result;
247    }
248
249    /**
250     * Revoke (remove) right(s) from the group(s)
251     *
252     * @param array key values pairs (value may be a string or an array)
253     *                      This will construct the WHERE clause of your update
254     *                      Be careful, if you leave this blank no WHERE clause
255     *                      will be used and all groups will be affected by the remove
256     * @return int|bool false on error, the affected rows on success
257     *
258     * @access public
259     */
260    function revokeGroupRight($filters)
261    {
262        $result = $this->_storage->delete('grouprights', $filters);
263        // todo: notify observer
264        return $result;
265    }
266
267    /**
268     * Add a user to agroup
269     *
270     * @param array containing the perm_user_id and group_id
271     * @return
272     *
273     * @access public
274     */
275    function addUserToGroup($data)
276    {
277        // check if the userhas already been granted added to that group
278        $filters = array(
279            'perm_user_id' => $data['perm_user_id'],
280            'group_id'     => $data['group_id'],
281        );
282
283        $count = $this->_storage->selectCount('groupusers', 'group_id', $filters);
284
285        // It already had been added. Return true.
286        if ($count > 0) {
287            return true;
288        }
289
290        $result = $this->_storage->insert('groupusers', $data);
291        // todo: notify observer
292        return $result;
293    }
294
295    /**
296     * Remove user(s) from group(s)
297     *
298     * @param array key values pairs (value may be a string or an array)
299     *                      This will construct the WHERE clause of your update
300     *                      Be careful, if you leave this blank no WHERE clause
301     *                      will be used and all users will be affected by the remove
302     * @return int|bool false on error, the affected rows on success
303     *
304     * @access public
305     */
306    function removeUserFromGroup($filters)
307    {
308        $result = $this->_storage->delete('groupusers', $filters);
309        // todo: notify observer
310        return $result;
311    }
312
313    /**
314     * Fetches rights
315     *
316     * @param array containing key-value pairs for:
317     *                 'fields'  - ordered array containing the fields to fetch
318     *                             if empty all fields from the user table are fetched
319     *                 'filters' - key values pairs (value may be a string or an array)
320     *                 'orders'  - key value pairs (values 'ASC' or 'DESC')
321     *                 'rekey'   - if set to true, returned array will have the
322     *                             first column as its first dimension
323     *                 'group'   - if set to true and $rekey is set to true, then
324     *                             all values with the same first column will be
325     *                             wrapped in an array
326     *                 'limit'   - number of rows to select
327     *                 'offset'  - first row to select
328     *                 'select'  - determines what query method to use:
329     *                             'one' -> queryOne, 'row' -> queryRow,
330     *                             'col' -> queryCol, 'all' ->queryAll (default)
331     *                 'selectable_tables' - array list of tables that may be
332     *                             joined to in this query, the first element is
333     *                             the root table from which the joins are done
334     *                 'by_group'  - if joins should be done using the 'userrights'
335     *                             (false default) or through the 'grouprights'
336     *                             and 'groupusers' tables (true)
337     * @return bool|array false on failure or array with selected data
338     *
339     * @access public
340     */
341    function getRights($params = array())
342    {
343        $selectable_tables = $this->_findSelectableTables('getRights' , $params);
344        $root_table = reset($selectable_tables);
345
346        // If the by_group is present, and the grouprights table is not in the selectable_tables:
347        if (array_key_exists('by_group', $params)
348            && $params['by_group']
349            && !in_array('grouprights', $selectable_tables)
350        ) {
351            unset($params['by_group']);
352            $key = array_search('userrights', $selectable_tables);
353            if ($key) {
354                // add the groupusers, replace the userrights with
355                // the grouprights and prepend the root table
356                $selectable_tables[0] = 'groupusers';
357                $selectable_tables[$key] = 'grouprights';
358                array_unshift($selectable_tables, $root_table);
359            } else {
360                // add the groupusers, prepend the grouprights and the root table
361                $selectable_tables[0] = 'groupusers';
362                array_unshift($selectable_tables, 'grouprights');
363                array_unshift($selectable_tables, $root_table);
364            }
365        }
366
367        return $this->_makeGet($params, $root_table, $selectable_tables);
368    }
369
370    /**
371     * Remove rights and all their relevant relations
372     *
373     * @param array key values pairs (value may be a string or an array)
374     *                      This will construct the WHERE clause of your update
375     *                      Be careful, if you leave this blank no WHERE clause
376     *                      will be used and all rights will be affected by the remove
377     * @return int|bool false on error, the affected rows on success
378     *
379     * @access public
380     */
381    function removeRight($filters)
382    {
383        $filters = $this->_makeRemoveFilter($filters, 'right_id', 'getRights');
384        if (!$filters) {
385            return $filters;
386        }
387
388        $result = $this->revokeGroupRight($filters);
389        if ($result === false) {
390            return false;
391        }
392
393        return parent::removeRight($filters);
394    }
395
396    /**
397     * Remove users and all their relevant relations
398     *
399     * @param array key values pairs (value may be a string or an array)
400     *                      This will construct the WHERE clause of your update
401     *                      Be careful, if you leave this blank no WHERE clause
402     *                      will be used and all users will be affected by the removed
403     * @return int|bool false on error, the affected rows on success
404     *
405     * @access public
406     */
407    function removeUser($filters)
408    {
409        // Prepare the filters. Based on the provided filters a new array will be
410        // created with the corresponding perm_user_id's. If the filters are empty,
411        // cause an error or just have no result 0 or false will be returned
412        $filters = $this->_makeRemoveFilter($filters, 'perm_user_id', 'getUsers');
413        if (!$filters) {
414            return $filters;
415        }
416
417        // Remove the users from any group it might be a member of.
418        // If an error occures, return false.
419        $result = $this->removeUserFromGroup($filters);
420        if ($result === false) {
421            return false;
422        }
423
424        // remove the user using Perm Simple.
425        return parent::removeUser($filters);
426    }
427
428    /**
429     * Fetches groups
430     *
431     * @param array containing key-value pairs for:
432     *                 'fields'  - ordered array containing the fields to fetch
433     *                             if empty all fields from the user table are fetched
434     *                 'filters' - key values pairs (value may be a string or an array)
435     *                 'orders'  - key value pairs (values 'ASC' or 'DESC')
436     *                 'rekey'   - if set to true, returned array will have the
437     *                             first column as its first dimension
438     *                 'group'   - if set to true and $rekey is set to true, then
439     *                             all values with the same first column will be
440     *                             wrapped in an array
441     *                 'limit'   - number of rows to select
442     *                 'offset'  - first row to select
443     *                 'select'  - determines what query method to use:
444     *                             'one' -> queryOne, 'row' -> queryRow,
445     *                             'col' -> queryCol, 'all' ->queryAll (default)
446     *                 'selectable_tables' - array list of tables that may be
447     *                             joined to in this query, the first element is
448     *                             the root table from which the joins are done
449     * @return bool|array false on failure or array with selected data
450     *
451     * @access public
452     */
453    function getGroups($params = array())
454    {
455        $selectable_tables = $this->_findSelectableTables('getGroups' , $params);
456        $root_table = reset($selectable_tables);
457
458        return $this->_makeGet($params, $root_table, $selectable_tables);
459    }
460}
461?>
462