Recent Changes - Search:

Cookbook

PmWiki

pmwiki.org

AuthUserSignup

Summary: Allow users to sign up themselves (with email verification) for authuser accounts
Version: 2008-11-18
Prerequisites: pmwiki 2.2.x, WikiSh version 2008-09-28 or later, MiscMX version 2008-09-13 or flater, WikiMail version 2008-08-08 or later
Status: Alpha
Maintainer: Peter Bowers
Categories: Administration and Security

Questions answered by this recipe

This section is optional; use it to indicate the types of questions (if any) this recipe is intended to answer.

  • I need to allow users to be able to self-register to sign up for an authuser account. Where do I find such a form with email verification?
  • I would like to maintain other information about users (address, email, telephone, etc) and allow them to update it - how?
  • I would like a form-based method where users can change their own password and the administrator can change anyone's password - how?
  • I would like to handle user administration via forms rather than by editing the SiteAdmin.AuthUser page directly - how?

Description

Allow users to sign up via a form for an authuser account.

Installation & Configuration

Installation of WikiSh

For those starting with an existing installation of WikiSh

  • Ensure you have version 2008-09-13 of WikiSh or later
  • Add the following authorizations in your SiteAdmin.WikiShAuth page (assuming you are using slParsePage)
(:if name Login.Confirm:)
edit=append,prepend,insert,overwrite,create
all=read,edit
SiteAdmin.AuthUser:read,append,forceread,forceedit
SiteAdmin.AuthUserExtra*:read,overwrite,forceread,forceedit
Login.Confirm:read
(:if name Login.Signup:)
SiteAdmin.AuthUser:read,append,overwrite,forceread,forceedit
SiteAdmin.AuthUserExtra*:read,append,overwrite,forceread,forceedit,create
Login.Signup:read
(:ifend:)
Temp.*: all
  • Ensure that you have the following variables set appropriately:
$EnableWikiShWritePage = true;
$EnableWikiShCreatePage = true;
$EnableWikiShOverwritePage = true;
$EnableWikiShMailx = true;

For those starting from scratch with WikiSh

  • Download the WikiSh.zip file from the WikiSh page. Unzip it and place the php files in your cookbook.
  • Place the following lines in your config.php:
include_once("$FarmD/cookbook/toolbox.php");
include_once("$FarmD/cookbook/WikiSh.php");
include_once("$FarmD/cookbook/SecLayer.php");
$EnableWikiShWritePage = true;
$EnableWikiShCreatePage = true;
$EnableWikiShOverwritePage = true;
$EnableWikiShMailx = true;
slParsePage($pagename, "SiteAdmin.WikiShAuth", $wshAuthPage);
  • Create the page SiteAdmin.WikiShAuth with the following information:
edit=append,prepend,insert,overwrite,create
all=read,edit
(:if name Login.Confirm:)
SiteAdmin.AuthUser:read,append,forceread,forceedit
SiteAdmin.AuthUserExtra*:read,overwrite,forceread,forceedit
Login.Confirm:read
(:if name Login.Signup:)
SiteAdmin.AuthUser:read,append,overwrite,forceread,forceedit
SiteAdmin.AuthUserExtra*:read,append,overwrite,forceread,forceedit,create
Login.Signup:read
(:ifend:)
Temp.*: all

Installation of MiscMX

  • Download MiscMX.php from MiscMX page (version 2008-09-13 or later) and place it in your cookbook directory
  • Place the usual line in your config.php:
include_once("$FarmD/cookbook/MiscMX.php");

Installation of WikiMail

  • Download wikimail.php (version 2008-08-08 or later) from the WikiMail page and place it in your cookbook directory
  • Place the usual line in your config.php:
include_once("$FarmD/cookbook/wikimail.php");
  • Configure the $WikiMailSMTP array as indicated on WikiMail page. Below might be a typical setting (replace the username, password, and domain as appropriate):
$WikiMailSMTP['Host'] = 'mail.example.com';          // only used on win32 systems
$WikiMailSMTP['User'] = 'wikimail';                  // user for SMTP
$WikiMailSMTP['Passwd'] = 'mysecret';                // passwd for SMTP
#$WikiMailSMTP['Port'] = 25;                         // only used on win32 systems - 0=default
#$WikiMailSMTP['SendmailPath'] = '/usr/bin/sendmail' // only used on unix systems
$WikiMailSMTP['From'] = 'wikimail@example.com';      // must be set in config.php unless already set via php.ini

Installation of scripts for AuthUserSignup

  • Create a page Login.Signup (YOU MUST HAVE THE EDIT PASSWORD SET TO @admin OR SIMILAR for this page) with the following source:
(:messages:)
----
{(wikish if test -n "$AuthId"; then; echo "You are logged in as $AuthId(:if auth admin:) (admin)(:ifend:).  [[{$FullName}?action=logout|logout]]"; else; echo "Click [[{$FullName}?action=login|here]] to log in." fi)}
{(wikish source {$FullName}#signup)}
>>lframe width=350px<<
(:if equal "${create_edit}" "${CreateNewUser}":)Fill in the form and press "${CreateNewUser}" to create a user ID:(:else:)Make any changes and press "${SaveExistingUser}" to modify the information on this user.(:ifend:)
||width=350px
|| Username:||(:input text username ${readonly}:)||
|| Password:||(:input password password:)||
|| Email:||(:input text email:)||
|| Fname:||(:input text fname:)||
|| Lname:||(:input text lname:)||
|| Telephone:||(:input text phone:)||
(:if [ equal "${create_edit}" "${CreateNewUser}" && auth admin ] :)(:input checkbox bypassconfirm:) Bypass Email Confirmation(:ifend:)
(:input submit save_button "${create_edit}":)
(:if equal "${create_edit}" "${SaveExistingUser}":)(:input submit del_button "Delete":)(:input submit resend_button "Resend Confirmation Email":)(:ifend:)
>><<
>>rframe width=350px<<
Enter your username and password, then click "Load Form" to view and edit your user details in the form on the left
||width=350px
|| Username:||(:input text edituser:)||
|| Password:||(:input password editpass:)||
(:input submit load_form "Load Form":)
(:input submit forgot_passwd "Forgot Password":)
>><<
(:input end:)

(:messages:)

(:if false:)
[[#signup]]
function clearform
{
   set -s --form username = 
   set -s --form password = 
   set -s --form email = 
   set -s --form fname = 
   set -s --form lname = 
   set -s --form phone = 
}
function require
{
   if test -z ${${1}}
   then
      echo "%red%${2}%%\\"
      set err ++
   fi
}
wikish_form quickform process

set -s confirmurl = 'http://www.qdk.org/pmwiki/pmwiki.php?n=Login.Confirm'
set -s readonly = ''
set -s AuthUserExtra = 'SiteAdmin.AuthUserExtra'
set -s CreateNewUser = 'Create New User'
set -s SaveExistingUser = 'Update Information'
set -s create_edit = ${CreateNewUser}  # Default if not over-ridden below
set -s PAGEVARS = ''  # suppress fmtpagename()
if test -n "${save_button}"
then
   #
   # The user has pressed "Save" - validate the data
   #
   if test "${username}" ~= "/[^a-z0-9_]/i"
   then
     echo "%red%Only alphabetic characters, numeric digits, and underscore are allowed in the username.%%"
     exit
   fi
   if test "${password}" ~= "/ /"
   then
     echo "%red%No spaces allowed in the password.%%"
     exit
   fi
   # PUT ANY OTHER PASSWORD VALIDATION ROUTINES HERE
   if test "${save_button}" == "${CreateNewUser}"
   then
      #
      # We are "saving" the info for a NEW user (not editing existing)
      #
      set err = 0
      require username "You must provide a username."
      require password "You must provide a password."
      require email "You must provide an email address."
      require fname "You must provide a first name."
      require lname "You must provide a last name."
      if test -n ${username}
      then
         if grep -q "^${username}:" SiteAdmin.AuthUser
         then
            echo "%red%That user already exists and is confirmed.%%\\"
            set err ++
         else
            if grep -q "^${username}:" ${AuthUserExtra}
            then
               echo "%red%That user already exists awaiting confirmation.%%\\"
               set err ++
            fi
         fi
      fi
      # CHECK FOR ANY DISALLOWED EMAIL DOMAINS HERE
      if test ${err} -gt 0
      then
         echo "%red%Cannot create due to ${err} error(s) listed above.%%"
         exit
      fi
      # OK, I think we're legitimate at this point - go ahead and add it to the ${AuthUserExtra} table
      if test -n ${bypassconfirm} && test --pmwiki auth admin
      then
         set -s random = 0
      else
         set random = ${RANDOM}
      fi
      set -s npass = "`crypt ${password}`"
      # ANY CHANGE TO THE FIELDS REQUIRES A CHANGE ON THE LINE BELOW 
      echo "${username}:${fname}:${lname}:${phone}:${email}:${random}:${npass}" >>${AuthUserExtra}
      if test -n ${bypassconfirm} && test --pmwiki auth admin
      then
         echo "${username}:${npass}" >>SiteAdmin.AuthUser
      else
         # This link must point to the Confirm page with the ?code… appended to the end.
         # You might want to change the wording on the email since this is pretty basic…
         mailx -t ${email} -s "Please reply to complete subscription" - "Please click on this link ${confirmurl}?code=${random}?user=${username}"
      fi
      echo "%red%Success%%"
      clearform
   else
      #
      # We are "saving" the info for an existing user (not creating a new user)
      #
      set err = 0
      require username "You must provide a username."
      require password "You must provide a password."
      require email "You must provide an email address."
      require fname "You must provide a first name."
      require lname "You must provide a last name."
      # CHECK FOR ANY INVALID EMAIL DOMAINS HERE
      if test ${err} -gt 0
      then
         echo "%red%Cannot create due to ${err} error(s) listed above.%%"
         exit
      fi
      set -s pass_source = 'authuser'
      set -s oldpass = "`grep '^${username}:' SiteAdmin.AuthUser | cut -d: -f2`"
      if test -z ${oldpass}
      then
         set -s oldpass = "`grep '^${username}:' ${AuthUserExtra} | cut -d: -f7`"
         set -s pass_source = 'extra'
      fi
      set -s oldpass = "`trim ${oldpass}`"
      if test "${password}" == "${oldpass}" || test ${oldpass} == "`crypt --salt:"${oldpass}" ${editpass}`" || test --pmwiki auth admin
      then
         if test "${password}" == "${oldpass}" # loaded via admin privs, already crypt'd
         then
            set -s newpass = "${password}"     # the password has been entered in the form - crypt it
            # NOTE THAT PASSWORD VALIDATION (no space, require numeric, 
            # require uppercase, etc.)
            # IS NOT BEING DONE HERE AND IT SHOULD PROBABLY BE DONE!
         else
            set -s newpass = "`crypt --salt:'${oldpass}' ${password}`"
         fi
         set -s code = "`grep '^${username}:' ${AuthUserExtra} | cut -d: -f6`"
         grep -v '^${username}:' ${AuthUserExtra} >Temp.AuthUserExtra
         echo "${username}:${fname}:${lname}:${phone}:${email}:${code}:${newpass}" >>Temp.AuthUserExtra
         cp -q Temp.AuthUserExtra ${AuthUserExtra}
         if test ${pass_source} == 'authuser'
         then
            grep -v '^${username}:' SiteAdmin.AuthUser >Temp.AuthUser
            echo '${username}:${newpass}' >>Temp.AuthUser
            cp -q Temp.AuthUser SiteAdmin.AuthUser
         fi
         echo "%red%Success%%"
         clearform
      else
         echo "%red%Error.  The password does not match.%%"
      fi
   fi
fi
if test -n "${load_form}"
then
   # 
   # The user filled something in on the RIGHT side form and pressed "load form"
   #
   if test -z ${edituser}
   then
      echo "Please specify an existing username."
      clearform
      exit
   fi
   if test -z ${editpass} && ! test --pmwiki auth admin
   then
      echo "Please specify the password for '${edituser}."
      clearform
      exit
   fi
   grep '^${edituser}:' ${AuthUserExtra} | read --initialize --IFS:: username fname lname phone email code readpass
   if test -z ${username}  # Presumably does not exist in AuthUserExtra
   then
      grep '^${edituser}:' SiteAdmin.AuthUser | read --initialize --IFS:: username readpass
      set -s pass_src = authuser
   else
      set -s pass_src = extra
   fi
   if test -n ${username}  # We found it in one or the other...
   then
      set -s readpass = "`trim ${readpass}`"
      if test --pmwiki auth admin || test "`crypt --salt:${readpass} ${editpass}`" == "${readpass}"
      then
         # Set defaults as we found in ${AuthUserExtra} (they'll be blank if no AuthUserExtra record)
         set -s --form username = ${edituser}
         set -s --form password = ${readpass}
         set -s --form email = ${email}
         set -s --form fname = ${fname}
         set -s --form lname = ${lname}
         set -s --form phone = ${phone}
         set -s readonly = 'readonly=1'  #Can't change username
      else
         echo "%red%ERROR: Username / Password do not match. %%"
         clearform
         exit
      fi
   else
      echo "%red%ERROR: Username / Password does not exist.  Did you mean to create a new user?%%"
      clearform
      exit
   fi
   set -s create_edit = ${SaveExistingUser}
fi
if test -n ${del_button}
then
   #
   # The user pressed the "delete" button
   #
   if test -z ${edituser}
   then
      echo "%red%ERROR: Must specify username and load form before deleting%%"
      exit
   fi
   if grep -q "^${edituser}:" SiteAdmin.AuthUser
   then
      grep -v "^${edituser}:" SiteAdmin.AuthUser >SiteAdmin.AuthUser
   fi
   if grep -q "^${edituser}:" ${AuthUserExtra}
   then
      grep -v "^${edituser}:" ${AuthUserExtra} >${AuthUserExtra}
   fi
   set -s --form username = ''
   set -s --form password = ''
   set -s --form email = ''
   set -s --form fname = ''
   set -s --form lname = ''
   set -s --form phone = ''
   echo "User '${edituser}' Deleted."
fi
if test -n ${forgot_passwd}
then
   # 
   # The user pressed the "forgot password" link
   #
   if test -z ${edituser}
   then
      echo "%red%ERROR: Must specify username and load form before resending confirmation%%"
      exit
   fi
   if ! grep -q "^${edituser}:" ${AuthUserExtra}
   then
      echo "%red%ERROR: User '${edituser}' does not exist."
      exit
   fi
   # Rewrite the authuserextra line for this user with a new code
   grep '^${edituser}:' ${AuthUserExtra} | read --initialize --IFS:: username fname lname phone email code readpass
   if test -z ${email}
   then
      echo "%red%ERROR: No email address is specified for that user.  An administrator must reset your password manually.%%"
      exit
   fi
   grep -v "^${edituser}:" ${AuthUserExtra} >${AuthUserExtra}
   set code = ${RANDOM}
   echo "${username}:${fname}:${lname}:${phone}:${email}:${code}:${readpass}" >>${AuthUserExtra}
   mailx -t ${email} -s "Please reply to confirm a password change" - "Please click on this link ${confirmurl}?code=${code}?user=${username}?newpass=1"
   echo "Request Sent."
fi
if test -n ${resend_button}
then
   # 
   # The user requested a resend of the confirmation email
   #
   if test -z ${edituser}
   then
      echo "%red%ERROR: Must specify username and load form before resending confirmation%%"
      exit
   fi
   grep '^${edituser}:' ${AuthUserExtra} | read --initialize --IFS:: username fname lname phone email code readpass
   mailx -t ${email} -s "Please reply to complete subscription" - "Please click on this link ${confirmurl}?code=${code}?user=${username}"
   echo "Resent."
fi
[[#signupend]]
(:ifend:)
  • Create a page Login.Confirm (YOU MUST HAVE THE EDIT PASSWORD SET TO @admin OR SIMILAR) with the following source:
(:linebreaks:)
{(wikish source {$FullName}#finishsignup)}


(:if false:)
[[#finishsignup]]
wikish_form process
if test -z ${user} || test -z ${code} || test ${code} == 0
then
   echo "Please use the link provided in the email to access this page."
   exit
fi
set -s AuthUserExtra = "SiteAdmin.AuthUserExtra"
set -s PAGEVARS = ''  # suppress fmtpagename due to funky chars in passwd
if test -n ${newpass}
then
    grep "^${user}:" ${AuthUserExtra} | while read --initialize --IFS:: username fname lname phone email pagecode pass
    do
       if test ${pagecode} == ${code}
       then
          set -s newpass = 'a${RANDOM}'
          set -s newcrypt = '`crypt ${newpass}`'
          # Change the password in AuthUser if the username is confirmed there
          if grep -q "^${username}:" SiteAdmin.AuthUser
          then
              grep -v "^${username}:" >SiteAdmin.AuthUser
              echo "${username}:${newcrypt}" >>SiteAdmin.AuthUser
          fi
          # Now change the password in AuthUserExtra (also zero the code)
          grep -v '^${username}:' ${AuthUserExtra} >Temp.AuthUserExtra
          echo "${username}:${fname}:${lname}:${phone}:${email}:0:${newcrypt}" >>Temp.AuthUserExtra
          cp -q Temp.AuthUserExtra ${AuthUserExtra}
          echo "Password changed for user ${username}.  The new password is '''${newpass}'''.  It is recommended to go directly to [[Login.Signup]] to change your password."
          exit
       fi
    done
	echo "%red%Error: Your email link is not synchronized with the state of the password file.  Please reissue your change-password request.%%"
else
    if grep -q "^${user}:" SiteAdmin.AuthUser
    then
       echo "User ${user} already exists.  Cannot add this user."
       exit
    fi

    # ANY CHANGE TO THE FIELDS REQUIRES A CHANGE ON THE LINE BELOW 
    grep "^${user}:" ${AuthUserExtra} | while read --initialize --IFS:: username fname lname phone email pagecode pass
    do
       if test ${pagecode} == ${code}
       then
          echo "${username}:${pass}" >>SiteAdmin.AuthUser
          # Now make the code from a random number to a 0 to indicate it's confirmed
          grep -v '^${username}:' ${AuthUserExtra} >Temp.AuthUserExtra
          echo "${username}:${fname}:${lname}:${phone}:${email}:0:${pass}" >>Temp.AuthUserExtra
          cp -q Temp.AuthUserExtra ${AuthUserExtra}
          echo "User ${username} confirmed.  You may log in immediately by clicking on [[Confirmed?action=logout|this link]].  (By clicking on this link you will automatically be logged OUT before allowing you to log in again.)"
          exit
       fi
    done
    echo "Please use the link provided in the email to access this page."  # Actually a wrong code, but let's avoid encouraging them trying a million times
fi
[[#finishsignupend]]
(:ifend:)
  • Create a page Login.Confirmed (SET THE READ PASSWORD TO id:* FOR THIS PAGE) with the following source:
Congratulations! Your new user has been confirmed and logged in. 
  • Check to make sure that both Login.Signup and Login.Confirm have the edit password set such that only an administrator can edit
  • You can change the information you are collecting to include fewer or additional or different fields. First you will change the form found on Login.Signup and then you will change both lines which are preceded (one on each page) with the comment "# ANY CHANGE TO THE FIELDS REQUIRES A CHANGE ON THE LINE BELOW"

Notes

Now users will go to Login.Signup to fill in the form. If all fields validate correctly then their information will be stored in SiteAdmin.AuthUserExtra and they will be sent an email with a link which they must click on. When they click on that link it will take them to the Login.Confirm page with an appropriate code specified and then their subscription will be confirmed and their information will be added to SiteAdmin.AuthUser.

A user can also fill in a username and password on the right-hand-form and click on "load form" to load the left-hand form with existing data in order to make modifications. Both SiteAdmin.AuthUserExtra fields as well as the password itself in SiteAdmin.AuthUser can be changed using this method.

You should make sure that SiteAdmin.AuthUserExtra and SiteAdmin.AuthUser have appropriate permissions. Presumably you want protected read AND protected edit for the former and at least protected edit for the latter.

Note that this recipe deals comfortably with users defined in the SiteAdmin.AuthUser page. It has no knowledge of LDAP nor htpasswd nor other methods of storing user accounts.

Release Notes

If the recipe has multiple releases, then release notes can be placed here. Note that it's often easier for people to work with "release dates" instead of "version numbers".

  • 2008-11-18 - It's gone live on at least one site and is working.
  • 2008-09-28 - Most bugs worked out. Added lots of new capabilities for user admin.
  • 2008-09-17 - I think this can be called a release now.
  • 2008-09-14 - Still not formally released, but closer.
  • 2008-09-10 - Not yet released formally. Still in testing.

See Also

Contributors

Roadmap

  • It'd be nice to allow the administrator to choose from a list of users to edit instead of typing in a username
  • It'd be nice to allow the administrator to change group memberships from the form
  • Theoretically it'd be nice to deal with htpasswd files, but there's already recipes that do that so probably not...

Comments

Edit - History - Print - Recent Changes - Search
Page last modified on December 02, 2008, at 06:15 AM