]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Added a User_username table that links the external username with a StatusNet user_id
authorCraig Andrews <candrews@integralblue.com>
Fri, 13 Nov 2009 01:12:00 +0000 (20:12 -0500)
committerCraig Andrews <candrews@integralblue.com>
Fri, 13 Nov 2009 01:12:36 +0000 (20:12 -0500)
Added EmailAuthenticationPlugin
Added ReverseUsernameAuthenticationPlugin
Changed the StartChangePassword and EndChangePassword events to take a user, instead of a nickname
User::allowed_nickname was declared non-static, but used as if it was static, so I made the declaration static

14 files changed:
EVENTS.txt
actions/passwordsettings.php
classes/User.php
classes/statusnet.ini
index.php
lib/util.php
plugins/Authentication/AuthenticationPlugin.php
plugins/Authentication/User_username.php [new file with mode: 0644]
plugins/EmailAuthentication/EmailAuthenticationPlugin.php [new file with mode: 0644]
plugins/EmailAuthentication/README [new file with mode: 0644]
plugins/LdapAuthentication/LdapAuthenticationPlugin.php
plugins/LdapAuthentication/README
plugins/ReverseUsernameAuthentication/README [new file with mode: 0644]
plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php [new file with mode: 0644]

index 3acff277ba1f04c8c8f63d99dffbce5f5eafe266..c788a9215f403d75ff4bafb77682dd3667b8ee14 100644 (file)
@@ -528,12 +528,12 @@ EndCheckPassword: After checking a username/password pair
 - $authenticatedUser: User object if credentials match a user, else null.
 
 StartChangePassword: Before changing a password
-- $nickname: user's nickname
+- $user: user
 - $oldpassword: the user's old password
 - $newpassword: the desired new password
 
 EndChangePassword: After changing a password
-- $nickname: user's nickname
+- $user: user
 
 UserDeleteRelated: Specify additional tables to delete entries from when deleting users
 - $user: User object
index 9e79501e2df5a6546a6f3d73fb57da637b2b74c1..11d7bf785317dd7628999e495abbfc91123571c5 100644 (file)
@@ -170,7 +170,7 @@ class PasswordsettingsAction extends AccountSettingsAction
         }
 
         $success = false;
-        if(! Event::handle('StartChangePassword', array($user->nickname, $oldpassword, $newpassword))){
+        if(! Event::handle('StartChangePassword', array($user, $oldpassword, $newpassword))){
             //no handler changed the password, so change the password internally
             $original = clone($user);
 
@@ -186,7 +186,7 @@ class PasswordsettingsAction extends AccountSettingsAction
                 $this->serverError(_('Can\'t save new password.'));
                 return;
             }
-            Event::handle('EndChangePassword', array($nickname));
+            Event::handle('EndChangePassword', array($user));
         }
 
         $this->showForm(_('Password saved.'), true);
index 9b90ce61bfaf65e4ff2fa285f2cff18cb7c5913c..9f1ee53f48c8e3e0db868b219527d5b469b2d972 100644 (file)
@@ -114,7 +114,7 @@ class User extends Memcached_DataObject
         return $result;
     }
 
-    function allowed_nickname($nickname)
+    static function allowed_nickname($nickname)
     {
         // XXX: should already be validated for size, content, etc.
         $blacklist = common_config('nickname', 'blacklist');
@@ -190,7 +190,17 @@ class User extends Memcached_DataObject
 
         $profile->query('BEGIN');
 
+        if(!empty($email))
+        {
+            $email = common_canonical_email($email);
+        }
+
+        $nickname = common_canonical_nickname($nickname);
         $profile->nickname = $nickname;
+        if(! User::allowed_nickname($nickname)){
+            common_log(LOG_WARNING, sprintf("Attempted to register a nickname that is not allowed: %s", $profile->nickname),
+                           __FILE__);
+        }
         $profile->profileurl = common_profile_url($nickname);
 
         if (!empty($fullname)) {
@@ -242,6 +252,10 @@ class User extends Memcached_DataObject
             }
         }
 
+        if(isset($email_confirmed) && $email_confirmed) {
+            $user->email = $email;
+        }
+
         // This flag is ignored but still set to 1
 
         $user->inboxed = 1;
index 912d05cdff0b5e9fb2cad788e5932bfa5d54a90d..19ab7bf975d6694bcc50f6b9a45fe33de8dd0032 100644 (file)
@@ -566,3 +566,13 @@ modified = 384
 user_id = K
 token = K
 
+[user_username]
+user_id = 129
+provider_name = 130
+username = 130
+created = 142
+modified = 384
+
+[user_username__keys]
+provider_name = K
+username = K
index b1e4f651e4872b1dbf8961a1064c3424fc29972a..577b491ed05b677dc9480d27fd04cdafe4a7fa21 100644 (file)
--- a/index.php
+++ b/index.php
@@ -68,7 +68,6 @@ function getPath($req)
  */
 function handleError($error)
 {
-//error_log(print_r($error,1));
     if ($error->getCode() == DB_DATAOBJECT_ERROR_NODATA) {
         return;
     }
index 68f3520db5c366086ce2c3e0af4c9a2501d47b63..4b2a25eadec5be9ebeac4d5abae357de3f1bfd4e 100644 (file)
@@ -1058,6 +1058,7 @@ function common_log($priority, $msg, $filename=null)
         }
     } else {
         common_ensure_syslog();
+        error_log($msg);
         syslog($priority, $msg);
     }
 }
index e3e55fea606b635b1c346fb4698850ce99f1d9dc..99b61b808d4cf2f26c1fe2ba59842fcd604c90e4 100644 (file)
@@ -48,20 +48,20 @@ abstract class AuthenticationPlugin extends Plugin
     //should accounts be automatically created after a successful login attempt?
     public $autoregistration = false;
 
-    //can the user change their email address
-    public $email_changeable=true;
-
     //can the user change their email address
     public $password_changeable=true;
 
+    //unique name for this authentication provider
+    public $provider_name;
+
     //------------Auth plugin should implement some (or all) of these methods------------\\
     /**
     * Check if a nickname/password combination is valid
-    * @param nickname
+    * @param username
     * @param password
     * @return boolean true if the credentials are valid, false if they are invalid.
     */
-    function checkPassword($nickname, $password)
+    function checkPassword($username, $password)
     {
         return false;
     }
@@ -69,88 +69,116 @@ abstract class AuthenticationPlugin extends Plugin
     /**
     * Automatically register a user when they attempt to login with valid credentials.
     * User::register($data) is a very useful method for this implementation
-    * @param nickname
-    * @return boolean true if the user was created, false if autoregistration is not allowed, null if this plugin is not responsible for this nickname
+    * @param username
+    * @return boolean true if the user was created, false if not
     */
-    function autoRegister($nickname)
+    function autoRegister($username)
     {
-        return null;
+        $registration_data = array();
+        $registration_data['nickname'] = $username ;
+        return User::register($registration_data);
     }
 
     /**
     * Change a user's password
     * The old password has been verified to be valid by this plugin before this call is made
-    * @param nickname
+    * @param username
     * @param oldpassword
     * @param newpassword
-    * @return boolean true if the password was changed, false if password changing failed for some reason, null if this plugin is not responsible for this nickname
+    * @return boolean true if the password was changed, false if password changing failed for some reason
     */
-    function changePassword($nickname,$oldpassword,$newpassword)
+    function changePassword($username,$oldpassword,$newpassword)
     {
-        return null;
-    }
-
-    /**
-    * Can a user change this field in his own profile?
-    * @param nickname
-    * @param field
-    * @return boolean true if the field can be changed, false if not allowed to change it, null if this plugin is not responsible for this nickname
-    */
-    function canUserChangeField($nickname, $field)
-    {
-        return null;
+        return false;
     }
 
     //------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\
-    function __construct()
-    {
-        parent::__construct();
+    function onInitializePlugin(){
+        if(!isset($this->provider_name)){
+            throw new Exception("must specify a provider_name for this authentication provider");
+        }
     }
-    
+
     function onStartCheckPassword($nickname, $password, &$authenticatedUser){
-        if($this->password_changeable){
-            $authenticated = $this->checkPassword($nickname, $password);
+        //map the nickname to a username
+        $user_username = new User_username();
+        $user_username->username=$nickname;
+        $user_username->provider_name=$this->provider_name;
+        if($user_username->find() && $user_username->fetch()){
+            $username = $user_username->username;
+            $authenticated = $this->checkPassword($username, $password);
             if($authenticated){
-                $authenticatedUser = User::staticGet('nickname', $nickname);
-                if(!$authenticatedUser && $this->autoregistration){
-                    if($this->autoregister($nickname)){
+                $authenticatedUser = User::staticGet('id', $user_username->user_id);
+                return false;
+            }
+        }else{
+            $user = User::staticGet('nickname', $nickname);
+            if($user){
+                //make sure a different provider isn't handling this nickname
+                $user_username = new User_username();
+                $user_username->username=$nickname;
+                if(!$user_username->find()){
+                    //no other provider claims this username, so it's safe for us to handle it
+                    $authenticated = $this->checkPassword($nickname, $password);
+                    if($authenticated){
                         $authenticatedUser = User::staticGet('nickname', $nickname);
+                        $user_username = new User_username();
+                        $user_username->user_id = $authenticatedUser->id;
+                        $user_username->provider_name = $this->provider_name;
+                        $user_username->username = $nickname;
+                        $user_username->created = DB_DataObject_Cast::dateTime();
+                        $user_username->insert();
+                        return false;
                     }
                 }
-                return false;
             }else{
-                if($this->authoritative){
-                    return false;
+                if($this->autoregistration){
+                    $authenticated = $this->checkPassword($nickname, $password);
+                    if($authenticated && $this->autoregister($nickname)){
+                        $authenticatedUser = User::staticGet('nickname', $nickname);
+                        $user_username = new User_username();
+                        $user_username->user_id = $authenticatedUser->id;
+                        $user_username->provider_name = $this->provider_name;
+                        $user_username->username = $nickname;
+                        $user_username->created = DB_DataObject_Cast::dateTime();
+                        $user_username->insert();
+                        return false;
+                    }
                 }
             }
-            //we're not authoritative, so let other handlers try
+        }
+        if($this->authoritative){
+            return false;
         }else{
-            if($this->authoritative){
-                //since we're authoritative, no other plugin could do this
-                throw new Exception(_('Password changing is not allowed'));
-            }
+            //we're not authoritative, so let other handlers try
+            return;
         }
     }
 
-    function onStartChangePassword($nickname,$oldpassword,$newpassword)
+    function onStartChangePassword($user,$oldpassword,$newpassword)
     {
         if($this->password_changeable){
-            $authenticated = $this->checkPassword($nickname, $oldpassword);
-            if($authenticated){
-                $result = $this->changePassword($nickname,$oldpassword,$newpassword);
-                if($result){
-                    //stop handling of other handlers, because what was requested was done
-                    return false;
-                }else{
-                    throw new Exception(_('Password changing failed'));
-                }
-            }else{
-                if($this->authoritative){
-                    //since we're authoritative, no other plugin could do this
-                    throw new Exception(_('Password changing failed'));
+            $user_username = new User_username();
+            $user_username->user_id=$user->id;
+            $user_username->provider_name=$this->provider_name;
+            if($user_username->find() && $user_username->fetch()){
+                $authenticated = $this->checkPassword($user_username->username, $oldpassword);
+                if($authenticated){
+                    $result = $this->changePassword($user_username->username,$oldpassword,$newpassword);
+                    if($result){
+                        //stop handling of other handlers, because what was requested was done
+                        return false;
+                    }else{
+                        throw new Exception(_('Password changing failed'));
+                    }
                 }else{
-                    //let another handler try
-                    return null;
+                    if($this->authoritative){
+                        //since we're authoritative, no other plugin could do this
+                        throw new Exception(_('Password changing failed'));
+                    }else{
+                        //let another handler try
+                        return null;
+                    }
                 }
             }
         }else{
@@ -164,9 +192,42 @@ abstract class AuthenticationPlugin extends Plugin
     function onStartAccountSettingsPasswordMenuItem($widget)
     {
         if($this->authoritative && !$this->password_changeable){
-            //since we're authoritative, no other plugin could change passwords, so do render the menu item
+            //since we're authoritative, no other plugin could change passwords, so do not render the menu item
+            return false;
+        }
+    }
+
+    function onAutoload($cls)
+    {
+        switch ($cls)
+        {
+         case 'User_username':
+            require_once(INSTALLDIR.'/plugins/Authentication/User_username.php');
             return false;
+         default:
+            return true;
         }
     }
+
+    function onCheckSchema() {
+        $schema = Schema::get();
+        $schema->ensureTable('user_username',
+                             array(new ColumnDef('provider_name', 'varchar',
+                                                 '255', false, 'PRI'),
+                                   new ColumnDef('username', 'varchar',
+                                                 '255', false, 'PRI'),
+                                   new ColumnDef('user_id', 'integer',
+                                                 null, false),
+                                   new ColumnDef('created', 'datetime',
+                                                 null, false),
+                                   new ColumnDef('modified', 'timestamp')));
+        return true;
+    }
+
+    function onUserDeleteRelated($user, &$tables)
+    {
+        $tables[] = 'User_username';
+        return true;
+    }
 }
 
diff --git a/plugins/Authentication/User_username.php b/plugins/Authentication/User_username.php
new file mode 100644 (file)
index 0000000..79adeb1
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Table Definition for user_username
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class User_username extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'user_username';                     // table name
+    public $user_id;                        // int(4)  not_null
+    public $provider_name;                  // varchar(255)  primary_key not_null
+    public $username;                       // varchar(255)  primary_key not_null
+    public $created;                        // datetime()   not_null
+    public $modified;                       // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=null)
+    { return Memcached_DataObject::staticGet('User_username',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+}
diff --git a/plugins/EmailAuthentication/EmailAuthenticationPlugin.php b/plugins/EmailAuthentication/EmailAuthenticationPlugin.php
new file mode 100644 (file)
index 0000000..25e5377
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin that uses the email address as a username, and checks the password as normal
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class EmailAuthenticationPlugin extends Plugin
+{
+    //---interface implementation---//
+
+    function onStartCheckPassword($nickname, $password, &$authenticatedUser)
+    {
+        if(strpos($nickname, '@'))
+        {
+            $user = User::staticGet('email',$nickname);
+            if($user && isset($user->email))
+            {
+                if(common_check_user($user->nickname,$password))
+                {
+                    $authenticatedUser = $user;
+                    return false;
+                }
+            }
+        }
+    }
+}
+
diff --git a/plugins/EmailAuthentication/README b/plugins/EmailAuthentication/README
new file mode 100644 (file)
index 0000000..3208156
--- /dev/null
@@ -0,0 +1,7 @@
+The Email Authentication plugin allows users to login using their email address.
+
+The provided email address is used to lookup the user's nickname, then that nickname and the provided password is checked.
+
+Installation
+============
+add "addPlugin('emailAuthentication');" to the bottom of your config.php
index ded5cf299a4bcb9abac5c872586511951c4bdd57..865154730f788c2d460824af56aff9726820c2dc 100644 (file)
@@ -48,20 +48,31 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin
     public $scope=null;
     public $attributes=array();
 
-    function __construct()
-    {
-        parent::__construct();
+    function onInitializePlugin(){
+        parent::onInitializePlugin();
+        if(!isset($this->host)){
+            throw new Exception("must specify a host");
+        }
+        if(!isset($this->basedn)){
+            throw new Exception("must specify a basedn");
+        }
+        if(!isset($this->attributes['nickname'])){
+            throw new Exception("must specify a nickname attribute");
+        }
+        if(!isset($this->attributes['username'])){
+            throw new Exception("must specify a username attribute");
+        }
     }
     
     //---interface implementation---//
 
-    function checkPassword($nickname, $password)
+    function checkPassword($username, $password)
     {
         $ldap = $this->ldap_get_connection();
         if(!$ldap){
             return false;
         }
-        $entry = $this->ldap_get_user($nickname);
+        $entry = $this->ldap_get_user($username);
         if(!$entry){
             return false;
         }else{
@@ -76,48 +87,33 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin
         }
     }
 
-    function autoRegister($nickname)
+    function autoRegister($username)
     {
-        $entry = $this->ldap_get_user($nickname,$this->attributes);
+        $entry = $this->ldap_get_user($username,$this->attributes);
         if($entry){
             $registration_data = array();
             foreach($this->attributes as $sn_attribute=>$ldap_attribute){
-                if($sn_attribute=='email'){
-                    $registration_data[$sn_attribute]=common_canonical_email($entry->getValue($ldap_attribute,'single'));
-                }else if($sn_attribute=='nickname'){
-                    $registration_data[$sn_attribute]=common_canonical_nickname($entry->getValue($ldap_attribute,'single'));
-                }else{
-                    $registration_data[$sn_attribute]=$entry->getValue($ldap_attribute,'single');
-                }
+                $registration_data[$sn_attribute]=$entry->getValue($ldap_attribute,'single');
+            }
+            if(isset($registration_data['email']) && !empty($registration_data['email'])){
+                $registration_data['email_confirmed']=true;
             }
             //set the database saved password to a random string.
             $registration_data['password']=common_good_rand(16);
-            $user = User::register($registration_data);
-            return true;
+            return User::register($registration_data);
         }else{
             //user isn't in ldap, so we cannot register him
-            return null;
+            return false;
         }
     }
 
-    function changePassword($nickname,$oldpassword,$newpassword)
+    function changePassword($username,$oldpassword,$newpassword)
     {
         //TODO implement this
         throw new Exception(_('Sorry, changing LDAP passwords is not supported at this time'));
 
         return false;
     }
-
-    function canUserChangeField($nickname, $field)
-    {
-        switch($field)
-        {
-            case 'password':
-            case 'nickname':
-            case 'email':
-                return false;
-        }
-    }
     
     //---utility functions---//
     function ldap_get_config(){
@@ -159,7 +155,7 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin
      */
     function ldap_get_user($username,$attributes=array()){
         $ldap = $this->ldap_get_connection();
-        $filter = Net_LDAP2_Filter::create($this->attributes['nickname'], 'equals',  $username);
+        $filter = Net_LDAP2_Filter::create($this->attributes['username'], 'equals',  $username);
         $options = array(
             'scope' => 'sub',
             'attributes' => $attributes
index 03647e7c75908a64b2faaba0cc54873081e321d3..b10a1eb9303d17933fc4d803460e6f2e67b63eff 100644 (file)
@@ -6,7 +6,8 @@ add "addPlugin('ldapAuthentication', array('setting'=>'value', 'setting2'=>'valu
 
 Settings
 ========
-authoritative (false): Set to true if LDAP's responses are authoritative (meaning if LDAP fails, do check the any other plugins or the internal password database).
+provider_name*: a unique name for this authentication provider.
+authoritative (false): Set to true if LDAP's responses are authoritative (meaning if LDAP fails, do check any other plugins or the internal password database).
 autoregistration (false): Set to true if users should be automatically created when they attempt to login.
 email_changeable (true): Are users allowed to change their email address? (true or false)
 password_changeable (true): Are users allowed to change their passwords? (true or false)
@@ -23,6 +24,7 @@ filter: Default search filter. See http://pear.php.net/manual/en/package.network
 scope: Default search scope. See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php
 
 attributes: an array with the key being the StatusNet user attribute name, and the value the LDAP attribute name
+    username*
     nickname*
     email
     fullname
@@ -37,6 +39,7 @@ Example
 Here's an example of an LDAP plugin configuration that connects to Microsoft Active Directory.
 
 addPlugin('ldapAuthentication', array(
+    'provider_name'=>'Example',
     'authoritative'=>true,
     'autoregistration'=>true,
     'binddn'=>'username',
diff --git a/plugins/ReverseUsernameAuthentication/README b/plugins/ReverseUsernameAuthentication/README
new file mode 100644 (file)
index 0000000..e9160ed
--- /dev/null
@@ -0,0 +1,26 @@
+The Reverse Username Authentication plugin allows for StatusNet to handle authentication by checking if the provided password is the same as the reverse of the username.
+
+THIS PLUGIN IS FOR TESTING PURPOSES ONLY
+
+Installation
+============
+add "addPlugin('reverseUsernameAuthentication', array('setting'=>'value', 'setting2'=>'value2', ...);" to the bottom of your config.php
+
+Settings
+========
+provider_name*: a unique name for this authentication provider.
+password_changeable*: must be set to false. This plugin does not support changing passwords.
+authoritative (false): Set to true if this plugin's responses are authoritative (meaning if this fails, do check any other plugins or the internal password database).
+autoregistration (false): Set to true if users should be automatically created when they attempt to login.
+
+* required
+default values are in (parenthesis)
+
+Example
+=======
+addPlugin('reverseUsernameAuthentication', array(
+    'provider_name'=>'Example',
+    'password_changeable'=>false,
+    'authoritative'=>true,
+    'autoregistration'=>true
+));
diff --git a/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php b/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php
new file mode 100644 (file)
index 0000000..d48283b
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin that checks if the password is the reverse of username
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/Authentication/AuthenticationPlugin.php';
+
+class ReverseUsernameAuthenticationPlugin extends AuthenticationPlugin
+{
+    //---interface implementation---//
+
+    function onInitializePlugin(){
+        parent::onInitializePlugin();
+        if(!isset($this->password_changeable) && $this->password_changeable){
+            throw new Exception("password_changeable cannot be set to true. This plugin does not support changing passwords.");
+        }
+    }
+
+    function checkPassword($username, $password)
+    {
+        return $username == strrev($password);
+    }
+
+    function autoRegister($username)
+    {
+        $registration_data = array();
+        $registration_data['nickname'] = $username ;
+        return User::register($registration_data);
+    }
+}