'users'; $AUDBaseTable['user_table'] => 'pmwiki_users'; $AUDBaseTable['user_field'] => 'username'; $AUDBaseTable['pw_field'] => 'password'; // normally "md5" or "sha1". // May also be admin-defined function; use the name of the function, // and define the function in config.php $AUDBaseTable['encrypt_f'] => 'md5'; // these are required for stand-alone use only $AUDBaseTable['standalone'] => false; // set to true for stand-alone use $AUDBaseTable['email'] => 'email'; $AUDBaseTable['validate_code'] => 'validatecode'; $AUDBaseTable['validate_field'] => 'validatefield'; $AUDBaseTable['date_joined'] => 'signupdate'; // not used at the moment $AUDBaseTable['lang'] => 'en'; 3. Include this file via the site configuration include_once("$FarmD/cookbook/authuser_dbase.php"); 4. Edit Site.AuthUser and add the following on its own line: " AUD: required for AuthUserDatabase" 5. Have users in the authentication table. 6. Make sure to include authuser.php *after* everything is set up for authuser_dbase // Must come after the other items! include_once('scripts/authuser.php'); ************************** Stand-alone usage ******************** SQL Installation ******************** Add table to a database. MySQL used in this example: CREATE TABLE `pmwiki_users` ( `id` int(11) NOT NULL auto_increment, `username` varchar(30) NOT NULL default '', `password` varchar(60) default NULL, `validatecode` varchar(60) default NULL, `signupdate` date default NULL, `email` varchar(60) default NULL, `validatefield` tinyint(1) default '0', PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=MyISAM AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 Adapt as needed for your own database. This database set-up is the default. ******************** Usage ******************** create sign-up page in Wiki. You can split the forms out to multiple pages if desired: (:messages:) (:title Account Management Page:) (:if !authid:) !!!User Login (:input form {$PageUrl}:) (:input hidden action login:) || Name:||(:input text authid:) || || Password:||(:input password authpw:) || || ||(:input submit value="OK" class="inputbutton":) || (:input end:) ----- (:div style='text-align:justify; float:left; valign:top; width:48%; padding-right:2%;padding-bottom:5px;':) !!!New User Registration (:input form method=post action={$PageUrl} :) (:input hidden name=AUD_SA value=addnew:) (:input hidden name=aud_returnUrl value={$PageUrl} :) || Username:||(:input text aud_username size=20:) || || Password:||(:input password aud_password size=20:) || || Repeat Password:||(:input password aud_dupepassword:) || || Email:||(:input text name=aud_email value="email" size=20:) || || ||(:input submit name=post value="New" accesskey=g:) || (:input end:) (:divend:) (:div style='text-align:justify; valign:top; float:left; width:48%; padding-right:2%;padding-bottom:5px;':) !!! Change Password Request An email with a new validation code will be sent to you. (:input form method=post action={$PageUrl} :) (:input hidden name=AUD_SA value=reregister:) (:input hidden name=aud_returnUrl value={$PageUrl} :) || Username:||(:input text name=aud_username size=20:) || || Email:||(:input text name=aud_email size=20:) || || New Password:||(:input password aud_newpassword:)|| || Repeat Password:||(:input password aud_dupepassword:)|| || ||(:input submit name=post value="Change" accesskey=g:) || (:input end:) (:divend:) [[<<]] ---- (:ifend:) (:if authid:) You are authenticated as '''{$Author}'''. [[{$FullName}?action=logout | Logout]] !!!Update User Information Change your email address and/or password. (:input form method=post action={$PageUrl} :) (:input hidden name=AUD_SA value=update:) (:input hidden name=aud_returnUrl value={$PageUrl} :) || Username:||(:input text name=aud_username size=20:) || || Current Password:||(:input text name=aud_oldpassword size=20:) || || New Password:||(:input text name=aud_newpassword size=20:) (optional) || || Repeat New Password:||(:input password aud_dupepassword:) || || New Email:||(:input text name=aud_newemail size=20:) (optional) || || ||(:input submit name=post value="Go" accesskey=g:) || (:input end:) (:ifend:) Hints & Tips - Put the config.php information AFTER setting $WikiTitle so the emails are automatically configured to say your wiki name Release History =============== v.0.1 July, 2005 - Private release v.0.2 May 31, 3006 - Semi-public release. v.1.0 ---- -, 2006 - Initial public release. * I am trying to implement multiple-table support * Cleaning up for easier use on various sites. v.1.0.01 July 25, 2006 by akc - fixed 3 typos v.1.0.02 Aug 03, 2006 by akc - working at last v.2.0 Nov 05, 2006 by Crisses (xes) - update to ADOdb use, and integrated stand-alone options prior xes_authuser_dbase.php fork history: 0.1 2006-09-20 Initial xes_authuser_dbase.php beta release - tested only with Triad form - postgresql support definitely broken - some mysql hacks need cleaning up!! - it miraculously works in any case - probably needs to be double-checked on security - nearly everything loops back to the original page, so everything on one page works best 0.1.2 2006-10-5 Security Fix merged back into authuser_dbase.php To Do Items =============== - database errors -- in multiple languages? - fix internationalization for email body? - consider adding username to validation links - optional email notification for new user sign-up - password/username length specifications **************************** DO NOT ALTER BELOW THIS LINE You should be able to change **************************** */ // new connection standards - defaults SDVA($AUDBaseTable, array( 'database' => 'users', 'user_table' => 'pmwiki_users', 'user_field' => 'username', 'pw_field' => 'password', 'encrypt_f' => 'md5', 'lang' => 'en', 'standalone' => false, 'email' => 'email', 'validate_code' => 'validatecode', 'validate_field' => 'validatefield', 'date_joined' => 'signupdate' )); SDV($AuthUserFunctions['AUD'], 'AuthUserDatabase'); function AuthUserDatabase($pagename, $id, $pw, $pwlist) { global $AUDBaseTable; #----------------------------------- # Encryption - allows admin-created function for encryption algorhythm $pw = $AUDBaseTable['encrypt_f']($pw); #----------------------------------- # Query Preparation $u = $AUDBaseTable['user_field']; $p = $AUDBaseTable['pw_field']; $t = $AUDBaseTable['user_table']; $vf = $AUDBaseTable['validate_field']; $id = AUD_Safe($id); $pw = AUD_Safe($pw); $ask = "SELECT * FROM $t WHERE $u=$id AND $p=$pw"; if ($AUDBaseTable['standalone']) { $ask .= " AND $vf=1";} if (AUD_Query($AUDBaseTable['database'], $ask)){ return true; } else { if ($AUDBaseTable['standalone']) { AUD_SA_FormExit('login_not_valid'); } else {AUD_SA_FormExit('user_not_valid'); } return false; } } // returns true if query has matches function AUD_Query($dbase, $query) { global $DB; $out = ADOdbConnect($dbase); if ($out !== TRUE) die($out); $result = $DB[$dbase]->Execute($query); if ($result === false) die("failed"); $return = false; if (($result->RecordCount() > 0) || ($DB[$dbase]->Affected_Rows() > 0)) {$return = true;} else {$return = false;} return $return; } // Replaces user-entered values and makes sure they're safe from SQL injection function AUD_Safe($value){ global $AUDBaseTable, $DB; $out = ADOdbConnect($AUDBaseTable['database']); if ($out !== TRUE) die($out); if (get_magic_quotes_gpc()) $value = trim(stripslashes($value)); $value = $DB[$AUDBaseTable['database']]->qstr($value); return($value); } /*********************** Stand-Alone Code ***********************/ SDV($AUD_SA_ValidationCodeLength, 15); SDV($AUD_SA_Msgs, array( "invalid_register_link" => "$[There has been an error in processing your change request. Please check the link in your email or contact the site administrator for assistance.]", "form_error" => "$[There has been an error in form processing. Please alert the admin.]", "user_not_found" => "

$[No user with that name and password found. Please register or try again].

", "login_not_valid" => "

$[No validated user with that name and password found. Please validate your account, register, or try again].

", "email_taken" => "

$[That email is already in our database. If you've forgotten your password, please use the Change Password Request form].

", "addnew_loggedin" => "

$[You are already logged in as a valid user. Please use your current account or log out to create a new account.].

", "username_taken" => "

$[That username is already taken. Please enter another username.]

", "email_invalid" => "

$[Email address appears invalid. Please check the email address or notify the administrator.]

", "update_invalid" => "

$[Username and current password are required. You must also include either a new email address or new password.]

", "password_invalid" => "

$[The old password you entered does not match the password we have on file. Please try again.]

", "user_not_validated" => "

$[I'm sorry, your account isn't validated. Please change details in your account after you've validated your account. Please check your email.]

", "validation_error" => "

$[There was a problem validating your account. Please contact the site administrator.]

", "email_sent" => "$[An email has been sent to you. Please find the email and click on the validation link.]", "password_mismatch" => "$[Your password and it's duplicate don't match. Please-reenter your new password twice.]", "updates_done" => "$[Your information has been updated. Thank you.]", "email_subject" => "$WikiTitle sign-up validation link", "email_msg" => "\nThank you for signing up to $WikiTitle.\n\nPlease click on the following link to complete your registration:\n", "standalone_error" => "

$[There is an error in your configuration settings, or in your form. To use AuthUserDBase with the standalone feature, you must set \$AUDBaseTable's standalone setting to true.]

", "validation_done" => "

$[Thank you for validating.]

" ) ); /*********************************/ // Stand-alone database management /*********************************/ //Program Logic // logic only runs for AUD_SA actions if (isset($_REQUEST['AUD_SA'])) { if (!$AUDBaseTable['standalone']) { AUD_SA_FormExit('standalone_error'); return false; } global $AUDBaseTable, $DB; $out = ADOdbConnect($AUDBaseTable['database']); if ($out !== TRUE) die($out); switch ($_REQUEST['AUD_SA']) { case "addnew": AUD_SA_ProcessAddnew(); break; case "update": AUD_SA_ProcessUpdate(); break; case "validate": AUD_SA_ProcessValidate(); break; case "reregister": if ($_POST['AUD_SA'] == "reregister") { AUD_SA_ProcessReRegister(); } else { AUD_SA_CompleteReRegister(); } break; default: AUD_SA_FormExit('form_error'); return false; break; } } // Handle the re-validation email from user request to fix passwords function AUD_SA_CompleteReRegister(){ global $AUDBaseTable; // validate "audcode" as 32 hexidecimal characters in length $password_valid = preg_match('/^[A-Fa-f0-9]{32}$/', $_GET['audcode']); if (!$password_valid) { AUD_SA_FormExit('invalid_reregister_link'); return false; } #----------------------------------- # Query Preparation $u = $AUDBaseTable['user_field']; $p = $AUDBaseTable['pw_field']; $t = $AUDBaseTable['user_table']; $e = $AUDBaseTable['email']; $v = $AUDBaseTable['validate_code']; $vf = $AUDBaseTable['validate_field']; $un = AUD_Safe($_GET['username']); $vc = AUD_Safe($_GET['valcode']); $ac = AUD_Safe($_GET['audcode']); // check username & validation code. $ask = "SELECT * FROM $t WHERE $u=$un AND $v=$vc"; $answer = AUD_Query($AUDBaseTable['database'], $ask); if ($answer === true) { //If valid, then update table with password. $ask = "UPDATE $t SET $v=NULL, $vf='1', $p=$ac WHERE $u=$un"; $answer = AUD_Query($AUDBaseTable['database'], $ask); if ($answer) { AUD_SA_FormExit('validation_done'); } else { AUD_SA_FormExit('validation_error'); } } } // Handle the user request to fix their password function AUD_SA_ProcessReRegister () { global $AUDBaseTable; #----------------------------------- # Query Preparation $u = $AUDBaseTable['user_field']; $p = $AUDBaseTable['pw_field']; $t = $AUDBaseTable['user_table']; $e = $AUDBaseTable['email']; $d = $AUDBaseTable['date_joined']; $v = $AUDBaseTable['validate_code']; $un = AUD_Safe($_POST['aud_username']); $em = AUD_Safe($_POST[aud_email]); // validate all user-entered data // entered passwords must match if (($_POST['aud_newpassword'] != '') && (!AUD_SA_ComparePasswords($_POST['aud_newpassword'], $_POST['aud_dupepassword']))) return false; // validate user - username & email must match account $ask = "SELECT * from $t where $u=$un AND $e=$em"; $answer = AUD_Query($AUDBaseTable['database'], $ask); if (!$answer) { AUD_SA_FormExit('user_not_found'); return false; } // generate new validation code $vcode = AUD_SA_ValidateCode(); // update database with validation code $ask = "UPDATE $t SET $v='$vcode' WHERE $u=$un"; $answer = AUD_Query($AUDBaseTable['database'], $ask); // Send email if ($answer > 0) { AUD_SA_EmailCode ($_POST['aud_username'], $email, $vcode, 1) ; AUD_SA_FormExit('email_sent'); } } // process user information changes - update email &/or password function AUD_SA_ProcessUpdate () { global $AUDBaseTable; // Form validation // Check that there's sufficient info to process the update if (($_POST['aud_username'] == '') || ($_POST['aud_oldpassword'] == '') || ( ($_POST['aud_newpassword'] == '') && ($_POST['aud_newemail'] == '') ) ) { AUD_SA_FormExit('update_invalid'); return false; } // entered passwords must match if (($_POST['aud_newpassword'] != '') && (!AUD_SA_ComparePasswords($_POST['aud_newpassword'], $_POST['aud_dupepassword']))) return false; // Make sure user IS validated if updating record. if (!AUD_SA_IsValidated(AUD_Safe($_POST['aud_username']))) { AUD_SA_FormExit('user_not_validated'); return false; } // If we're going to be updating the email address, make sure it's valid if ($_POST['aud_newemail'] != '') { if (!AUD_SA_ValidateEmail($_POST['aud_newemail'])) { AUD_SA_FormExit('email_invalid'); return false; } } // Check user's password etc. using false as placeholders for unused values if (!AuthUserDatabase(false, $_POST['aud_username'], $_POST['aud_oldpassword'], false)) { AUD_SA_FormExit('password_invalid'); return false; } // entered new passwords must match if (($_POST['aud_newpassword'] != '') && (!AUD_SA_ComparePasswords($_POST['aud_newpassword'], $_POST['aud_dupepassword']))) return false; #----------------------------------- # Query Preparation $u = $AUDBaseTable['user_field']; $p = $AUDBaseTable['pw_field']; $t = $AUDBaseTable['user_table']; $e = $AUDBaseTable['email']; $d = $AUDBaseTable['date_joined']; $ne = AUD_Safe($_POST['aud_newemail']); $un = AUD_Safe($_POST['aud_username']); // create the update query $ask = "UPDATE $t SET "; if ($_POST['aud_newpassword'] != "") { // encrypt password using the format set by user or default $crypt = $AUDBaseTable['encrypt_f']($_POST['aud_newpassword']); $ask .= "$p = '$crypt' " ; } if (($_POST['aud_newemail'] != "") && ($_POST['aud_newpassword'] != "") ) {$ask .= ", ";} if ($_POST['aud_newemail'] != "") { $ask .= "$e = $ne " ;} $ask .= "WHERE $u=$un"; $answer = AUD_Query($AUDBaseTable['database'], $ask); // userfeedback if ($answer == 1) { AUD_SA_FormExit('updates_done'); return true; } else { AUD_SA_FormExit('form_error'); return false; } } // This function adds a new user to the database function AUD_SA_ProcessAddNew () { global $AUDBaseTable, $DB; // Check this --> need to make sure user IS not(!) validated if adding a record. if (AUD_SA_IsValidated(AUD_Safe($_POST['aud_username']))) {AUD_SA_FormExit('addnew_loggedin'); return false;} // entered passwords must match if ($_POST['aud_password'] != '') { $p_answer = AUD_SA_ComparePasswords($_POST['aud_password'], $_POST['aud_dupepassword']); if (!$p_answer) { return false; } } else { AUD_SA_FormExit('password_mismatch'); return false; } // Validate Email Address if (!AUD_SA_ValidateEmail($_POST['aud_email'])) { AUD_SA_FormExit('email_invalid'); return false;} #----------------------------------- # Query Preparation $u = $AUDBaseTable['user_field']; $p = $AUDBaseTable['pw_field']; $t = $AUDBaseTable['user_table']; $e = $AUDBaseTable['email']; $d = $AUDBaseTable['date_joined']; $v = $AUDBaseTable['validate_code']; $em = AUD_Safe($_POST['aud_email']); $un = AUD_Safe($_POST['aud_username']); // check whether requested email address exists $ask = "SELECT * FROM $t WHERE $e=$em"; $eanswer = AUD_Query($AUDBaseTable['database'], $ask); // if email exists, send error to browser if ($eanswer) { AUD_SA_FormExit ('email_taken'); } // check whether requested username exists $ask = "SELECT * FROM $t WHERE $u=$un"; $uanswer = AUD_Query($AUDBaseTable['database'], $ask); // if username exists, send error to browser, ask for new username if ($uanswer) { AUD_SA_FormExit ('username_taken'); } // if neither username or email exist, add user if ((!$uanswer) && (!$eanswer)) { global $AUD_SA_ValidationCodeLength; // generate a random validation code $vcode = AUD_SA_ValidateCode($AUD_SA_ValidationCodeLength); // encrypt password using the encryption format $crypt = $AUDBaseTable['encrypt_f']($_POST['aud_password']); // Add new user to database with validation code $now = $DB[$AUDBaseTable['database']]->DBdate(time()); $ask = "INSERT INTO $t ($u, $e, $p, $v, $d) VALUES ($un, $em, '$crypt', '$vcode', $now )"; // run query $answer = AUD_Query($AUDBaseTable['database'], $ask); // Send out email with validation code // can check $answer, send email, and send message to browser if ($answer) { AUD_SA_EmailCode($_POST['aud_username'], $_POST['aud_email'], $vcode); AUD_SA_FormExit('email_sent'); }; } } // This function sends out emails, whether for first validation or re-validation function AUD_SA_EmailCode ($username, $email, $vcode, $revalidate=0) { global $AUD_SA_Msgs, $ScriptUrl, $pagename, $AUDBaseTable; // create validation URL if ($revalidate == 0) { $url = $ScriptUrl . "?n=$pagename&aud_username=$username&AUD_SA=validate&valcode=$vcode"; } else { $crypt = $AUDBaseTable['encrypt_f']($_POST['aud_newpassword']); $uencuser = urlencode($username); $url = $ScriptUrl . "?n=$pagename&AUD_SA=reregister&username=$uencuser&valcode=$vcode&audcode=$crypt"; } $body = $username . ": \n\n$AUD_SA_Msgs[email_msg]\n" . $url; //may want to add a from line later $from = ""; return (mail($email, $AUD_SA_Msgs['email_subject'], $body )); } //Should check for valid email addresses including ones with abnormal but valid characters function AUD_SA_ValidateEmail ($email) { // This may look like a rather loose email validator, but if you look at RFPs // for real valid email addresses, it's pretty scary what people can put in email // addresses and then there's .info, .name, .museum and a bunch of .com.au etc. // and oddest of all is name@[127.0.0.1] type addresses. $lastdot = strrpos($email, '.'); $amp = strrpos($email, '@'); $length = strlen($email); if ( ($lastdot === false) || (!(substr_count($email, '@')==1)) || ($amp === 0) || ($amp === false) || ($length === false) || (($lastdot - $amp) < 3) || (($length - $lastdot) < 3) ) { $return = false; } else { $return = true; } return $return; } // Quick check to see if the user has already validated their account function AUD_SA_IsValidated ($username) { global $AUDBaseTable; #----------------------------------- # Query Preparation $u = $AUDBaseTable['user_field']; $t = $AUDBaseTable['user_table']; $vf = $AUDBaseTable['validate_field']; // return true/false on whether a user is validated -- no need to check password, etc. $ask = "select $vf from $t where $u = $username"; $answer = AUD_Query($AUDBaseTable['database'], $ask); return $answer; } // Process a request to validate the user - from link in email using GET values function AUD_SA_ProcessValidate () { global $AUDBaseTable; // Note: Consider adding username to the link & query -- valcodes COULD be duped #----------------------------------- # Query Preparation $u = $AUDBaseTable['user_field']; $t = $AUDBaseTable['user_table']; $v = $AUDBaseTable['validate_code']; $vf = $AUDBaseTable['validate_field']; $vc = AUD_Safe($_GET['valcode']); $ask = "SELECT * FROM $t WHERE $v =$vc"; $count = AUD_Query($AUDBaseTable['database'], $ask); if ($count) { $ask = "UPDATE $t SET $v=NULL, $vf=1 WHERE $v =$vc"; $answer = AUD_Query($AUDBaseTable['database'], $ask); if ($answer) { AUD_SA_FormExit('validation_done'); } else { AUD_SA_FormExit('validation_error'); } } } // Process errors and send them to the browser PmWiki-style function AUD_SA_FormExit ($type) { global $MessagesFmt, $AUD_SA_Msgs; $MessagesFmt[] = $AUD_SA_Msgs[$type]; return true; } // Create random alphanumeric code function AUD_SA_ValidateCode ($length=20) { //Borrowed and hacked from Author: Peter Mugane Kionga-Kamau //http://www.pmkmedia.com //code found at http://codewalkers.com/seecode/279.html //"Modify at will." $from = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; for ($i=0; $i<$length; $i++) $key .= $from[(mt_rand(0,(strlen($from)-1)))]; return $key; } // case-sensitive string comparison -> compare passwords given in dupe password forms function AUD_SA_ComparePasswords($first, $second) { // This function compares two passwords to make sure they're the same, and if not, returns false $result = strcmp($first, $second) ; if ($result != 0) { AUD_SA_FormExit('password_mismatch'); return false; } return true; }