Experimental: Give the user an idea how long they have until the security token expires
3 /* Reminder: always indent with 4 spaces (no tabs). */
4 // +---------------------------------------------------------------------------+
6 // +---------------------------------------------------------------------------+
7 // | lib-security.php |
9 // | Geeklog security library. |
10 // +---------------------------------------------------------------------------+
11 // | Copyright (C) 2000-2009 by the following authors: |
13 // | Authors: Tony Bibbs - tony AT tonybibbs DOT com |
14 // | Mark Limburg - mlimburg AT users DOT sourceforge DOT net |
15 // | Vincent Furia - vmf AT abtech DOT org |
16 // | Michael Jervis - mike AT fuckingbrit DOT com |
17 // +---------------------------------------------------------------------------+
19 // | This program is free software; you can redistribute it and/or |
20 // | modify it under the terms of the GNU General Public License |
21 // | as published by the Free Software Foundation; either version 2 |
22 // | of the License, or (at your option) any later version. |
24 // | This program is distributed in the hope that it will be useful, |
25 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
27 // | GNU General Public License for more details. |
29 // | You should have received a copy of the GNU General Public License |
30 // | along with this program; if not, write to the Free Software Foundation, |
31 // | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
33 // +---------------------------------------------------------------------------+
36 * This is the security library for Geeklog. This is used to implement Geeklog's
37 * *nix-style security system.
39 * Programming notes: For items you need security on you need the following for
40 * each record in your database:
41 * owner_id | mediumint(8)
42 * group_id | mediumint(8)
43 * perm_owner | tinyint(1) unsigned
44 * perm_group | tinyint(1) unsigned
45 * perm_members | tinyint(1) unsigned
46 * perm_anon | tinyint(1) unsigned
48 * For display one function can handle most needs:
49 * function SEC_hasAccess($owner_id,$group_id,$perm_owner,$perm_group,$perm_members,$perm_anon)
50 * A call to this function will allow you to determine if the current user should see the item.
52 * For the admin screen several functions will make life easier:
53 * function SEC_getPermissionsHTML($perm_owner,$perm_group,$perm_members,$perm_anon)
54 * This function displays the permissions widget with arrays for each permission
55 * function SEC_getPermissionValues($perm_owner,$perm_group,$perm_members,$perm_anon)
56 * This function takes the permissions from the previous function and converts them into
57 * an integer for saving back to the database.
61 // Turn this on to get various debug messages from the code in this library
62 $_SEC_VERBOSE = false;
64 if (strpos(strtolower($_SERVER['PHP_SELF']), 'lib-security.php') !== false) {
65 die('This file can not be used on its own!');
68 /* Constants for account stats */
69 define('USER_ACCOUNT_DISABLED', 0); // Account is banned/disabled
70 define('USER_ACCOUNT_AWAITING_ACTIVATION', 1); // Account awaiting user to login.
71 define('USER_ACCOUNT_AWAITING_APPROVAL', 2); // Account awaiting moderator approval
72 define('USER_ACCOUNT_ACTIVE', 3); // active account
74 /* Constant for Security Token */
75 if (!defined('CSRF_TOKEN')) {
76 define('CSRF_TOKEN', '_glsectoken');
80 * Returns the groups a user belongs to
82 * This is part of the GL security implementation. This function returns
83 * all the groups a user belongs to. This function is called recursively
84 * as groups can belong to other groups
86 * Note: this is an expensive function -- if you are concerned about speed it should only
87 * be used once at the beginning of a page. The resulting array $_GROUPS can then be
88 * used through out the page.
90 * @param int $uid User ID to get information for. If empty current user.
91 * @return array Associative Array grp_name -> ug_main_grp_id of group ID's user belongs to
94 function SEC_getUserGroups($uid='')
96 global $_TABLES, $_USER, $_SEC_VERBOSE;
99 COM_errorLog("****************in getusergroups(uid=$uid,usergroups=$usergroups,cur_grp_id=$cur_grp_id)***************",1);
105 if (empty($_USER['uid'])) {
108 $uid = $_USER['uid'];
112 $result = DB_query("SELECT ug_main_grp_id,grp_name FROM {$_TABLES["group_assignments"]},{$_TABLES["groups"]}"
113 . " WHERE grp_id = ug_main_grp_id AND ug_uid = $uid",1);
119 $nrows = DB_numRows($result);
122 COM_errorLog("got $nrows rows",1);
128 for ($i = 1; $i <= $nrows; $i++) {
129 $A = DB_fetchArray($result);
132 COM_errorLog('user is in group ' . $A['grp_name'],1);
134 if (!in_array($A['ug_main_grp_id'], $groups)) {
135 array_push($cgroups, $A['ug_main_grp_id']);
136 $groups[$A['grp_name']] = $A['ug_main_grp_id'];
140 if (count($cgroups) > 0) {
141 $glist = join(',', $cgroups);
142 $result = DB_query("SELECT ug_main_grp_id,grp_name FROM {$_TABLES["group_assignments"]},{$_TABLES["groups"]}"
143 . " WHERE grp_id = ug_main_grp_id AND ug_grp_id IN ($glist)",1);
144 $nrows = DB_numRows($result);
150 uksort($groups, 'strcasecmp');
153 COM_errorLog("****************leaving getusergroups(uid=$uid)***************",1);
160 * Checks to see if a user has admin access to the "Remote Users" group
161 * Admin users will probably not be members, but, User Admin, Root, and
162 * group admin will have access to it. However, we can not be sure what
163 * the group id for "Remote User" group is, because it's a later static
164 * group, and upgraded systems could have it in any id slot.
166 * @param groupid int The id of a group, which might be the remote users group
167 * @param groups array Array of group ids the user has access to.
170 function SEC_groupIsRemoteUserAndHaveAccess($groupid, $groups)
172 global $_TABLES, $_CONF;
173 if(!isset($_CONF['remote_users_group_id']))
175 $result = DB_query("SELECT grp_id FROM {$_TABLES['groups']} WHERE grp_name='Remote Users'");
178 $row = DB_fetchArray( $result );
179 $_CONF['remote_users_group_id'] = $row['grp_id'];
182 if( $groupid == $_CONF['remote_users_group_id'] )
184 if( in_array( 1, $groups ) || // root
185 in_array( 9, $groups ) || // user admin
186 in_array( 11, $groups ) // Group admin
199 * Determines if user belongs to specified group
201 * This is part of the Geeklog security implementation. This function
202 * looks up whether a user belongs to a specified group
204 * @param string $grp_to_verify Group we want to see if user belongs to
205 * @param int $uid ID for user to check. If empty current user.
206 * @param string $cur_grp_id NOT USED Current group we are working with in hierarchy
207 * @return boolean true if user is in group, otherwise false
210 function SEC_inGroup($grp_to_verify,$uid='',$cur_grp_id='')
212 global $_TABLES, $_USER, $_SEC_VERBOSE, $_GROUPS;
215 if (empty ($_USER['uid'])) {
218 $uid = $_USER['uid'];
222 if ((empty($_USER['uid']) && ($uid == 1)) ||
223 (isset($_USER['uid']) && ($uid == $_USER['uid']))) {
224 if (empty($_GROUPS)) {
225 $_GROUPS = SEC_getUserGroups($uid);
229 $groups = SEC_getUserGroups($uid);
232 if (is_numeric($grp_to_verify)) {
233 if (in_array($grp_to_verify, $groups)) {
239 if (!empty($groups[$grp_to_verify])) {
248 * Determines if current user is a moderator of any kind
250 * Checks to see if this user is a moderator for any of the GL features OR
253 * @return boolean returns if user has any .moderate rights
256 function SEC_isModerator()
258 global $_USER,$_RIGHTS;
260 // Loop through GL core rights.
261 for ($i = 0; $i < count($_RIGHTS); $i++) {
262 if (stristr($_RIGHTS[$i],'.moderate')) {
267 // If we get this far they are not a Geeklog moderator
268 // So, let's return if they're a plugin moderator
270 return PLG_isModerator();
274 * Checks to see if current user has access to a topic
276 * Checks to see if current user has access to a topic
278 * @param string $tid ID for topic to check on
279 * @return int returns 3 for read/edit 2 for read only 0 for no access
282 function SEC_hasTopicAccess($tid)
290 $result = DB_query("SELECT owner_id,group_id,perm_owner,perm_group,perm_members,perm_anon FROM {$_TABLES['topics']} WHERE tid = '$tid'");
291 $A = DB_fetchArray($result);
293 return SEC_hasAccess($A['owner_id'],$A['group_id'],$A['perm_owner'],$A['perm_group'],$A['perm_members'],$A['perm_anon']);
297 * Checks if current user has access to the given object
299 * This function takes the access info from a Geeklog object
300 * and let's us know if they have access to the object
301 * returns 3 for read/edit, 2 for read only and 0 for no
304 * @param int $owner_id ID of the owner of object
305 * @param int $group_id ID of group object belongs to
306 * @param int $perm_owner Permissions the owner has
307 * @param int $perm_group Permissions the gorup has
308 * @param int $perm_members Permissions logged in members have
309 * @param int $perm_anon Permissions anonymous users have
310 * @return int returns 3 for read/edit 2 for read only 0 for no access
313 function SEC_hasAccess($owner_id,$group_id,$perm_owner,$perm_group,$perm_members,$perm_anon)
317 // Cache current user id
318 if (empty($_USER['uid'])) {
321 $uid = $_USER['uid'];
324 // If user is in Root group then return full access
325 if (SEC_inGroup('Root')) {
329 // If user is owner then return 1 now
330 if ($uid == $owner_id) return $perm_owner;
332 // Not private, if user is in group then give access
333 if (SEC_inGroup($group_id)) {
337 // This is an anonymous user, return it's rights
340 // This is a logged in member, return their rights
341 return $perm_members;
347 * Checks if current user has rights to a feature
349 * Takes either a single feature or an array of features and returns
350 * an array of whether the user has those rights
352 * @param string|array $features Features to check
353 * @param string $operator Either 'and' or 'or'. Default is 'and'. Used if checking more than one feature.
354 * @return boolean Return true if current user has access to feature(s), otherwise false.
357 function SEC_hasRights($features,$operator='AND')
359 global $_USER, $_RIGHTS, $_SEC_VERBOSE;
361 if (strstr($features,',')) {
362 $features = explode(',',$features);
365 if (is_array($features)) {
366 // check all values passed
367 for ($i = 0; $i < count($features); $i++) {
368 if ($operator == 'OR') {
369 // OR operator, return as soon as we find a true one
370 if (in_array($features[$i],$_RIGHTS)) {
372 COM_errorLog('SECURITY: user has access to ' . $features[$i],1);
377 // this is an "AND" operator, bail if we find a false one
378 if (!in_array($features[$i],$_RIGHTS)) {
380 COM_errorLog('SECURITY: user does not have access to ' . $features[$i],1);
387 if ($operator == 'OR') {
389 COM_errorLog('SECURITY: user does not have access to ' . $features[$i],1);
394 COM_errorLog('SECURITY: user has access to ' . $features[$i],1);
399 // Check the one value
401 if (in_array($features,$_RIGHTS)) {
402 COM_errorLog('SECURITY: user has access to ' . $features,1);
404 COM_errorLog('SECURITY: user does not have access to ' . $features,1);
407 return in_array($features,$_RIGHTS);
412 * Shows security control for an object
414 * This will return the HTML needed to create the security control see on the admin
415 * screen for GL objects (i.e. stories, etc)
417 * @param int $perm_owner Permissions the owner has 1 = edit 2 = read 3 = read/edit
418 * @param int $perm_group Permission the group has
419 * @param int $perm_members Permissions logged in members have
420 * @param int $perm_anon Permissions anonymous users have
421 * @return string needed HTML (table) in HTML $perm_owner = array of permissions [edit,read], etc edit = 1 if permission, read = 2 if permission
424 function SEC_getPermissionsHTML($perm_owner,$perm_group,$perm_members,$perm_anon)
426 global $LANG_ACCESS, $_CONF;
430 $perm_templates = new Template($_CONF['path_layout'] . 'admin/common');
431 $perm_templates->set_file(array('editor'=>'edit_permissions.thtml'));
433 $perm_templates->set_var ( 'xhtml', XHTML );
434 $perm_templates->set_var ('site_url', $_CONF['site_url']);
435 $perm_templates->set_var ('site_admin_url', $_CONF['site_admin_url']);
436 $perm_templates->set_var ('layout_url', $_CONF['layout_url']);
437 $perm_templates->set_var ('owner', $LANG_ACCESS['owner']);
438 $perm_templates->set_var ('group', $LANG_ACCESS['group']);
439 $perm_templates->set_var ('members', $LANG_ACCESS['members']);
440 $perm_templates->set_var ('anonymous', $LANG_ACCESS['anonymous']);
443 if ($perm_owner >= 2) {
444 $perm_templates->set_var ('owner_r_checked',' checked="checked"');
446 if ($perm_owner == 3) {
447 $perm_templates->set_var ('owner_e_checked',' checked="checked"');
450 if ($perm_group >= 2) {
451 $perm_templates->set_var ('group_r_checked',' checked="checked"');
453 if ($perm_group == 3) {
454 $perm_templates->set_var ('group_e_checked',' checked="checked"');
456 // Member Permissions
457 if ($perm_members == 2) {
458 $perm_templates->set_var ('members_checked',' checked="checked"');
460 // Anonymous Permissions
461 if ($perm_anon == 2) {
462 $perm_templates->set_var ('anon_checked',' checked="checked"');
465 $perm_templates->parse('output','editor');
466 $retval .= $perm_templates->finish($perm_templates->get_var('output'));
472 * Gets everything a user has permissions to within the system
474 * This is part of the Geeklog security implementation. This function
475 * will get all the permissions the current user has. Calls itself recursively.
477 * @param int $grp_id DO NOT USE (Used for recursion) Current group function is working on
478 * @param int $uid User to check, if empty current user.
479 * @return string returns comma delimited list of features the user has access to
482 function SEC_getUserPermissions($grp_id='', $uid='')
484 global $_TABLES, $_USER, $_SEC_VERBOSE, $_GROUPS;
489 COM_errorLog("**********inside SEC_getUserPermissions(grp_id=$grp_id)**********",1);
492 // Get user ID if we don't already have it
494 if (empty ($_USER['uid'])) {
497 $uid = $_USER['uid'];
501 if ((empty ($_USER['uid']) && ($uid == 1)) || ($uid == $_USER['uid'])) {
502 if (empty ($_GROUPS)) {
503 $_GROUPS = SEC_getUserGroups ($uid);
507 $groups = SEC_getUserGroups ($uid);
510 if (empty($groups)) {
511 // this shouldn't happen - make a graceful exit to avoid an SQL error
515 $glist = join(',', $groups);
516 $result = DB_query("SELECT DISTINCT ft_name FROM {$_TABLES["access"]},{$_TABLES["features"]} "
517 . "WHERE ft_id = acc_ft_id AND acc_grp_id IN ($glist)");
519 $nrows = DB_numrows($result);
520 for ($j = 1; $j <= $nrows; $j++) {
521 $A = DB_fetchArray($result);
523 COM_errorLog('Adding right ' . $A['ft_name'] . ' in SEC_getUserPermissions',1);
525 $retval .= $A['ft_name'];
535 * Converts permissions to numeric values
537 * This function will take all permissions for an object and get the numeric value
538 * that can then be used to save the database.
540 * @param array $perm_owner Array of owner permissions These arrays are set up by SEC_getPermissionsHTML
541 * @param array $perm_group Array of group permissions
542 * @param array $perm_members Array of member permissions
543 * @param array $perm_anon Array of anonymous user permissions
544 * @return array returns numeric equivalent for each permissions array (2 = read, 3=edit/read)
545 * @see SEC_getPermissionsHTML
546 * @see SEC_getPermissionValue
549 function SEC_getPermissionValues($perm_owner,$perm_group,$perm_members,$perm_anon)
551 global $_SEC_VERBOSE;
554 COM_errorLog('**** Inside SEC_getPermissionValues ****', 1);
557 if (is_array($perm_owner)) {
558 $perm_owner = SEC_getPermissionValue($perm_owner);
563 if (is_array($perm_group)) {
564 $perm_group = SEC_getPermissionValue($perm_group);
569 if (is_array($perm_members)) {
570 $perm_members = SEC_getPermissionValue($perm_members);
575 if (is_array($perm_anon)) {
576 $perm_anon = SEC_getPermissionValue($perm_anon);
582 COM_errorLog('perm_owner = ' . $perm_owner, 1);
583 COM_errorLog('perm_group = ' . $perm_group, 1);
584 COM_errorLog('perm_member = ' . $perm_members, 1);
585 COM_errorLog('perm_anon = ' . $perm_anon, 1);
586 COM_errorLog('**** Leaving SEC_getPermissionValues ****', 1);
589 return array($perm_owner,$perm_group,$perm_members,$perm_anon);
593 * Converts permission array into numeric value
595 * This function converts an array of permissions for either
596 * the owner/group/members/anon and returns the numeric
597 * equivalent. This is typically called by the admin screens
598 * to prepare the permissions to be save to the database
600 * @param array $perm_x Array of permission values
601 * @return int int representation of a permission array 2 = read 3 = edit/read
602 * @see SEC_getPermissionValues
605 function SEC_getPermissionValue($perm_x)
607 global $_SEC_VERBOSE;
610 COM_errorLog('**** Inside SEC_getPermissionValue ***', 1);
615 for ($i = 1; $i <= count($perm_x); $i++) {
617 COM_errorLog("perm_x[$i] = " . current($perm_x), 1);
619 $retval = $retval + current($perm_x);
623 // if they have edit rights, assume read rights
629 COM_errorLog("Got $retval permission value", 1);
630 COM_errorLog('**** Leaving SEC_getPermissionValue ***', 1);
637 * Return the group to a given feature.
639 * Scenario: We have a feature and we want to know from which group the user
640 * got this feature. Always returns the lowest group ID, in case the feature
641 * has been inherited from more than one group.
643 * @param string $feature the feature, e.g 'story.edit'
644 * @param int $uid (optional) user ID
645 * @return int group ID or 0
648 function SEC_getFeatureGroup ($feature, $uid = '')
650 global $_GROUPS, $_TABLES, $_USER;
655 if (empty ($_USER['uid'])) {
658 $uid = $_USER['uid'];
662 if ((empty ($_USER['uid']) && ($uid == 1)) || ($uid == $_USER['uid'])) {
663 if (empty ($_GROUPS)) {
664 $_GROUPS = SEC_getUserGroups ($uid);
668 $ugroups = SEC_getUserGroups ($uid);
673 $ft_id = DB_getItem ($_TABLES['features'], 'ft_id', "ft_name = '$feature'");
674 if (($ft_id > 0) && (count($ugroups) > 0)) {
675 $grouplist = implode (',', $ugroups);
676 $result = DB_query ("SELECT acc_grp_id FROM {$_TABLES['access']} WHERE (acc_ft_id = $ft_id) AND (acc_grp_id IN ($grouplist)) ORDER BY acc_grp_id LIMIT 1");
677 $A = DB_fetchArray ($result);
678 if (isset ($A['acc_grp_id'])) {
679 $group = $A['acc_grp_id'];
687 * Attempt to login a user.
689 * Checks a users username and password against the database. Returns
692 * @param string $username who is logging in?
693 * @param string $password what they claim is their password
694 * @param int $uid This is an OUTPUT param, pass by ref,
695 * sends back UID inside it.
696 * @return int user status, -1 for fail.
699 function SEC_authenticate($username, $password, &$uid)
701 global $_CONF, $_TABLES, $LANG01;
703 $result = DB_query("SELECT status, passwd, email, uid FROM {$_TABLES['users']} WHERE username='$username' AND ((remoteservice is null) or (remoteservice = ''))");
705 $nrows = DB_numRows($result);
707 if (($tmp == 0) && ($nrows == 1)) {
708 $U = DB_fetchArray($result);
710 if ($U['status'] == USER_ACCOUNT_DISABLED) {
711 // banned, jump to here to save an md5 calc.
712 return USER_ACCOUNT_DISABLED;
713 } elseif ($U['passwd'] != SEC_encryptPassword($password)) {
714 return -1; // failed login
715 } elseif ($U['status'] == USER_ACCOUNT_AWAITING_APPROVAL) {
716 return USER_ACCOUNT_AWAITING_APPROVAL;
717 } elseif ($U['status'] == USER_ACCOUNT_AWAITING_ACTIVATION) {
718 // Awaiting user activation, activate:
719 DB_change($_TABLES['users'], 'status', USER_ACCOUNT_ACTIVE,
720 'username', $username);
721 return USER_ACCOUNT_ACTIVE;
723 return $U['status']; // just return their status
726 $tmp = $LANG01[32] . ": '" . $username . "'";
727 COM_errorLog($tmp, 1);
733 * Return the current user status for a user.
735 * NOTE: May not return for banned/non-approved users.
737 * @param int $userid Valid uid value.
738 * @return int user status, 0-3
741 function SEC_checkUserStatus($userid)
743 global $_CONF, $_TABLES;
746 $status = DB_getItem($_TABLES['users'], 'status', "uid=$userid");
748 // only do redirects if we aren't on users.php in a valid mode (logout or
750 if (strpos($_SERVER['PHP_SELF'], 'users.php') === false) {
753 if (empty($_REQUEST['mode']) || ($_REQUEST['mode'] == 'logout')) {
759 if ($status == USER_ACCOUNT_AWAITING_ACTIVATION) {
760 DB_change($_TABLES['users'], 'status', USER_ACCOUNT_ACTIVE, 'uid', $userid);
761 } elseif ($status == USER_ACCOUNT_AWAITING_APPROVAL) {
762 // If we aren't on users.php with a default action then go to it
764 COM_accessLog("SECURITY: Attempted Cookie Session login from user awaiting approval $userid.");
765 echo COM_refresh($_CONF['site_url'] . '/users.php?msg=70');
768 } elseif ($status == USER_ACCOUNT_DISABLED) {
770 COM_accessLog("SECURITY: Attempted Cookie Session login from banned user $userid.");
771 echo COM_refresh($_CONF['site_url'] . '/users.php?msg=69');
780 * Check to see if we can authenticate this user with a remote server
782 * A user has not managed to login localy, but has an @ in their user
783 * name and we have enabled distributed authentication. Firstly, try to
784 * see if we have cached the module that we used to authenticate them
785 * when they signed up (i.e. they've actualy changed their password
786 * elsewhere and we need to synch.) If not, then try to authenticate
787 * them with /every/ authentication module. If this suceeds, create
790 * @param string $loginname Their username
791 * @param string $passwd The password entered
792 * @param string $server The server portion of $username
793 * @param string $uid OUTPUT parameter, pass it by ref to get uid back.
794 * @return int user status, -1 for fail.
796 function SEC_remoteAuthentication(&$loginname, $passwd, $service, &$uid)
798 global $_CONF, $_TABLES;
800 /* First try a local cached login */
801 $remoteusername = addslashes($loginname);
802 $remoteservice = addslashes($service);
803 $result = DB_query("SELECT passwd, status, uid FROM {$_TABLES['users']} WHERE remoteusername='$remoteusername' AND remoteservice='$remoteservice'");
805 $nrows = DB_numRows($result);
806 if (($tmp == 0) && ($nrows == 1)) {
807 $U = DB_fetchArray($result);
809 $mypass = $U['passwd']; // also used to see if the user existed later.
810 if ($mypass == SEC_encryptPassword($passwd)) {
811 /* Valid password for cached user, return status */
816 $service = COM_sanitizeFilename($service);
817 $servicefile = $_CONF['path_system'] . 'classes/authentication/' . $service
819 if (file_exists($servicefile)) {
820 require_once $servicefile;
822 $authmodule = new $service();
823 if ($authmodule->authenticate($loginname, $passwd)) {
824 /* check to see if they have logged in before: */
825 if (empty($mypass)) {
826 // no such user, create them
828 // Check to see if their remoteusername is unique locally
829 $checkName = DB_getItem($_TABLES['users'], 'username',
830 "username='$remoteusername'");
831 if (!empty($checkName)) {
832 // no, call custom function.
833 if (function_exists('CUSTOM_uniqueRemoteUsername')) {
834 $loginname = CUSTOM_uniqueRemoteUsername($loginname,
838 USER_createAccount($loginname, $authmodule->email, SEC_encryptPassword($passwd), $authmodule->fullname, $authmodule->homepage, $remoteusername, $remoteservice);
839 $uid = DB_getItem($_TABLES['users'], 'uid', "remoteusername = '$remoteusername' AND remoteservice='$remoteservice'");
840 // Store full remote account name:
841 DB_query("UPDATE {$_TABLES['users']} SET remoteusername='$remoteusername', remoteservice='$remoteservice', status=3 WHERE uid='$uid'");
842 // Add to remote users:
843 $remote_grp = DB_getItem($_TABLES['groups'], 'grp_id',
844 "grp_name='Remote Users'");
845 DB_query("INSERT INTO {$_TABLES['group_assignments']} (ug_main_grp_id,ug_uid) VALUES ($remote_grp, $uid)");
846 return 3; // Remote auth precludes usersubmission,
847 // and integrates user activation, see?
849 // user existed, update local password:
850 DB_change($_TABLES['users'], 'passwd', SEC_encryptPassword($passwd), array('remoteusername','remoteservice'), array($remoteusername,$remoteservice));
851 // and return their status
852 return DB_getItem($_TABLES['users'], 'status', "remoteusername='$remoteusername' AND remoteservice='$remoteservice'");
863 * Return available modules for Remote Authentication
865 * @return array Names of available remote authentication modules
868 function SEC_collectRemoteAuthenticationModules()
874 $modulespath = $_CONF['path_system'] . 'classes/authentication/';
875 if (is_dir($modulespath)) {
876 $folder = opendir($modulespath);
877 while (($filename = @readdir($folder)) !== false) {
878 $pos = strpos($filename, '.auth.class.php');
879 if ($pos && (substr($filename, strlen($filename) - 4) == '.php')) {
880 $modules[] = substr($filename, 0, $pos);
889 * Add user to a group
893 * Rather self explanitory shortcut function
894 * Is this the right place for this, Dirk?
896 * @author Trinity L Bays, trinity93 AT gmail DOT com
898 * @param string $uid Their user id
899 * @param string $gname The group name
900 * @return boolean status, true or false.
902 function SEC_addUserToGroup($uid, $gname)
904 global $_TABLES, $_CONF;
906 $remote_grp = DB_getItem ($_TABLES['groups'], 'grp_id', "grp_name='". $gname ."'");
907 DB_query ("INSERT INTO {$_TABLES['group_assignments']} (ug_main_grp_id,ug_uid) VALUES ($remote_grp, $uid)");
911 * Set default permissions for an object
913 * @param array $A target array
914 * @param array $use_permissions permissions to set
917 function SEC_setDefaultPermissions (&$A, $use_permissions = array ())
919 if (!is_array ($use_permissions) || (count ($use_permissions) != 4)) {
920 $use_permissions = array (3, 2, 2, 2);
924 if (($use_permissions[0] > 3) || ($use_permissions[0] < 0) ||
925 ($use_permissions[0] == 1)) {
926 $use_permissions[0] = 3;
928 if (($use_permissions[1] > 3) || ($use_permissions[1] < 0) ||
929 ($use_permissions[1] == 1)) {
930 $use_permissions[1] = 2;
932 if (($use_permissions[2] != 2) && ($use_permissions[2] != 0)) {
933 $use_permissions[2] = 2;
935 if (($use_permissions[3] != 2) && ($use_permissions[3] != 0)) {
936 $use_permissions[3] = 2;
939 $A['perm_owner'] = $use_permissions[0];
940 $A['perm_group'] = $use_permissions[1];
941 $A['perm_members'] = $use_permissions[2];
942 $A['perm_anon'] = $use_permissions[3];
947 * Common function used to build group access SQL
949 * @param string $clause Optional parm 'WHERE' - default is 'AND'
950 * @return string $groupsql Formatted SQL string to be appended in calling script SQL statement
952 function SEC_buildAccessSql ($clause = 'AND')
954 global $_TABLES, $_USER;
956 if (isset($_USER) AND $_USER['uid'] > 1) {
957 $uid = $_USER['uid'];
962 $_GROUPS = SEC_getUserGroups($uid);
964 if (count($_GROUPS) == 1) {
965 $groupsql .= " $clause grp_access = '" . current($_GROUPS) ."'";
967 $groupsql .= " $clause grp_access IN (" . implode(',',array_values($_GROUPS)) .")";
974 * Remove a feature from the database entirely.
976 * This function can be used by plugins during uninstall.
978 * @param string $feature_name name of the feature, e.g. 'foo.edit'
979 * @param boolean $logging whether to log progress in error.log
983 function SEC_removeFeatureFromDB ($feature_name, $logging = false)
987 if (!empty ($feature_name)) {
988 $feat_id = DB_getItem ($_TABLES['features'], 'ft_id',
989 "ft_name = '$feature_name'");
990 if (!empty ($feat_id)) {
991 // Before removing the feature itself, remove it from all groups
993 COM_errorLog ("Attempting to remove '$feature_name' rights from all groups", 1);
995 DB_delete ($_TABLES['access'], 'acc_ft_id', $feat_id);
997 COM_errorLog ('...success', 1);
1000 // now remove the feature itself
1002 COM_errorLog ("Attempting to remove the '$feature_name' feature", 1);
1004 DB_delete ($_TABLES['features'], 'ft_id', $feat_id);
1006 COM_errorLog ('...success', 1);
1008 } else if ($logging) {
1009 COM_errorLog ("SEC_removeFeatureFromDB: Feature '$feature_name' not found.");
1015 * Create a group dropdown
1017 * Creates the group dropdown menu that's used on pretty much every admin page
1019 * @param int $group_id current group id (to be selected)
1020 * @param int $access access permission
1021 * @return string HTML for the dropdown
1024 function SEC_getGroupDropdown ($group_id, $access)
1031 $usergroups = SEC_getUserGroups ();
1033 $groupdd .= '<select name="group_id">' . LB;
1034 foreach ($usergroups as $ug_name => $ug_id) {
1035 $groupdd .= '<option value="' . $ug_id . '"';
1036 if ($group_id == $ug_id) {
1037 $groupdd .= ' selected="selected"';
1039 $groupdd .= '>' . ucwords($ug_name) . '</option>' . LB;
1041 $groupdd .= '</select>' . LB;
1043 // They can't set the group then
1044 $groupdd .= DB_getItem ($_TABLES['groups'], 'grp_name',
1045 "grp_id = '$group_id'")
1046 . '<input type="hidden" name="group_id" value="' . $group_id
1047 . '"' . XHTML . '>';
1056 * For now, this is only a wrapper function to get all the direct calls to
1057 * md5() out of the core code so that we can switch to another method of
1058 * encoding / encrypting our passwords in some future release ...
1060 * @param string $password the password to encrypt, in clear text
1061 * @return string encrypted password
1064 function SEC_encryptPassword($password)
1066 return md5($password);
1070 * Generate a security token.
1072 * This generates and stores a one time security token. Security tokens are
1073 * added to forms and urls in the admin section as a non-cookie double-check
1074 * that the admin user really wanted to do that...
1076 * @param $ttl int Time to live for token in seconds. Default is 20 minutes.
1078 * @return string Generated token, it'll be an MD5 hash (32chars)
1080 function SEC_createToken($ttl = 1200)
1082 global $_USER, $_TABLES, $_DB_dbms;
1086 if (isset($last_token)) {
1090 /* Figure out the full url to the current page */
1091 $pageURL = COM_getCurrentURL();
1093 /* Generate the token */
1094 $token = md5($_USER['uid'].$pageURL.uniqid (rand (), 1));
1095 $pageURL = addslashes($pageURL);
1097 /* Destroy exired tokens: */
1098 $sql['mssql'] = "DELETE FROM {$_TABLES['tokens']} WHERE (DATEADD(ss, ttl, created) < NOW()) AND (ttl > 0)";
1099 $sql['mysql'] = "DELETE FROM {$_TABLES['tokens']} WHERE (DATE_ADD(created, INTERVAL ttl SECOND) < NOW()) AND (ttl > 0)";
1102 /* Destroy tokens for this user/url combination */
1103 $sql = "DELETE FROM {$_TABLES['tokens']} WHERE owner_id={$_USER['uid']} AND urlfor='$pageURL'";
1106 /* Create a token for this user/url combination */
1107 /* NOTE: TTL mapping for PageURL not yet implemented */
1108 $sql = "INSERT INTO {$_TABLES['tokens']} (token, created, owner_id, urlfor, ttl) "
1109 . "VALUES ('$token', NOW(), {$_USER['uid']}, '$pageURL', $ttl)";
1112 $last_token = $token;
1114 /* And return the token to the user */
1119 * Check a security token.
1121 * Checks the POST and GET data for a security token, if one exists, validates that it's for this
1124 * @return boolean true if the token is valid and for this user.
1126 function SEC_checkToken()
1128 global $_USER, $_TABLES, $_DB_dbms;
1130 $token = ''; // Default to no token.
1131 $return = false; // Default to fail.
1133 if(array_key_exists(CSRF_TOKEN, $_GET)) {
1134 $token = COM_applyFilter($_GET[CSRF_TOKEN]);
1135 } else if(array_key_exists(CSRF_TOKEN, $_POST)) {
1136 $token = COM_applyFilter($_POST[CSRF_TOKEN]);
1139 if (trim($token) != '') {
1140 $sql['mysql'] = "SELECT ((DATE_ADD(created, INTERVAL ttl SECOND) < NOW()) AND ttl > 0) as expired, owner_id, urlfor FROM {$_TABLES['tokens']} WHERE token='$token'";
1141 $sql['mssql'] = "SELECT owner_id, urlfor, expired =
1143 WHEN (DATEADD(s,ttl,created) < getUTCDate()) AND (ttl>0) THEN 1
1147 FROM {$_TABLES['tokens']} WHERE token='$token'";
1148 $tokens = DB_query($sql);
1150 $numberOfTokens = DB_numRows($tokens);
1151 if($numberOfTokens != 1) {
1152 $return = false; // none, or multiple tokens. Both are invalid. (token is unique key...)
1154 $tokendata = DB_fetchArray($tokens);
1156 * token's user is the current user.
1157 * token is not expired.
1158 * the http referer is the url for which the token was created.
1160 if( $_USER['uid'] != $tokendata['owner_id'] ) {
1162 } else if($tokendata['urlfor'] != $_SERVER['HTTP_REFERER']) {
1164 } else if($tokendata['expired']) {
1167 $return = true; // Everything is AOK in only one condition...
1170 // It's a one time token. So eat it.
1171 DB_delete($_TABLES['tokens'], 'token', $token);
1174 $return = false; // no token.
1181 * Get a token's expiry time
1183 * @param string $token the token we're looking for
1184 * @return int UNIX timestamp of the expiry time or 0
1187 function SEC_getTokenExpiryTime($token)
1189 global $_TABLES, $_USER;
1193 if (!COM_isAnonUser()) {
1195 $sql['mysql'] = "SELECT UNIX_TIMESTAMP(DATE_ADD(created, INTERVAL ttl SECOND)) AS expirytime FROM {$_TABLES['tokens']} WHERE (token = '$token') AND (owner_id = '{$_USER['uid']}') AND (ttl > 0)";
1196 $sql['mssql'] = "SELECT UNIX_TIMESTAMP(DATEADD(ss, ttl, created)) AS expirytime FROM {$_TABLES['tokens']} WHERE (token = '$token') AND (owner_id = '{$_USER['uid']}') AND (ttl > 0)";
1198 $result = DB_query($sql);
1199 if (DB_numRows($result) == 1) {
1200 list($retval) = DB_fetchArray($result);
1208 * Set a cookie using the HttpOnly flag
1210 * Use this function to set "important" cookies (session, password, ...).
1211 * Browsers that support the HttpOnly flag will not allow JavaScript access
1214 * @param string $name cookie name
1215 * @param string $value cookie value
1216 * @param int $expire expire time
1217 * @param string $path path on the server or $_CONF['cookie_path']
1218 * @param string $domain domain or $_CONF['cookiedomain']
1219 * @param bool $secure whether to use HTTPS or $_CONF['cookiesecure']
1220 * @link http://blog.mattmecham.com/2006/09/12/http-only-cookies-without-php-52/
1223 function SEC_setCookie($name, $value, $expire = 0, $path = null, $domain = null, $secure = null)
1229 if ($path === null) {
1230 $path = $_CONF['cookie_path'];
1232 if ($domain === null) {
1233 $domain = $_CONF['cookiedomain'];
1235 if ($secure === null) {
1236 $secure = $_CONF['cookiesecure'];
1239 // the httponly parameter is only available as of PHP 5.2.0
1240 if (version_compare(PHP_VERSION, '5.2.0', '>=')) {
1241 $retval = setcookie($name, $value, $expire, $path, $domain, $secure,
1244 // fake it for older PHP versions; kudos to Matt Mecham
1245 $retval = setcookie($name, $value, $expire, $path,
1246 $domain . '; httponly', $secure);
1253 * Prepare an array of the standard permission values
1255 * This helper functions does the following:
1256 * 1) filter permission values, e.g. after a POST request
1257 * 2) translates the permission checkbox arrays into numerical values
1258 * 3) ensures that all the standard permission entries are set, so you don't
1259 * have to check with isset() all the time
1262 * $PERM = SEC_filterPermissions($_POST);
1263 * if ($PERM['perm_anon'] != 0) { ...
1266 * @param array $A array to filter on, e.g. $_POST
1267 * @return array array of only the 6 standard permission values
1268 * @see SEC_getPermissionValues
1271 function SEC_filterPermissions($A)
1275 if (isset($A['owner_id'])) {
1276 $retval['owner_id'] = COM_applyFilter($A['owner_id'], true);
1278 $retval['owner_id'] = 0;
1281 if (isset($A['group_id'])) {
1282 $retval['group_id'] = COM_applyFilter($A['group_id'], true);
1284 $retval['group_id'] = 0;
1287 $perms = array('perm_owner', 'perm_group', 'perm_members', 'perm_anon');
1290 foreach ($perms as $p) {
1291 if (isset($A[$p])) {
1298 $B = SEC_getPermissionValues($B['perm_owner'], $B['perm_group'],
1299 $B['perm_members'], $B['perm_anon']);
1300 for ($i = 0; $i < 4; $i++) {
1301 $retval[$perms[$i]] = $B[$i];
1308 * Helper function for when you want to call SEC_hasAccess and have all the
1309 * values to check in an array.
1311 * @param array $A array with the standard permission values
1312 * @return int returns 3 for read/edit 2 for read only 0 for no access
1313 * @see SEC_hasAccess
1316 function SEC_hasAccess2($A)
1318 return SEC_hasAccess($A['owner_id'], $A['group_id'], $A['perm_owner'],
1319 $A['perm_group'], $A['perm_members'], $A['perm_anon']);