UserAdmin

Summary: AuthUser account self-registration and management
Version: 2016-08-23
UserAdmin-dbase Version: 2015-09-20
UserAdmin-profiles Version: 2015-05-16
UserAdmin-authuser Version: 2015-05-16
Prerequisites: PHP 5.4 or newer, PmWiki 2.2.17 or newer, AuthUser
Status: beta
Maintainer: Peter Bowers
Users: (View? / Edit?)
Discussion: UserAdmin-Talk

Description

User account self-registration and management

This recipe is quite functional but still under development & needs more testing before full-scale use. Testers welcome (see below for available testing site).

To facilitate testing, 3 sites have been prepared to test the authuser, the profiles, and the database storage solutions. Feel free to auto-register yourself, try edge conditions, basically see how it works and if there's anything not working...

To install:

UserAdmin supports multiple storage solutions to store users.

  1. user information is stored in the SiteAdmin.AuthUser page (with a slightly extended syntax)
  2. user information is stored in Profiles page in invisible attributes
  3. user information is stored in a database (currently only tested with mysql) accessed via PDO.

Functionality and user interface is identical between the 3 solutions -- the only differentiation is where and how the information is stored behind the scenes.

To install with user & authentication information in SiteAdmin.AuthUser page:

include_once("$FarmD/cookbook/useradmin-authuser.php");

To install with user & authentication information in the Profiles page:

include_once("$FarmD/cookbook/useradmin-profiles.php");
  • add the following line to your SiteAdmin.AuthUser page:
userprofilegroup: Profiles

To install with user & authentication information in a database:

  • download useradmin-core.phpΔ and useradmin-dbase.phpΔ to your cookbook directory.
  • add the following code to your configuration file before including authuser.php (make appropriate substitutions for the name of the host, name of the database, db user, and db password):
include_once("$FarmD/cookbook/useradmin-dbase.php");
$UserAdmin = 
        new UserAdminDbase('mysql:host=name.of.host;dbname=name_of_db;charset=utf8', 
            'db_user', 
            'db_pass', 
            array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
            'db_prefix');
  • The first 4 parameters correspond to those used in the PDO::__construct() except only the 4th is optional.
  • The fifth (optional) parameter allows tablenames to be prefixed with a string if the database already contains tables with the same names.
    • For instance, if you specify "ua_" as the 5th parameter then instead of your tables being named "users" and "groups" they will be named "ua_users" and "ua_groups"
  • add the following line to your SiteAdmin.AuthUser page:
useradmindb: something
  • Log in as an admin user to your system and append ?action=user/install to the URL and load a page - this will create the tables in your database
    • If the files already exist they are not touched. If you need to recreate them for some reason you will have to manually drop them from the database first.

Configuration options

Configuring redirect pages on success/fail of actions

It is possible to specify custom redirect destinations to be used upon successful or failing completion of any UserAdmin action. To do this, set values within the $UAredirects array in the config.php:

$UAredirects = array(
    'UAunlock_success_activated' => 'Mygroup/Welcome', 
    'UAunlock_fail' => 'Mygroup/YouLose');

When the action is complete and success/failure would normally have returned the user to UA_return_link (set via XLSDV()), users will instead be redirected to the URL provided in the $UAredirects array.

In the example above "user/unlock" is the action. To learn the exact values for the keys to the array, look in useradmin-core.php and search for "_success" or "_fail" in the XLSDV() section. (approximately between lines 53 and 150) Do note that if you do not have the exactly matching key then the redirect will not work.

Configuring how Superuser is identified

There are multiple ways Superuser can be determined. Depending on the value of $UserAdmin->SuperuserFunc you can change how superuser is determined:

  • 'Superuser' = If someone has admin rights they are considered superuser (this is the default for useradmin-dbase and active in all other methods as well - being admin always makes you superuser)
  • 'SuperuserAuthUserEdit' = If someone has edit privileges on SiteAdmin.AuthUser page then they are considered superuser (this is the default for useradmin-authuser)
  • 'SuperuserProfilesAttr' = If someone has attr privileges on their own profiles page then they are considered superuser (this is the default for useradmin-profiles)

The value of $UserAdmin->SuperuserFunc can be set after including the main file (see installation above). For instance, if you were using useradmin-profiles storage method but wanted the normal admin password to define superuser then you might have this in your config.php:

include_once("$FarmD/cookbook/useradmin-profiles.php");
if (isset($UserAdmin)) $UserAdmin->SuperuserFunc = 'Superuser';

Configuring the main menu for UserAdmin options

The main menu can be over-ridden by a #ua-mainmenu template -- see Custom Forms below for more details.

If it is not over-ridden by a template then the main menu is constructed from the global variable $UserAdminActions. Its default initialization follows:

        SDVA($UserAdminActions, array(
                'onlyadmin' => array('group', 'install'),
                'onlyauth' => array('edit', 'loginother'),
                'anonymous' => array('resetpasswd', 'new', 'unlock', 'login2edit', 'login'),
                'onlyanonymous' => array('login2edit', 'login'),
                'unlock' => array('resetpasswd', 'new', 'unlock', 'login2edit', 'login', 'loginother'),
                'extra' => array('login2edit', 'login', 'loginother'),
                'invisible' => array('install'),
            ));

The concept is that we begin with a set of all possible actions.

  1. Any elements in the array element extra are added.
  2. Elements in the array element invisible are removed (these are available by URL editing but not visible in the menu)
  3. If the current user is not an administrator then actions in the array element onlyadmin will be removed from the menu.
  4. If the current user is not logged in then actions in the array element onlyauth will be removed from the menu and (as an additional measure of security) the possible actions will be intersected with the array element anonymous.
  5. If a user is logged in then actions in the array element onlyanonymous will be removed from the menu.
  6. If the current action (new, edit, unlock, resetpasswd, etc.) has a corresponding array element in $UserAdminActions then it will be intersected with the set of all possible actions, reducing the possible actions.

For each action there are 1 optional and 1 required XL string (in each case action is substituted by the actual action name from above - see Internationalizations and particularly XLSDV() for setting these strings):

  • UAaction_link -- this value is a URL which executes the action
    • If this XL string is not available then it defaults to $ScriptUrl/Cookbook/UserAdmin?action=user/action
  • UAaction_title -- this value will be displayed to the user as the actual menu item text

Thus the main menu can be configured very flexibly by altering the various elements of the $UserAdminActions array within the config.php or by altering the XL strings for the various links and titles (XL changes must be configured before including UserAdmin in config.php). For instance, to eliminate the "Login to edit your user profile" option, change the wording and destination on the "Login to continue" option, and add a new "logout" option you would have something like this:

XLSDV('en', array(
   'UAlogin_link' => 'Profiles.{$AuthId}',
   'UAlogin_title' => 'Login and view your profile',
   'UAlogout_link' => '{$FullName}?action=logout',
   'UAlogout_title' => 'Logout',
));
include_once("$FarmD/cookbook/useradmin-authuser");
$UserAdminActions['extra'] = array('login', 'logout'); // added 'logout', removed 'login2edit'
$UserAdminActions['onlyauth'] = array('edit', 'logout'); // added 'logout'

Custom forms & menus (optional)

By default, UserAdmin will generate the forms & menus used by the recipe directly without the page [[Site.UserAdminTemplates]]. However, you may also define these forms using wiki markup in that page. For a sample implementation, copy/paste the source on the page Cookbook.UserAdminTemplates to your site with the name Site.UserAdminTemplates. These templates may later become a part of the default UserAdmin installation.

Be aware that if you define custom forms and menus then UserAdmin no longer uses the internal mechanism which shows only available actions. This may result in confusion by users if it is not carefully configured.

Currently the following section names will be honored (each of these can also have a -admin suffix):

  • #ua-new
  • #ua-edit
  • #ua-resetpasswd
  • #ua-unlock
  • #ua-mainmenu

If you wish to include a list of groups as checkboxes in #ua-new or #ua-edit (they will be disabled [view only - still useful] to show/update group memberships for a given user then include the text {{USERGROUPS}} in your markup within the sections on Site.UserAdminTemplates.

Note that #ua-group is not configurable in this way.

The naming of the section is significant. It begins with #ua- followed by the action name followed by an option -admin if the form is specific for administrators.

Custom fields can also be added using these custom forms.

For instance, this source, when placed in your Site.UserAdminTemplates page, will add a new custom field userrealname:

[[#ua-new]]
(:input form action='$PageUrl?action=user/new':)
(:table class=ua-form:)
(:cellnr:)$[UA_txt_username]
(:cell:)(:input text name='username':)
(:cellnr:)$[UA_txt_userpasswd]
(:cell:)(:input password name='userpasswd':)
(:cellnr:)$[UA_txt_userpasswd2]
(:cell:)(:input password name='userpasswd2':)
(:cellnr:)$[UA_txt_useremail]
(:cell:)(:input text name='useremail':)
(:cellnr:)Real Name
(:cell:)(:input text name='userrealname':)
(:cellnr:)
(:cell:)(:input submit name=post value='$[UAnew_submit]':) (:input submit name=cancel value='$[Cancel]':)
(:tableend:)
(:input end:)
[[#ua-newend]]

[[#ua-edit]]
(:input form action='$PageUrl?action=user/edit':)
(:table class=ua-form:)
(:cellnr:)$[UA_txt_username]
(:cell:)(:input text readonly=1 name='username':)
(:cellnr:)$[UA_txt_useroldpasswd]
(:cell:)(:input password name='useroldpasswd':)
(:cellnr:)$[UA_txt_userpasswd]
(:cell:)(:input password name='userpasswd':)
(:cellnr:)$[UA_txt_userpasswd2]
(:cell:)(:input password name='userpasswd2':)
(:cellnr:)$[UA_txt_useremail]
(:cell:)(:input text name='useremail':)
(:cellnr:)Real Name
(:cell:)(:input text name='userrealname':)
(:cellnr:)
(:cell:)(:input submit name=post value='$[UAedit_submit]':) (:input submit name=cancel value='$[Cancel]':)
(:tableend:)
(:input end:)
[[#ua-editend]]

[[#ua-edit-admin]]
(:input form action='$PageUrl?action=user/edit':)
(:table class=ua-form:)
(:cellnr:)$[UA_txt_username]
(:cell:)(:input text readonly=1 name='username':)
(:cellnr:)$[UA_txt_userpasswd]
(:cell:)(:input password name='userpasswd':)
(:cellnr:)$[UA_txt_userpasswd2]
(:cell:)(:input password name='userpasswd2':)
(:cellnr:)$[UA_txt_useremail]
(:cell:)(:input text name='useremail':)
(:cellnr:)Real Name
(:cell:)(:input text name='userrealname':)
(:cellnr:)
(:cell:)(:input submit name=post value='$[UAedit_submit]':) (:input submit name=cancel value='$[Cancel]':)
(:tableend:)
(:input end:)
[[#ua-edit-adminend]]

Naming of custom fields must follow this rule: All custom field names MUST begin with "user" (userrealname and userfieldx - not just realname and fieldx).

If you are using the useradmin-dbase variant then custom fields must be added to the "user" database table manually using an external tool such as phpMyAdmin.

Usage

To get a menu of available actions, add ?action=user to any page where the recipe is enabled. Currently available actions are dynamic, based on current logged-in status and etc. All actions can be accessed by manually appending ?action=user/action to the URL of the current page.

The recipe has the following functionality:

Self-registering a new user, with optional email address confirmation: ?action=user/new

To use, add ?action=user/new to any page where the recipe is enabled. You'll be presented with a form asking for a username, password and email address. These will be verified, and the data added to the chosen storage method (authuser, dbase, profiles) along with a randomly generated key. An activation link (including said key) will be mailed to the given address, and when said link is followed, the account activated.

PROFILE-METHOD NOTE: Using a profile page for a new user is allowed even if the page already exists, provided that it doesn't have a user account previously associated with it.

If you are logged in as an administrator when you use the ?action=user/new then there is no email address confirmation and the user is automatically created as a verified user. Administrators can also edit group memberships on this form.

Password reset via e-mail: ?action=user/resetpasswd

Send a reset link to a user's e-mail address. The link will contain a key which will let the user set a new password.

Edit user account details: ?action=user/edit

Change the password and/or the e-mail address associated with the account. Requires the user to be either logged-in as themselves and to re-enter their password, or to have admin access (see this section for a description of admin access for each storage method.).

Activate an account: ?action=user/unlock

This is used as the second step of both account activation and password resets. It will normally be reached by using the link sent in an email address. A random key is sent in the email to assure that the user requesting the activation/reset is the one actually getting the account.

Notes

UserAdmin-AuthUser data format

In a normal authuser environment users are stored in a format such as this:

username: HASH @group HASH2 @group2

where HASH and HASH2 represent encrypted passwords and @group and @group2 represent group memberships.

When UserAdmin-AuthUser is used, the additional user properties are stored in ParseArgs() format following a second colon:

username: HASH @group : useremail=user@example.com userRealName="John Jacob Jingleheimerschmidt"

UserAdmin-AuthUser data format

Information about users is stored in a database with tables users, groups, and groupmembers.

Access to the database is via PDO as described in the installation section above.

UserAdmin-Profiles data format

Information about each user is kept as profile page attributes. The appropriate page is generated from the username using MakePageName, but the page itself also contains the username as an attribute. This means that usernames don't need to be valid page names, but they still each map to a unique profile page.

The attributes used by the recipe share a common prefix user; by default at least the following attributes are used: username, useremail, userpwhash, and userkey. userkey contains a random number that acts as an activation key and a hash of the user's password. Once the account is activated, userkey is removed and userpwhash set to the hash, allowing the account to be used.

Reading profile pages for user data is enabled by including the line "userprofilegroup: Profiles" on the SiteAdmin.AuthUser page, which lets AuthUserId call AuthUserProfiles, defined in useradmin-profiles.php. Reading user group data from profile pages (as an attribute usergroups, holding a comma or white space delimited list of @groups to which the user belongs) requires a slight modification to authuser.php, as shown near the bottom of PITS.01197.

Managing user data in other places

In order to put together your own solution for managing users, you may extend the core UserAdmin class by implementing at least the ReadUser and WriteUser methods.

The code includes some PHP silliness for eg. field validation: if you have an input field "userpasswd", you can extend UserAdmin with a class that includes a method called "ValidPasswd" and it'll automagically get used to validate the field. Similar stuff happens for the different sub-actions of the recipe; defining UserAdminX::HandleFroot will make the action accessible via ?action=user/froot as well as listing it in ?action=user.

Et cetera

If you look through the code there are a number of globals you can set to control the recipe's behaviour. These should get documented, but they may also get completely renamed, removed or moved within the class declaration.

Proposed/Requested Features / Roadmap

  • When editing a user it should be possible to add/delete membership in groups from that form DONE
  • After I edit an account, I would like to be returned to the UA_return_link that I set. Having UA_return_link specify a location after completing the form would be a new feature. Or having some other way to specify a specific destination rather than just curpage?action=user... DONE ($UAredirects)
  • After adding custom fields, these should be available as template vars ({$$var}) with appropriate substitution in contexts such as the email being sent to the user. What other contexts [besides the email] should these template variables be substituted?
  • Administrator option: user/suspend (for a certain number of days)
  • Administrator option: user/delete
  • Add PVs for all (?) fields It is unclear how this would be implemented since pmwiki allows a session to have multiple simultaneous logins.
  • Add for use in conditionals in templates
  • Fix #ua-mainmenu template so it works for action=unlock to have just the login option
  • Add the ability to lock certain fields from being updated by users (perhaps admins can do this simply by means of templates?)
  • Add the ability to lock down certain accounts to not be editable by users (multi-user accounts, for instance) (perhaps admins can do this simply by means of templates?)
  • Add the ability to change password DONE
  • When using UserAdmin-authuser, allow the administrator to choose "user: @group1 @group2" or "@group: user1 user2" format
  • Password reset is synonymous in some cases for edit profile. Either the menu wording should reflect this or the functionality should be different, allowing edit of only password instead of all profile.
  • Allow concepts of super-admins and per-group-admins and open-groups so as to facilitate editing group membership by users who are not otherwise admin-authorized and to allow people to join certain groups that are open without any kind of administrative intervention. It's got significant security ramifications and so needs to be thought through very carefully.

Known problems

  • Cannot set (:title ...:) in UserAdminTemplates sections.
  • Logged in as admin, I clicked on email link to reset password. It asked me to select a user. I did. Nothing happened.
  • The built-in menu includes "account activation". "What's that?" asks the user. This option allows a user to copy/paste the activate code into a form (rather than clicking on the link in the email)
  • The menu includes "installation" for admins when using dbase - it shouldn't exist in a menu. FIXED - now invisible
  • A logged in user sees an option to sign up in the built-in available actions, even though he's already logged in. That's correct. You don't have to log out before registering a new user. PmWiki allows multiple simultaneous logins.
  • There may be a conflict with RequireSummary. FIXED
  • Reported issue with UserAdmin-authuser where other information in SiteAdmin.AuthUser is changed when saving via UserAdmin.
  • Use of preg_replace('/.../e'...) at line 1173 - problems with PHP 5.5

Release notes

  • 2015-09-20: coreΔ. Changes for PHP55 compatibility (getting rid of preg_replace('/.../e', ...) calls).
  • 2015-09-20: coreΔ and dbaseΔ. Improve user experience during dbase install action. Fix message when validating '@' prefix on group names. Add $UAredirects functionality to redirect to a configured URL after any success/fail action. Menu changed to add "Login as another user" when already logged in. Added capability to edit group memberships upon creation of a user by an admin. Having typical pmwiki admin privileges now always results in useradmin superuser privileges. Added additional menu options after activation or password reset. Allowed presence of {{USERGROUPS}} in custom forms to facilitate editing group memberships for users. Hopefully resolved issues where non-useradmin actions were intercepted by useradmin.
  • 2015-05-16: coreΔ and authuserΔ and profileΔ and dbaseΔ. Added the capability of viewing/changing a user's groups while you are editing the user (rather than editing the group). Added the $UserAdmin->SuperuserFunc configuration capability to allow different methods of defining superuser. Fixed some minor bugs.
  • 2015-05-11: coreΔ and authuserΔ and profileΔ and dbaseΔ. Fixed

an error related to resetting passwords - it was giving a blank page to admins and now allows admins to edit the user's account.

  • 2015-05-10: coreΔ and authuserΔ and profileΔ and dbaseΔ. Fixed a bug in dbase which made an unknown action act like ?action=user. Fixed the bug related to RequireSummary.
  • 2015-05-08: coreΔ and authuserΔ and profileΔ and dbaseΔ. Groups (including nested groups) implemented in the dbase storage solution. I think the dbase storage method is ready for primetime testing as opposed to being just a dev version.
  • 2015-04-29: coreΔ and authuserΔ and profileΔ and dbaseΔ. Making a valiant effort to get this development moving forward again as I think it is one of the key "holes" in pmwiki currently. I *believe* I have the profiles version brought back up-to-date with core and authuser versions. What is really needed is some testers who will take it for a spin... (The dbase version is not even fully functional - more of a backup of my development version.)
  • 2011-04-11: coreΔ and authuserΔ. Rework the main menu for greater configurability. $UserAdminAnonActions no longer used. $UserAdminActions is now a multi-dimensional array, allowing administrators to add their own links and give greater or lesser privileges to admins, logged-on-users, and anonymous users. Also capability of adding new template-sections, #ua-mainmenu and #ua-mainmenu-admin, was added. See the configuration section for more details.
  • 2011-04-10: coreΔ and authuserΔ. Add capability of setting $UserAdmin->pagename to provide a default page for links and etc. Fix menus to provide only authorized activities. Added configurable $UserAdminActions['ACTION'] = array(...) to allow for limiting the menu options at the conclusion of certain actions (such as after unlocking a user account - the new default is to only show a "log in to edit your account" option). Fixed group editing to provide appropriate messages, better error checking, added cancel option, etc.
  • 2011-04-09: coreΔ and authuserΔ. Remove some debug statements that slipped through in 2011-04-08. No changed/additional functionality.
  • 2011-04-08: coreΔ and authuserΔ. No longer require login to reset password. Just require username and email to match in order to get a key emailed. Links in the list of users as well as links in emails now respect current page rather than changing to home page.
  • 2011-04-07: coreΔ and authuserΔ. Fix problem related to Superuser() definition in AuthUser. Add email templates for easier customization (See UserAdminTempaltes? for defaults.)
  • 2011-04-05: coreΔ and authuserΔ. Definition of Superuser() altered as per Randy's suggestion, whoever has edit privileges on AuthUser. Debugging message removed. Capability to add a new group added. Suppressed viewing of list of users (potential security hole) on account activation. Fixed editing of empty groups.
  • 2011-04-04a: coreΔ and authuserΔ. Fixed the same problem with passwords in cleartext, but this time for user/edit since before I just fixed it for user/new.
  • 2011-04-04: coreΔ and authuserΔ. Fixed a problem where passwords were being stored in cleartext in authuser as user properties. Added #ua-ACTION-admin template capabilities for administrator-specific forms.
  • 2011-04-03: coreΔ and authuserΔ - largely functional. Useradmin-profiles no longer functional. Group editing is functional. New user and edit user work well in limited testing. Use of custom forms in Site.UserAdminTemplates is now functional. A bug was fixed that previously it was possible to get a list of groups and users even if you were not logged in as administrator - this possibility has been eliminated in known cases.
  • 2011-03-29: coreΔ and authuserΔ — last (largely untested) versions by Peter Bowers. Almost certainly breaks useradmin-profiles. Implements group editing to some degree, signing up as a new user and editing users has been roughly tested.
  • 2010-06-14: coreΔ and profilesΔ — last (untested) versions by Eemeli Aro; see PmWikiUsers:2011-March/058558.html for more information
  • 2010-06-08: profilesΔ — bugfix: removed fifth $authlist parameter from AuthUserProfiles(), which requires PmWiki version 2.2.17 (reported by Dave Cooke)
  • 2010-06-07a: useradmin-authuser.phpΔ -- initial release to provide capabilities of storing user info in SiteAdmin.AuthUser page
  • 2010-06-07a: coreΔ — replaced class constants with global constants for PHP4 compatibility
  • 2010-06-07: coreΔ and profilesΔ — added $pagename parameter to Superuser(), fixed page title, Form() now outputs markup instead of HTML
  • 2010-06-04: coreΔ — internal updates, added custom forms
  • 2010-06-03: coreΔ and profilesΔ — re-released as UserAdmin with completely new internals
  • 2010-05-25Δ — first public release as AuthUserProfiles

See also

Contributors

  • Eemeli Aro - original design & code focusing on useradmin-profiles
  • Peter Bowers - subsequent development & maintainer

Comments

See discussion at UserAdmin-Talk

User notes? : If you use, used or reviewed this recipe, you can add your name. These statistics appear in the Cookbook listings and will help newcomers browsing through the wiki.