plainblack.com
Username Password
search
Bookmark and Share

Authentication Plugins

Authentication in WebGUI allows plugins, providing you the power and flexibility of defining how users authenticate against your site. If you want users coming from a particular IP address to be authenticated automatically, you can do that. If you would like to create a wizard for creating a new account, you can do that as well. Nearly anything is possible with WebGUI's authentication system.

 

History

WebGUI has allowed authentication plugins since version 5. One of the drawbacks with authentication in version 5 was that you were limited to the set of routines that WebGUI itself expected to call in various places. Thus, you couldn't create or define your own set of rules for authentication. You could only modify the existing rules in an attempt to make them match your requirements. This drawback caused a reevaluation of authentication in WebGUI. WebGUI's developers wanted to provide users with the most flexibility possible while at the same time making authentication plugins easy to develop and manage.

 

Authentication as it exists in WebGUI today came with the release of WebGUI 6.0. It is object oriented, making it possible to build on existing authentication plugins, which makes them easy and fast to develop. It is also infinitely flexible. You can choose to use or build upon existing methods available through the Authentication package, or you can choose to completely ignore the existing framework and create your own. As you will see in upcoming chapters, the possibilities are limitless.

 

General Authentication Structure

Authentication is structured such that it is possible to do anything you wish. To make this possible, a single global web operation exists for all authentication methods: op=auth. This method serves as a router into an Authentication plugin. By specifying this at the end of a URL, you tell WebGUI that you wish to make a call within the active authentication plugin. If you wish to see this method, it can be found in WebGUI::Operation::Auth.

 

In order to call a method from within WebGUI, you must then specify a method within the plugin. You do this by specifying the method after the op=auth call: op=auth;method=login

 

This calls the login method within the active Authentication plugin. If the current authentication method is the default, or WebGUI authentication, this will call the login method within Auth::WebGUI. Be aware that the method you specify will only be called if it is flagged as “callable” from within the Authentication plugin. More will be covered about this and how to protect methods in your custom authentication plugins later.

 

WebGUI itself makes only one call within any authentication method. This will be discussed a bit later. For now, you should know that the only method called by WebGUI's core is “init”. By overriding the init method, you get to decide what you want WebGUI to do when it calls your authentication plugin. This gives you ultimate freedom in creating an authentication framework within WebGUI.

 

Writing a Custom Authentication Plugin

Now that you have a basic understanding of how authentication in WebGUI works, it's time to get into the nuts and bolts of writing a custom plugin. The first thing you'll need to decide is what you want your authentication plugin to do and if you can reuse either of the authentication plugins that ship with WebGUI, or any other custom authentication plugin you may have access to. This will determine the starting point of your custom authentication plugin and, as you'll learn later, determines how much of the authentication plugin you actually need to write yourself as some of it could be inherited.

 

Every authentication plugin in WebGUI is an extension of WebGUI::Auth. If you open this module and look through the various methods, you will see that there are many methods that will be inherited into your custom plugin. You may choose to use, not use, or change the way that these methods work. Most of these methods are discussed in this chapter, so don't spend too much time trying to figure out what is going on here. Once you've decided what you'll be extending (WebGUI::Auth or some subclass thereof), inherit from that class as you would in any regular OO Perl application by using base 'WebGUI::Auth';

 

Auth Constructor

Like any other object oriented application, you begin writing authentication plugins by creating a constructor. The constructor of your authentication plugin is responsible for setting up a framework for individual authentication sessions. It is important to remember this as authentication deals with one person at a time, not large groups of people.

 

Also, it is important to understand that you will rarely be responsible for instantiating authentication. WebGUI will typically do this for you once your authentication method is plugged into the system. Typically, you will instantiate authentication in your own custom code, such as if you were building a custom asset or utility script that needed to find out some information about a user's authentication information.

 

The superclass constructor expects a few things to be passed along in order to instantiate an instance of an authentication plugin:

 

  • WebGUI Session (session): the current active WebGUI session.

  • Authentication Identifier (authMethod): the identifier for the authentication method you wish to instantiate. This is covered more later.

  • User Id (userId): the GUID (Generic Unique ID) of the user that you wish to instantiate an authentication method for.

 

You can expect that these items will be passed into your constructor by WebGUI whenever your authentication plugin is instantiated and that the superclass expects them in order to properly set up an authentication framework. Additionally, these items are stored as object data by the superclass which makes them, as well as a few other other convenience instance methods, available to you.

 

Assume $auth is an instance of a WebGUI Authentication plugin. $auth->authMethod([authMethodIdentifier]) returns or resets the authentication identifier within the current instance of this authentication plugin. This method is important because it determines the authentication plugin that methods will be called from. It is possible to change this value so be careful. Why would you want to change the authentication method? Typically you wouldn't, but in cases where a site runs more than one authentication method, WebGUI will identify the individual's auth method at login time and change the authentication identifier to the proper one.

 

For example, if a site is configured to use WebGUI's default authentication method, but has some individuals who authenticate through LDAP, when those individuals attempt to log in WebGUI needs to make calls to the proper authentication plugin to properly authenticate them. When they first come to the site, they are essentially logged in as the visitor user, which uses the WebGUI authentication plugin. When the user attempts to log in, WebGUI looks up the authentication identifier associated with the user's account via the username entered, and then changes the authentication identifier currently stored if it is different than what visitor uses. Then, when the init method is called (shown shortly), it is called from the proper module. In the case of users that authenticate via LDAP, that would be the WebGUI::Auth::LDAP module. For those that authenticate via WebGUI, it would be the WebGUI::Auth::WebGUI module.

 

$auth->session

This returns the session object. You'll need this to retrieve various tasks in your methods.

 

$auth->user([$user_object])

This returns or resets the user object within the current instance of this authentication plugin. This is an important method for several reasons. Once you've instantiated your auth method, you should never have to create the user object again, which is very slow. Additionally, you use this method to reset the user that is currently associated with this authentication session. Why would you want to do that? Take the following example:

 

Someone comes to a WebGUI site using WebGUI's default authentication plugin and does not have a valid WebGUI session cookie identifying them. The user is logged in as the “Visitor” user (userId 1) by default. Once the user successfully logs onto the site (however that might happen), you want to change the userId that is associated with the current authentication framework to that of the user that just logged in. To do this, after a successful authentication call: $auth->user(WebGUI::User->new($auth->session,$userId)).

 

You can see an example of this in WebGUI::Auth::authenticate.

 

$auth->userId

This is a convenience method which returns the userId for the user currently set in the authentication method. This is equivalent to $auth->user->userId.

 

$auth->username

This is a convenience method which returns the username for the user currently set in the authentication method. This is equivalent to $auth->user->username.

 

The constructor also initializes some object data that can be used to retrieve or set warning and errors from the superclass. Many times there is a need to return varying amounts of data to a method call. In authentication this often happens in the form of returning success, or an error and an error message. Rather than having to do things like return zero on success or a message if an error happened, or true and undef in the case of no error, the authentication system allows you to simply return true or false and set object data containing any errors or warnings that you might want to throw.

 

$auth->error([$errorMessage])

This returns or sets the current error message stored in object data.

 

$auth->warning([$warningMessage])

This returns or sets the current warning message stored in object data.

 

Let's say you are validating that a password entered complies with some custom rules you require for entering passwords. There could be many reasons the password doesn't pass the test. Instead of having to name your method something like passwordIsNotValid and return false if the password is valid, you can simply return true or false and set a warning or error in object data that can be used if necessary:

 

my $false = 0;

if(length($password) < 10) {

$self->error(“Passwords must be 10 characters long”);

return $false;

}

 

Finally, the constructor is where you need to declare the methods in your authentication plugin are “callable” via a URL. You do this using the following API method.

 

$auth->setCallable(\@callableMethods)

This security feature enables you to write an unlimited amount of methods in your authentication plugin, but have a limited number that can be called in the form of op=auth;method=yourMethod. Methods that are not declared in this fashion will not be allowed to be called from the URL.

 

Once you have created an instance of your authentication method, use the setCallable method to determine the additional methods you would like to be called from the URL.

 

my $auth = $class->SUPER::new(@_);

my @callable = ('login','logout');

$auth->setCallable(\@callable);

 

This enables the login and logout subroutines of your plugin to be called via URL. Additionally, you can determine if a method is in the callable stack by using the following API method.

 

$auth->isCallable([methodName])

Take a look at the typical constructor of a custom authentication method.

 

sub new {

#Shift the class off the argument stack

my $class = shift;

#Call the superclass method passing in the remaining argument stack

my $auth = $class->SUPER::new(@_);

#create a callable array with the methods we'd like to be url accessible

my @callable = (

“login”,

“logout”,

“createAccount”,

“createAccountSave”,

“deactivateAccount”,

“deactivateAccountConfirm”,

“displayAccount”,

“displayLogin”

);

#Tell the auth method that the methods in the callable array are url accessible

$auth->setCallable(\@callable);

#Return the object

return $auth;

}

 

You'll notice that the first thing done is call the superclass method which returns a valid authentication object of your type. Then, set the callable methods that you will create or use via inheritance and return a reference to the blessed authentication plugin. You can now continue to build the plugin, making customizations as necessary, as you will see in the following sections.

 

init()

As mentioned earlier in the chapter, the init method is the only one that gets called by WebGUI. This method is the entry point for any authentication plugin. It is here that you will determine what happens when a user tries to log in or attempts to access a privileged page as the visitor.

 

By default, the init method of the Auth superclass calls its displayLogin method. You can choose to use this method as is, override this method in your own plugin and make it work the way you need it to work, or you can override init completely in your plugin and make it do something completely different.

 

Let's say you want people coming from a particular IP address to automatically have access to a portion of your site that normal visitors should not. If someone tries to access the portion of the site from another IP address, you want to present the normal login page.

 

Every asset has three global privilege properties: owner, groupToView, and groupToEdit. If the user attempting to view the page is not the owner, and does not belong to either of the groupToView or groupToEdit groups, WebGUI will call the init method to determine what to do next.

 

In this example, you would want to create another visitor-like user, that you'll call “ipvisitor”, using WebGUI's user management system, and add that user to the groupToView group you've defined for the particular portion of the site you'd like to restrict to those users.

 

Now, override the init method in your custom auth plugin and check the IP address of the user attempting to access the site. If the IP address matches the one you check for, you will automatically log the user in as the ipvisitor. If not, you'll return the displayLogin page (this method is discussed in the next section).

 

sub init {

#shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Retrieve the allowed IP address from the authentication settings

my $allowedIP = $self->getSetting("allowedIPAddress");

#Get the IP address of the visitor

my $visitorIP = $session->env->getIp();



#Log the user in of the visitorIP address is the same as the allowed IP address

#and the user is currently the visitor

if($self->userId eq “1” && $visitorIP eq $allowedIP) {

#Retrieve the userId we wish to log users in as from the

#authentication settings

my $ipvisitorId = $self->getSetting("ipvisitorId");



#If the ipvisitorId is not set in the settings, fall through

if ($ipvisitorId) {

#Create the ipvisitor user object

my $ipvisitor = WebGUI::User->new($session,$ipvisitorId);



#Set the ipvisitor as the user in the auth method

$self->user($ipvisitor);

#Log the user in

return $self->SUPER::login;

}

}



#Otherwise return the displayLogin method

return $self->displayLogin;

}

 

Start as you would start building any instance method in an object by shifting off the object from the argument stack. Then, use the getSetting method of the superclass to return the IP address you've stored in the authentication settings (how to do this is later in the chapter). Use the getIp() method from the WebGUI::Session::Env API to retrieve the IP address of the current user.

 

Then, check to see if the IP address of the user matches the IP address stored in the authentication settings. If they do, you'll get the userId of the ipvisitor user you mentioned earlier, which you also have stored in the authentication settings. If the userId has been set (you don't want to try to operate on a userId that doesn't exist), you'll create an instance of the ipvisitor user object and set that as the new user object for this authentication session. Once that is done, you can simply return the login method of the superclass which will log in the user and forward him on to the page that was trying to be accessed (again, this method is discussed later in the chapter).

 

If the IP addresses don't match, simply stick with the default logic and forward the user on to the displayLogin method of the authentication plugin.

 

As you have probably figured out by now, the possibilities in this method are limitless. As the single point of entry into WebGUI's authentication system, the init method gives you the power to make your authentication plugins extremely flexible.

 

displayLogin()

If you don't choose to override the init method, the displayLogin method will be returned. This method is really a convenience method which returns a template containing the core form elements that are used in almost every traditional login page. Typically, you will not want to override this method, but instead you will want to extend it with some options that are available from the superclass.

 

$auth->displayLogin([postMethod, templateVariables])

“postMethod” is a string which indicates which callable method should be posted to when the user makes a request. By default, postMethod is set to “login”, a method you have seen in the previous section and one that will be covered in the next. If you choose to pass in your own postMethod, the form will be generated so that when the user fills in his username, password, and whatever else you might specify, it will post to whichever method you specify. Keep in mind that this method must be listed in your callable methods. For example, $auth->displayLogin('doLogin'). This will attempt to post to the doLogin method of your custom authentication plugin.

 

“templateVariables” is a hash reference containing template variables that you can create in your extension of displayLogin that will be exported to the template. By default, the following template variables are available from the displayLogin superclass method:

 

Variable

Description

title

Internationalized title of the login page.

login.form.header

<form> open tag containing action and method.

login.form.hidden

Hidden fields which specify which page to post to.

login.form.username

Text field for accepting username input.

login.form.username.label

Internationalized label for username.

login.form.password

Password field for accepting password input.

login.form.password.label

Internationalized label for password.

login.form.submit

Submit button for posting login information.

login.form.footer

</form> closing tag.

anonymousRegistration.isAllowed

Boolean indicating whether or not users can anonymously register for the site (this is a global authentication setting).

createAccount.url

A URL which links to the createAccount authentication method.

createAccount.label

Internationalized label for the create account href.

 

You extend these by passing in a hash reference of your own custom template variables that will be used on your display login page. For instance, say you wanted to add password recovery to your authentication plugin. You would want to display a URL linking to your password recovery method, and possibly a label. In order to get those items into your template, you need to pass them along as a hash reference to the displayLogin superclass method. It is important to note that you cannot change the template variables in the superclass. If you ever want to change something that was in the superclass variables, you need to create entirely new template variables and use those instead. You could also choose to override the displayLogin method completely and make it do whatever you need it to do.

 

Let's extend the displayLogin method and add a few things. First, if the user is already logged in, you'd rather not display this page. Instead, bounce the user to the displayAccount page, which will be discussed a bit later. Next, add an error message that you can display to the user in the case that login fails for some reason. Finally, add your own password recovery method. So, you'll add some extra template variables which link to the password recovery method, which is written later in the chapter.

 

sub displayLogin {

#shift the object off the argument stack

my $self = shift;

#shift off any message that we might have passed in from a failed login

my $message = shift;

#Create a session variable

my $session = $self->session;

#Create an empty has to use for our template variables

my $vars = {};

#Return the display account page if the user is already logged in

if($self->userId ne "1" && $self->userId ne $self->getSetting("ipvisitorId")) {

return $self->displayAccount($message);

}

#Add some template variables to the empty hash we created

$vars->{'login.message'} = $message;

$vars->{'recoverPassword.url'}

= $session->url->page('op=auth;method=recoverPassword');

$vars->{'recoverPassword.label'}

= "Forgot your password? Click here";

#Return the superclass displayLogin method with our additional template

#variables

return $self->SUPER::displayLogin(undef,$vars);

}

 

Start out by shifting the session object off the argument stack as usually done. Also, shift a message off. If an error occurs in the login post method, you'll export the message to the template to display to the user. Now, check the userId for the authentication session. If the userId is not “1”, which is the userId of the visitor, and the userId is not that of the ipvisitor (ipvisitors should be able to log in as another user account), that means that the user is already logged in. You don't want users who are already logged in to log in again, so show them the displayAccount form instead and pass it any message that might be forwarded. If this is the visitor or ipvisitor user, then you'll set the login message template variable along with a URL and label for your custom password recovery method and then return template returned by the displayLogin method of the superclass.

 

One final thing to note for the displayLogin method is that it is different from a lot of the other authentication methods in that it is usually called before you know which authentication method to use. In order for the displayLogin method of your authentication plugin to be called, you must be sure to set the default authentication method for the WebGUI site to your authentication plugin. You do this on the authentication tab of the Settings screen within WebGUI's Admin Console. For more information on administering WebGUI, see the WebGUI Administrators Guide.

 

getLoginTemplateId()

With all this talk of template variables and templates returned by the displayLogin method, you are probably wondering how to set the template you would like your method to use. If you have overridden the displayLogin method completely, this isn't a problem, but if you have extended the superclass method, in the previous section, you need a way to do this as templateId wasn't something that could be passed into the superclass method.

 

You do this by adding a getLoginTemplateId method to your custom authentication asset which simply returns the ID of the template to use. This is typically a setting with a default templateId set to the templateId of the default template which you provide with your authentication method:

 

sub getLoginTemplateId {

#Shift the class off the argument stack

my $self = shift;

#Retrieve the templateId from the authentication settings

my $template = $self->getSetting("loginTemplateId");

#Return the template stored in the settings or the default template

return $template || "IPtmpl0000000000000003";

}

 

You create similar “get” methods for templates from superclass methods listed in the table below. You should also create similar template methods for templates from your custom methods making them easier to subclass.

 

Superclass Method

Template Method

displayAccount

getAccountTemplateId

createAccount

getCreateAccountTemplateId

 

login()

Unless you have specified otherwise, the login() method is called when a user submits the displayLogin form. This method performs the actual login for the user, so it is almost always the case that you need to extend or override this method and add the actual authentication logic.

 

The superclass login method is very general in order to make it easy to authenticate users however you like. This method changes the user that is currently in WebGUI session to the user currently stored in the authentication object instance. If you look inside the login() method of the WebGUI::Auth, you will see a line of code that looks like this:

 

$self->session->user({user=>$u});

 

This one line of code essentially performs the login. This is important to know if you plan to completely override the login method and make yours do something else. You will always need this code in some form or other in your login routine.

 

In addition to performing the actual login, the login() method also gives the user karma if the global karma setting is enabled. It also logs the login in the userLoginLog database table. This table stores all current login sessions which are displayed in the WebGUI's Login History administrative interface.

 

The default login() method also handles changing the encryption after login if you are using an SSL certificate, redirects the user to the page he/she was trying to access when he/she was prompted to log in, and runs any command line script you may have configured to run when a user logs in.

 

Since WebGUI has global authentication settings, you should handle each of the above when logging a user in. You can choose to do this yourself if you override the login method (or choose to bypass it completely), or you can simply extend the current login method and add your own logic.

 

As stated earlier, you will almost always extend or override this method as it doesn't actually authenticate the user. It is your responsibility to decide how users are authenticated. Many different types of authentication exist. In WebGUI, you authenticate by matching a username and password MD5 hash combination against what is stored for the user in the authentication database table (this is discussed later with creating accounts). The LDAP authentication method looks up the distinguished name for the user in the authentication database table and attempts to bind to the LDAP server with that distinguished name.

 

A convenience method exists in the superclass for very basic authentication. You may choose to use it or extend it as it does some standard checks against the user table that you would probably do anyway.

 

$auth->authenticate($username);

Every user in WebGUI, regardless of the authentication method you are using, must have an entry in WebGUI's users table. The authenticate method in the superclass looks up the user by username. If the user does not exist, an error is set in the object's error data and false is returned. If the user does exist, but is not active, an error is set in the object's error data, the login attempt is logged, and false is returned. Finally, if the user does exist and is active, the user object is created, set for the authentication session, and true is returned.

 

Say you wanted to authenticate users via a database; however, you don't feel the MD5 hashing algorithm is secure enough for your passwords. Instead, you'd like your passwords hashed by something with a longer key like SHA512. There are actually two steps to this. The first is the actual authentication. Later in this chapter, saving the password hash will be discussed along with creating accounts.

 

To write the SHA512 password authentication, start by using the Digest::SHA module that comes with Perl in your authentication plugin. Then, extend the authenticate method to accept a password in clear text. You can then SHA512 encrypt the clear text password and compare that to what you have stored in the database. If they match (and you should also check to make sure that the password entered is not nothing), you will allow the authentication to continue. If they don't match, you'll set an error message in the object data and return false to the login method.

 

sub authenticate {

#shift the object off the argument stack

my $self = shift;

#shift the username off the argument stack

my $username = shift;

#shift the password off the argument stack

my $password = shift;

#validate the username using the superclass method

unless ($self->SUPER::authenticate($username)) {

#The superclass method will log the error for us, return false

return 0;

}

#SHA-512 hash the password

my $sha512pw = Digest::SHA::sha512_base64($password);

#Get a hash reference of all the authentication data for this user

my $userData = $self->getParams;

#if the password is empty or the current identifier in the data does

#not match the hashed clear text password, the password is not correct.

if ($password eq "" || $sha512pw ne $userData->{identifier}) {

#Return an error to the user

$self->error("Username/Password combination is not correct");

#Reset the user object to the visitor since login was unsuccessful

$self->user(WebGUI::User->new($self->session,1));

#Return false

return 0;

}

#Username and password combination passed, return true.

return 1;

}

 

As usual, start by shifting all of the arguments off the argument stack. In this case, you are accepting two arguments: username and password. You then validate the username by calling the superclass authenticate method, which was discussed earlier. If this fails, simply return false to the login method as any errors will already be in object data for you to return. If user authentication passes, you then want to validate that the passwords match. Start by SHA-512 hashing the clear text password that was passed in by the login method and contains the value that was posted in the password field. Then, use another superclass API method to retrieve all of the authentication data for the current user.

 

$auth->getParams();

This method returns a hash reference containing all of the data from the authentication table for the current user and authentication plugin. Note that by validating the username first, you are assured that the current user is that of the username entered in the form field as it gets reset in the authenticate method of the superclass. When you call getParams() in your extension of authenticate, you receive the data from the user attempting to log in rather than that of the visitor user.

 

From this hash you retrieve the identifier field from the database which stores your hashed password. You can then compare that to the hash of the clear text password that was passed in. If the password is empty or the hashes don't match, you know that the password entered is incorrect. Then, set an error message in the object and reset the user in the authentication session to the visitor object. If you don't do this step, the user will be logged in even though he wasn't properly authenticated against the system. You can then return false to the login method which will relay your error message to the user, as you will see shortly. If the passwords do match, simply accept the login performed by the superclass method and return true to the login method.

 

Now, simply call your authenticate method, pass it the username and password that were submitted from the displayLogin form, and decide what to do on a successful and unsuccessful login. In the example, if authentication is successful, you'll simply let the superclass do the work of logging the action and getting the user to the right page. If authentication is unsuccessful, you'll return the user to the displayLogin page and output an error message.

 

sub login {

#Shift the class off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Get the username from the form post

my $username = $session->form->process("username");

#Get the password from the form post

my $password = $session->form->process("identifier");

#If the username and password combination don't authenticate

unless($self->authenticate($username,$password)) {

#Set the http status of the page returned

$session->http->setStatus("401","Incorrect Credentials");

#Log a security warning to the error log

$session->errorHandler->security(

"login to account $username with invalid information."

);

#Return the displayLogin page and pass it the error for display

return $self->displayLogin($self->error);

}

#Authentication was successful, continue logging in the user

return $self->SUPER::login();

}

 

logout()

Now that your users can log in, you should probably give them a way to log out. Fortunately, this process is so simple it almost never requires extending the functionality provided by the superclass. In most cases, you don't need to provide a logout method.

 

In cases where you do need to add or change the default logout functionality, it is a good idea to extend the superclass logout method rather than override it completely as it handles ending the current user's session, setting the current user to the visitor user, and running any logout script that might be necessary.

 

Suppose that you wish to redirect the user to the home page whenever she/he logs out of the website. To do this, simply extend the functionality of the logout() method and set the http header to redirect the user to the site URL after the logout is complete.

 

sub logout {

#Shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Log the user out with the superclass method

$self->SUPER::logout();

#Redirect the user to the homepage of the website

my $homepage = $session->url->getSiteURL;

$session->http->setRedirect($homepage);

#Return the default behavior

return;

}

 

Authentication Data

Now that your users are able to log in and log out of your site, it's time to allow user accounts to be created, updated, and deactivated. Before getting into that, it's important to understand how data related to users, and authentication in general, is stored in WebGUI.

 

Settings

Let's start by talking about local and global authentication settings. You've already seen examples of retrieving local authentication settings in this chapter using the getSetting() method: $auth->getSetting([$settingName]);

 

All settings in WebGUI are stored in the settings table which looks like this:

 

Field

Type

Null

Key

name

varchar(255)

No

PRIMARY

value

text

Yes

 

 

Notice that there is no concept of namespace, which creates a small problem with authentication in that each plugin can have its own local settings, but it's not possible to reuse the same setting name as it serves as the primary key of the table. To overcome this, the getSetting method of the superclass automatically namespaces your local settings for you by placing the name of your authentication identifier (which must be unique to each WebGUI site) in front of the name of the setting passed in, capitalizing the first letter of the setting name so that it becomes camel cased. Thus, $auth->getSetting(“ipvisitorId”); for the Yourplugin authentication plugin returns the setting yourpluginIpvisitorId.

 

Global authentication settings are returned by calling the get method from the settings object within the session object.

 

my $globalSetting = $self->session->setting->get(“encryptLogin”)

 

Global and local authentication settings are found in the Settings screen of WebGUI's administrative interface. Global authentication settings are found on the User tab. Here you'll find settings such as whether or not to allow Anonymous Registration, what workflow activity to run when a new regular or admin user is created or updated, whether or not karma is enabled and how much karma should be given for each login, and so on. WebGUI's Authentication superclass handles the display and processing of all global authentication settings, so if you completely override something, it will be up to you to determine what to do with these settings. You may choose not to use them at all, but they will not be removed from the user interface, making it is easy to create something that is confusing to use. For instance, if you decide to override the login method and completely ignore karma, the enable karma setting will not work for your plugin regardless of whether or not it is turned on. You cannot add global authentication settings without directly modifying the core of WebGUI.

 

Local authentication settings are found on the Authentication tab of the Settings screen and are located within the box titled with the name of the authentication identifier for each plugin. These settings are specific to each authentication plugin, so anything custom you wish to add is done here. Recall that previously in the chapter the getSetting method was used to retrieve the ipvisitorId, allowedIpAddress, and all of the templateIds.

 

editUserSettingsForm()

To add local settings for your authentication plugin you must override the editUserSettingsForm() method in the superclass. If you look, this is an empty method because, by default, WebGUI assumes your authentication method has no settings. Another important thing to note is that this method does not need to be callable, as it will never be called directly. Instead, this method is executed for each of the authentication plugins installed on your site (to see the executing code, look at WebGUI::Operation::Settings::www_editSettings()). If you do not override this method, your auth method will appear on the Authentication tab of the Settings screen as an empty box.

 

To override this method, you must create a WebGUI::HTMLForm object and return the printRowsOnly method as the calling function to write the raw form elements directly to the page. In this example, you'll want to create settings for the ipvisitorId, allowedIpAddress, and the various templateIds of the authentication plugin. It is vital that you namespace your settings as you do not want your form elements to share the same name as other form elements on the Settings screen. Use the namespace rules described above to ensure that you will not have naming conflicts, and that your settings can be retrieved with the getSetting() method mentioned earlier.

 

sub editUserSettingsForm {

#shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Create the WebGUI::HTMLForm object

my $f = WebGUI::HTMLForm->new($session);

#Create the form element for the ipvisitorId setting

$f->user(

name => "ipIpvisitorId",

value => $self->getSetting("ipvisitorId"),

label => "IP Visitor User",

);

#Create the form element for the allowedIPAddress setting

$f->text(

name => "ipAllowedIPAddress",

value => $self->getSetting("allowedIPAddress"),

label => "Allowed IP Address",

);

#Create the form element for the accountTemplate setting

$f->template(

name => "ipAccountTemplateId",

value => $self->getSetting("accountTemplateId"),

namespace => "Auth/IP/Account",

label => "Display Account Template",

);

#Create the form element for the createAccountTemplate setting

$f->template(

name => "ipCreateAccountTemplateId",

value => $self->getSetting("createAccountTemplateId"),

namespace => "Auth/IP/Create",

label => "Registration Template"

);

#Create the form element for the loginTemplate setting

$f->template(

name => "ipLoginTemplateId",

value => $self->getSetting("loginTemplateId"),

namespace => "Auth/IP/Login",

label => "IP Login Template"

);

#Return the raw form rows

return $f->printRowsOnly;

}

 

After shifting the object off the argument stack and setting the session variable, create a new WebGUI::HTMLForm object. Then, start adding elements of various types to the form. For the ipvisitorId setting, use a WebGUI::Form::User element. For the allowedIpAddress element, use a WebGUI::Form::Text element, and for the templates use a WebGUI::Form::Template element. Also notice that each of the names follows the namespace rule. The name of the authentication plugin is “IP”, thus the ipvisitorId field is named “ipIpvisitorId” and so on. You should also notice that for each of the values you simply use the getSettings method on the name of the setting as it will be referred to throughout the rest of the code base. This makes it less confusing to others reading your code and easier to support for yourself in the future. Finally, return the printRowsOnly method of the WebGUI::HTMLForm object, which will write your form directly to the Authentication tab of the Settings screen within the area for your authentication plugin.

 

editUserSettingsFormSave()

Now that you've got your form displaying on the Authentication tab of the Settings screen, you need to ensure that your settings are saved to the settings table. Do this by overriding the editUserSetttingsFormSave() method. Once again, this method is empty in the superclass so it is up to you to save your own settings. Like the editUserSettingsForm() method, this method does not need to be callable as it is executed for each authentication plugin installed on your site (see the executing code in WebGUI::Operation::Settings::www_saveSettings()).

 

To save settings, process the data from the form post, handle any user errors that might exist in the data posted, and then use the WebGUI::Sesssion::Setting API to write your settings to the database. In this example, you have five fields that are posted via the editUserSettingsForm() method to handle.

 

sub editUserSettingsFormSave {

#shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Create a form object variable

my $form = $session->form;

#Create a setting object variable

my $setting = $session->setting;

#Create an array for storing errors

my @errors = ();

#Get the data from the form post

my $ipvisitor = $form->process("ipIpvisitorId","user");

my $allowedIPAddress = $form->process("ipAllowedIPAddress","text");

my $accountTmplId = $form->process("ipAccountTemplateId","template");

my $createAcctTmplId

= $form->process("ipCreateAccountTemplateId","template");

my $loginTemplateId = $form->process("ipLoginTemplateId","template");

#Write the data to the settings table

$setting->set("ipIpvisitorId",$ipvisitor);

$setting->set("ipAllowedIPAddress",$allowedIPAddress);

$setting->set("ipAccountTemplateId",$accountTmplId);

$setting->set("ipCreateAccountTemplateId",$createAcctTmplId);

$setting->set("ipLoginTemplateId",$loginTemplateId);

#Handle errors

#Don't allow an IP address to be set if an ipvisitor is not

if($allowedIPAddress ne "" && $ipvisitor eq "") {

#Unset the setting. Don't remove it or it will not be able to be set again

$setting->set("ipAllowedIPAddress","");

#Push a message on to the error stack

push(@errors,"IP Visitor is empty. Allowed IP Address unset");

}

#Return a reference to the error array

return \@errors;

}

 

After shifting the object off the argument stack and setting up some session variables, begin by processing the form fields from the form post. You'll notice that in this method you must refer to the names of the fields that are posted, which you namespaced in the previous section. Also, there is no authentication API method for setting data without using the namespace, so you will simply refer to the full name of the setting throughout this method. If you prefer, you can refer to the full names of the settings throughout the application and avoid the getSetting() method all together, as it is simply a convenience method.

 

Once you have processed your form variables, write all of the data to the settings table. You may be wondering why errors weren't processed before writing the database settings. Because each authentication method handles writing its own data to the database, you can't stop other data from being posted. Since other settings are going to be saved regardless of any errors that happen in your authentication method, it makes sense to save the settings first and then unset anything that might cause the user problems. You don't want the entire post to fail if one mistake is made. Simply protect the user from harm by unsetting anything that could be a problem, and notify the user that there is an issue with the data submitted.

 

After writing all of the settings to the database, check for errors. If the user set an allowed IP address, your authentication method is going to try to automatically log in users coming from that IP address as the IP Visitor user. If that user doesn't exist, you have a problem. For that reason, you cannot allow the Allowed IP Address to be set unless the IP Visitor field is also set. Do this by updating the setting in the database to an empty string and pushing an error message onto the error array. It is important not to use the remove method in the settings API as this will completely remove the setting row from the database, making the setting impossible to set again. Finally, return a reference to your error array, which will be reported to the user by the calling function.

 

User Data

Now that you have your local authentication settings taken care of, you can finally allow user accounts to be created, updated, and deactivated. Before diving into the guts of how to do this, it's important to take a look at user accounts in WebGUI and where data is stored.

 

All users in WebGUI, regardless of the authentication method, must have a WebGUI user account. As you've seen, even visitors who have not registered for the site have a user account: the visitor account (userId 1). Every user account in WebGUI is stored in the users table which looks like this:

 

Field

Type

Null

Key

userId

varchar(22)

No

PRIMARY

username

varchar(100)

Yes

UNIQUE

authMethod

varchar(30)

No

 

dateCreated

int(11)

No

 

lastUpdated

int(11)

No

 

karma

int(11)

No

 

status

varchar(35)

No

 

referringAffiliate

varchar(22)

No

 

friendsGroup

varchar(22)

No

 

 

Notice that it is in this table that you store what authMethod (authentication identifier) is used by each user. When a user attempts to log in, you look up the authMethod, create an instance of that method, and call the init method, as seen earlier.

 

You'll also notice that you don't store a password in this table. That is because the password is strictly dependent on the authentication plugin being used. For instance, the example specifies that passwords be SPA-512 hashed. If the password field existed in the user table and was of length just long enough to support MD5 hashed, the field wouldn't be long enough to hold your password hash. Some authentication methods don't even use passwords that are stored in WebGUI. The LDAP plugin, for instance, authenticates the password posted at login against the LDAP server. The password isn't even stored in WebGUI for that module, so what use would it be in the users table?

 

Passwords, and other authentication specific user data, are stored in the authentication table, which looks like this:

 

Field

Type

Null

Key

userId

varchar(22)

No

PRIMARY

authMethod

varchar(30)

No

PRIMARY

fieldName

varchar(128)

No

PRIMARY

fieldData

text

No

 

 

The composite key of userId, authMethod, and fieldName ensures that no user can have more than one data entry for a field within any authentication plugin. It is here that you will store things like each user's password, the last time the user logged in, whether or not the user is allowed to change his/her password, the distinguished name of the user on the LDAP server, and so on.

 

The authentication superclass contains API methods for retrieving and manipulating this data. One such method was discussed in the login section of this chapter.

 

$auth->getParams

This method retrieves all of the the fieldName and fieldData pairs for a given user and a given authentication method, returning them as a hash reference.

 

$auth->saveParams($userId,$authMethod,$propertiesHashRef)

The saveParams method writes all of the key/value pairs passed in as a properties hash reference to the authentication table for the userId and authMethod passed in. The key of the hash is expected to be the fieldName column, and the value of the hash is expected to be the fieldData column.

 

$auth->deleteParams()

Typically, an authentication method doesn't handle deleting users, so this method, along with the following, should almost never be used; however, it is good to know about them since they do exist and may serve a purpose in any external applications you may write. This method deletes all parameters from the authentication table for the user currently stored in object data. This method can be very dangerous to call as it crosses over authentication methods, and should only be called when completely removing a user from WebGUI.

 

$auth->deleteSingleParam($userId,$authMethod,$fieldName)

This method deletes a single field from the authentication table for the userId and authMethod passed in. Unlike the authentication settings, it is okay to remove entire rows of data from the authentication table as they are re-inserted by the saveParams() method if the user's information is ever updated.

 

Finally, it is worth noting one more table where user data is stored, which is the userProfileData table, a sample of which is displayed below:

 

Field

Type

Null

Key

userId

varchar(22)

No

PRIMARY

email

varchar(255)

No

 

firstName

varchar(255)

No

 

middleName

varchar(255)

No

 

lastName

varchar(255)

No

 

 

This table is an extension of the users table and stores all of the profile data related to a user account. It is flexible and may not look the same on any other WebGUI site, as it can be modified via WebGUI's user profiling interface (for more on user profiling, see the Users chapter of the WebGUI Administrators Guide). When creating user accounts, it is sometimes necessary to collect additional data to be stored in this table.

 

Creating User Accounts

Since each authentication plugin stores different data related to each user, it is the responsibility of the plugin to provide a way for WebGUI's user management system to update this data so users can properly authenticate against the system. Likewise, if you want users to be able to anonymously create accounts using your authentication plugin, it is important to create a way for that to happen as well.

 

There are two methods available for creating user accounts. The first must always be implemented if you have custom user data that needs to be stored. The second need only be implemented if you wish to allow users to create their own accounts. Please note, however, that there is a global setting for turning anonymous registration on and off, which can lead to some confusion by those using your plugin.

 

editUserForm()

To allow custom user data to be updated in WebGUI's user management system, you need to provide it the necessary form elements. Do this by overriding the editUserForm() method in the superclass. Again, you will note that this method is empty in the superclass, implying that it will be assumed that your custom authentication method has no custom user data. Also, this method does not need to be callable as it will never be called directly. Instead, each method is executed for each of the authentication plugins installed on your site (the executing code can be found in WebGUI::Operation::User::www_editUser). If you do not override this method, your auth method will appear on the Account tab of the Add/Edit User page of WebGUI's User Management system without any associated form fields.

 

Like editUserSettingsForm(), override this method by creating a WebGUI::HTMLForm object and return the printRowsOnly method. In the example, you'll want to create a form field for your customized password. It is again vital that you namespace your fields as you do not want the form elements to share the same name as other form elements on the page. Use the namespace rules described above to ensure uniqueness.

 

sub editUserForm {

#shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Create the WebGUI::HTMLForm object

my $f = WebGUI::HTMLForm->new($session);

#Create the form element for the password field

$f->password(

name=>"ipPassword",

label=>"Password",

value=>"password"

);

#Return the raw form rows

return $f->printRowsOnly;

}

 

After shifting the object off the argument stack and setting the session variable, create a new WebGUI::HTMLForm object. Then, add the password element to the form using the WebGUI::Form::Password form module. Notice that the name of the password form element follows the namespace rule.

 

You might be wondering why the value of password is hard coded to the string “password”. This is for several reasons. First, the password is stored as a ISA-512 hash, so it isn't possible to submit the actual clear text value of the password. Even if you could, you wouldn't want anyone to be able to view source on the page and see the clear text value for each user's password. Instead, put some default text in to “trick” the user into thinking the password is being displayed to avoid an unintentional change. This also serves you on the form post to ensure that the user has actually changed the password to something else before you update.

 

Finally, return the printRowsOnly method of the WebGUI::HTMLForm object, which will write your form directly to the Add/Edit User screen of WebGUI's User Management System for your authentication plugin.

 

editUserFormSave()

Now that you've got your form displaying on the Add/Edit User page of WebGUI's User Management System, you need to ensure that the data is saved properly to the authentication table for the correct user. Do this by extending the editUserFormSave() method in the superclass. The superclass method expects a hash reference of properties to write to the authentication table as it simply calls the saveParams() method discussed earlier in the chapter. Like editUserForm(), this method does not need to be callable as it is executed for each authentication plugin installed on your site (see the executing code in WebGUI::Operation::User::www_editUserSave()).

 

To save settings, process the data from the form post, create the properties hash reference with any data that needs to be saved, and then call the superclass method. If you choose to override this method entirely, you would simply call the saveParams() method rather than calling the superclass method. In this example, you only have your custom password field to handle.

 

sub editUserFormSave {

#shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Create a form object variable

my $form = $session->form;

#Create an empty properties hash reference

my $props = {};

#Get the password from the form post

my $password = $form->process("ipPassword",”password”);

#Update the password if it was changed

if ($password && $password ne "password") {

$props->{identifier} = Digest::SHA::sha512_base64($password);

}

#Call the superclass method with the properties hash ref

$self->SUPER::editUserFormSave($props);

}

 

After shifting the object off the argument stack and setting up some session variables, create an empty hash reference to store your properties in and then process your password form field. When you processed your settings fields in the editUserSettingsFormSave() method, you were able to handle errors and return messages to the user. In this method, you can still handle errors if you choose to, but you cannot return any messages to the user. For that reason, it is usually a good idea to simply save the data as entered by the user. If you absolutely need validation, it is possible to add client side validation using the WebGUI:HTMLForm API; however, this is not discussed here.

 

In the example, you simply check to see if a password has been set by the user. Some logic is added to assure that the password is not empty and that the password is not the same as the default value, in which case you are assured that the user has typed something new into this field and you should change the password. Finally, call the superclass method passing in the authentication data to be set.

 

createAccount()

Now that user data can be created and updated through WebGUI's User Management System, it's time to allow users to create and update their own accounts. As mentioned earlier, you can choose not to allow this to happen; however, there are global settings which enable this ability regardless of what you do. It is usually a good idea to implement these methods and simply return a page stating that the action is not possible with the authentication plugin being used.

 

The createAccount method is similar to the displayLogin method in that it is almost always called as the visitor user. In order to use the createAccount method for your authentication plugin, you must set it to be the default plugin for the WebGUI site.

 

If you use the default WebGUI authentication plugin and turn on anonymous registration in the user settings, you will see a “click here to register” link on the default WebGUI login template when you are logged out. Clicking this link brings you to this method which allows you to create your own account for logging in.

 

This method is really a convenience method which returns a template containing the core form elements that are used for creating a new account. Typically, you will not want to override this method. Instead, you will want to extend it with some options that are available from the superclass.

 

$auth->createAccount([postMethod, templateVariables])

 

“postMethod” is a string which indicates which callable method should be posted to when the user posts the create account form. By default, postMethod is set to “createAccountSave”, a method covered in the next section. If you choose to pass in your own postMethod, the form will be generated so that when the user fills in the account creation form, it will post to whichever method you specify. Keep in mind that this method must be listed in your callable methods. For example, $auth>displayLogin('doCreateAccount') will attempt to post to the doCreateAccount method of your custom authentication plugin.

 

“templateVariables” is a hash reference containing template variables that you can create in your extension of createAccount that will be exported to the template. By default, the following template variables are available from the createAccount superclass method:

 

Variable

Description

title

Internationalized title of the login page.

create.form.header

<form> open tag containing action and method and hidden fields which specify which page to post to.

create.form.profile

Template loop containing all of the profile fields to display on the page.

profile.formElement

Loop field within create.form.profile which contains the form element.

profile.formElement.label

Loop field within create.form.profile which contains the internationalized label for the form element.

profile.requied

Loop field within create.form.profile which determines whether or not the form field is required for submission.

create.form.profile.<id>.formElement

Individual profile element that can be used outside the form loop where <id> represents the name of the field you wish to access.

create.form.profile.<id>.formElement.label

Individual profile element label that can be used outside the form loop where <id> represents the name of the field you wish to access.

create.form.profile.<id>.required

Individual element that determines whether or not the form field represented by <id> is required or not.

create.form.submit

Submit button for posting the account creation form.

create.form.footer

</form> closing tag

login.url

URL which calls the init method of the default authentication plugin.

login.label

Internationalized text for the login.url field.

 

You will notice the profile form element loop and single access variables among the template variables. WebGUI allows users to create custom profile fields and determine whether or not those, or any of the default profile fields that come standard, should be displayed or required at login. These loops export that data to the template for display. If you choose to override this method completely, it will be your responsibility to make sure that this works properly.

 

You extend the template by passing in a hash reference of your own custom template variables that will be used on your create account page. Notice that these template variables do not have a place to enter a username and password since you may require neither for creating an account (you might pull username directly out of an LDAP by asking a user to enter some other information). In order to get those items into your template, you'll need to pass them along as a hash reference to the createAccount superclass method. It is important to note that you cannot change the template variables in the superclass. If you ever want to change something in the superclass variables, you will need to create entirely new template variables and use those instead. You could also choose to override the createAccount method completely and make it do whatever you need it to do.

 

Extend the createAccount method and add the username and password fields. In addition, ask the user for a password confirmation to validate that the password was entered correctly. Also, if the user is already logged in, you would rather not display this page. Instead, bounce the user to the displayAccount page, which is discussed a bit later. Also, check to make sure that the anonymous registration field is turned on before allowing access to the page. Finally, add an error message that you can display to the user in the case registration fails for some reason.

 

sub createAccount {

#shift the object off the argument stack

my $self = shift;

#shift off any message that we might have passed in from a failed login

my $message = shift;

#Create a session variable

my $session = $self->session;

#Create a form object variable

my $form = $session->form;

#Create a setting object variable

my $setting = $session->setting;

#Create an empty hash ref to use for our template variables

my $vars = {};

#Return the display account page if the user is already logged in

if($self->userId ne "1" && $self->userId ne $self->getSetting("ipvisitorId")) {

return $self->displayAccount($message);

}

#Otherwise, if anonymous registration is not enabled, return the login page

elsif (!$setting->get("anonymousRegistration")) {

return $self->displayLogin;

}

#Add some template variables to the empty hash we created

#Return any error messages passed in to the page

$vars->{'create.message'} = $message if ($message);

#Create a field for displaying the username

$vars->{'form.username' } = WebGUI::Form::text($session, {

name => "username",

value => $form->process("username"),

});

#Create a label field for the username

$vars->{'form.username.label' } = "Username";

#Create a field for displaying the password

$vars->{'form.password' } = WebGUI::Form::password($session, {

name => "password"

});

#Create a label field for the password

$vars->{'form.password.label' } = "Password";

#Create a field for displaying the password confirmation

$vars->{'form.passConfirm' } = WebGUI::Form::password($session, {

name => "passwordConfirm"

});

#Create a label field for the password confirmation

$vars->{'form.passConfirm.label'} = "Password Confirm";

return $self->SUPER::createAccount("createAccountSave",$vars);

}

 

Start out by shifting the session object off the argument stack as you usually do. Then, also shift a message off. Set up some session variables to use through the code and create an empty hash reference to store your template variables. Now, check the userId for the authentication session. If the userId is not “1”, which is the userId of the visitor, and the userId is not that of the ipvisitor (ipvisitors should also be able to create new accounts), that means that the user is already logged in. You don't want users who are already logged in to be able to create new accounts, so show them the displayAccount form instead and pass it any message that might be forwarded. Otherwise, if the anonymous authentication flag is turned off, visitors shouldn't be able to create their own accounts. If this happens, bounce the user to the displayLogin page. If this is the visitor or ipvisitor user, then you will create your custom template variables and pass them to the superclass method.

 

createAccountSave()

Now that there is a form available for users to create new accounts, you have to make sure it can be submitted and have the account successfully created. The createAccountSave superclass method handles storing most of the data, but leaves all of the validation up to you. For this reason you will nearly always want to extend or override this method. If you decide to override the method, the createAccountSave method handles:

 

  • creating the new user account

  • assigning karma if karma is enabled

  • saving all of the profile fields created in the createAccount() superclass method

  • saving all the authentication user data

  • sending a welcome message if a properly namespaced sendWelcomeMessage field exists for your authentication plugin

  • logging in the user

  • logging the login

  • running any scripts configured to run on registration

  • redirecting the user to the page he/she was trying to access when he/she created the account

  • and logging the user's registration if the user was invited to join the site.

 

As you can see, there is a lot to handle, so you will want to extend this method rather than completely override it. You extend the method by passing it the username of the user creating the account, a properties file containing the authentication user data to write to the authentication table, the password of the user creating the account (password is only used to send to the user in the welcome message. If you do not use a password in your authentication scheme, you do not need to pass it in.), and a validated profile data hash, which you will see how to provide shortly.

 

All validating must be done in in this method. In the example you will need to validate once again that anonymous registration is enabled, that the username entered conforms to WebGUI's username standards, that the password entered matches the confirm password field, and that all of the required profile fields have been filled in.

 

Once you complete validation, you hand the superclass the data it needs and let it create the user and log him/her in.

 

sub createAccountSave {

#shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Create a form object variable

my $form = $session->form;

#Create a setting object variable

my $setting = $session->setting;

#Return the display account page if the user is already logged in

if($self->userId ne "1" && $self->userId ne $self->getSetting("ipvisitorId")) {

return $self->displayAccount;

}

#Otherwise, if anonymous registration is not enabled, return the login page

elsif (!$setting->get("anonymousRegistration")) {

#Log a security warning to ourselves that someone tried to hack a login

$session->errorHandler->security("Registration hack attempted!");

return $self->displayLogin;

}

#Process our form fields from createAccount()

my $username = $form->process('username',"text");

my $password = $form->process('password',"password");

my $passConfirm = $form->process('passwordConfirm',"password");

#Use the Profile API to validate the profile data

my ($profile, $error)

= WebGUI::Operation::Profile::validateProfileData($session, {

regOnly => 1

}

);

#Validate the username

unless ($self->validUsername($username)) {

$error .= $self->error;

}

#Validate that a password was entered

if($password eq "") {

$error .= "<li>Password cannot be empty</li>";

}

#Validate that the password matches the confirmation

unless ($password eq $passConfirm) {

$error .= "<li>Password does not match confirmation</li>";

}

#Return the user to the create account page if an error happened

unless ($error eq "") {

return $self->createAccount($error);

}

#Set the user data properties

my $props = {};

$props->{ identifier } = Digest::SHA::sha512_base64($password);

return $self->SUPER::createAccountSave(

$username,

$props,

$password,

$profile

);

}

 

Start out by shifting the session object off the argument stack as you usually do. Then, set up some session variables to use through the code. Now, check the userId for the authentication session. If the userId is not “1”, which is the userId of the visitor, and the userId is not that of the ipvisitor (ipvisitors should also be able to create new accounts), that means that the user is already logged in. You don't want users who are already logged in to be able to create new accounts, so you'll show them the displayAccount form instead. Otherwise, if the anonymous authentication flag is turned off, and someone tries to post to this form, he is trying to hack your site. If this happens, you will log a security message for yourself and bounce the user to the displayLogin page.

 

Now, process the form fields that you created in createAccount() for error handling. Use the validateProfileData method of the WebGUI::Operation::Profile API, which will loop through all of the profile fields that are flagged for display on registration, determine whether or not the required fields are filled in, and pass back a hash containing the profile fields to store in the database as well as any errors that may have occurred.

 

Once you do this, validate the username. There is a convenience method available in the superclass to check the validity of the username supplied as certain rules need to be enforced when creating usernames in WebGUI.

 

$auth->validUsername($username);

This method security checks the username to make sure that macros are not used, that the username is not a duplicate, that it is not empty, and that it does not contain whitespace at the beginning or end. Any errors are logged in object data and can be retrieved with the error() method.

 

Finally, validate that the password is not empty and that it matches the password confirmation posted. If any errors occurred in your validation, you return the user to the createAccount method and pass it the errors so they can be displayed.

 

If all of the data is valid, set a properties hash containing the SHA-512 hashed password and call the superclass method to pass it the appropriate data. This will successfully create the account and log the user in for the first time.

 

displayAccount()

Once the user is logged in, it is sometimes important that he/she is able to update the authentication information. Do this by overriding or extending the displayAccount method in the superclass. This method is truly a convenience method as it simply sets up some template variables you would otherwise declare yourself. By default, nothing is posted on the displayAccount method, so unlike some of the other methods you've seen, a default post method is not provided and you will have to create your own callable method for posting to should you choose to extend this method.

 

To extend the method, if you so choose, you will call it with some options that you have seen before.

 

$auth->displayAccount(postMethod,[templateVariables])

“postMethod” is a string which indicates which callable method should be posted to when the user makes a request. Unlike other similar superclass methods, postMethod is required to extend this. Keep in mind that whatever method you choose here must be listed in your callable methods. For example, $auth->displayAccount('displayAccountSave') attempts to post to the displayAccountSave method of your custom authentication plugin.

 

“templateVariables” is a hash reference containing template variables that you can create in your extension of displayAccount that will be exported to the template. By default, the following template variables are available from the displayAccount superclass method:

 

Variable

Description

title

The internationalized title of the account page.

account.form.header

<form> open tag containing action and method and hidden fields which specify to which page to post.

account.form.karma

Amount of karma current user has amassed.

account.form.karma.label

The internationalized title for users karma.

account.form.submit

Submit button for posting account information.

account.form.footer

</form> closing tag

account.options

Form loop containing the different display options that are available based on the user's privileges.

options.display

Loop field within account.options which outputs a link to various user account pages within WebGUI.

 

You extend the template by passing in a hash reference of your own custom template variables that will be used on your display account page. Notice that these template variables do not have any form fields. In order to get the authentication data you would like updated into your template, you'll need to pass them along as a hash reference to the displayAccount superclass method. It is important to note that you cannot change the template variables in the superclass. If you ever need to change something that is in the superclass, you will either need to create entirely new template variables and use those, or override the displayAccount method completely. For this method, either option would be fine.

 

Let's extend the displayAccount method and allow users to change their passwords. In addition, let's also ask the user to confirm the password to validate that the password was entered correctly. Also, if the user is not logged in, display the displayLogin page instead.

 

sub displayAccount {

#shift the object off the argument stack

my $self = shift;

#shift off any message that we might have passed in from a failed update

my $message = shift;

#Create a session variable

my $session = $self->session;

#Create an empty has to use for our template variables

my $vars = {};

#If the user is not logged in, return the displayLogin page

if($self->userId eq "1" || $self->userId eq $self->getSetting("ipvisitorId")) {

return $self->displayLogin;

}

#Create template varibales

#Output any message that might have been passed in

$vars->{'account.message' } = $message if ($message);

#Create the password field and label

$vars->{'form.password' } = WebGUI::Form::password($session,{

name => "password",

value =>"password"

});

$vars->{'form.password.label'} = "Password";

#Create the password confirm field and label

$vars->{'form.passwordConfirm'} = WebGUI::Form::password($session,{

name => "passwordConfirm",

value => "password"

});

$vars->{'form.passwordConfirm.label'} = "Confirm Password";

return $self->SUPER::displayAccount("displayAccountSave",$vars);

}

 

Start out by shifting the session object off the argument stack as you usually do. Then, shift any message off that may have been passed in. Next, set up a session variable to use throughout the code and create an empty hash reference to store your template variables. Now, check the userId for the authentication session. If the userId is “1”, which is the userId of the visitor or that of the ipvisitor (ipvisitors should not be able to update the ipvisitor account), show the user the displayLogin page. If this is a valid registered user, create your template variables for password and password confirm and pass them to the superclass method.

 

As mentioned earlier, you also need to create a custom post method to save the account information, which in this case is simply an updated password. Do this by adding a displayAccountSave method to your authentication plugin.

 

sub displayAccountSave {

#shift the object off the argument stack

my $self = shift;

#Create a session variable

my $session = $self->session;

#Create a form object variable

my $form = $session->form;

#If the user is not logged in, return the displayLogin page

if($self->userId eq "1" || $self->userId eq $self->getSetting("ipvisitorId")) {

return $self->displayLogin;

}

#Get the password data from the form post

my $password = $form->process('password');

my $passConfirm = $form->process('passwordConfirm');

#Create an empty error message

my $error = "";

#Check to make sure the password is valid

if($password eq "") {

$error = "Password cannot be empty";

}

elsif($password ne "password" && $password ne $passConfirm) {

$error = "Password does not match confirmation";

}

#If there are no errors and the password was changed, update the password

if ($error eq "" && $password ne "password") {

#Save the new password

my $props = {};

$props->{identifier} = Digest::SHA::sha512_base64($password);

$self->saveParams($self->userId,$self->authMethod,$props);

}

#Set a message to return to the user

my $display = $error || "Account updated!";

#Return the display account page

return $self->displayAccount($display);

}

 

Start out by shifting the session object off the argument stack as you usually do. Then, set up some session variables to use through the code. Now, check the userId for the authentication session. If the userId is “1”, which is the userId of the visitor or that of the ipvisitor (ipvisitors should not be able to update the ipvisitor account), show the user the displayLogin page, as neither of these users should be able to update this account. Next, retrieve password data from the form post and validate that it is not empty and that it matches the password confirm field. If no errors are found and the password was changed (not equal to the default value of “password”), hash the password and save it in the authentication table. Finally, return the user to the displayAccount page with either an error message or a message letting him/her know that the account was updated.

 

deactivateAccount()

WebGUI's authentication API also gives you the ability to let users deactivate their own accounts. While not a necessary feature, this again is a global authentication setting, so at the very least you should override the method and let users know they aren't allowed to self-deactivate.

 

This method is a convenience method which returns a template containing the core form elements that are used for deactivating accounts. Unlike other methods that are similar to this, the templateId for this method is hardcoded, so if you want to use your own template you will need to completely override the method.

 

$auth->deactivateAccount([postMethod])

“postMethod” is a string which indicates which callable method should be posted to when the user posts the create account form. A default post method is not provided by the superclass even though the superclass contains a “deactivateAccountConfirm” method, which is covered in the next section. If you choose to extend this method, you can either pass the deactivateAccountConfirm, which allows you to let the superclass handle, extend yourself, or override. Or, you can pass your own method, in which case the form will be generated so that when the user fills in the account creation form it will post to whichever method you specify. Keep in mind that this method must be listed in your callable methods. For example, $auth->deactivateAccount('deactivateUser') attempts to post to the deactivateUser method of your custom authentication plugin.

 

Because you cannot pass your own template variables to the method, the template is not extendable. The only reason you would ever extend this method would be to add additional functionality not handled by the superclass. If you do choose to override this method, the superclass method simply checks to make sure the user attempting to use the feature is valid (you cannot deactivate the visitor or admin account) and that the setting to allow users to self-deactivate is enabled.

 

Let's extend the template and ensure that the IP Visitor account, as well as the regular visitor account, cannot be self-deactivated. If either of these users attempts to deactivate their accounts, you'll return the displayLogin page.

 

sub deactivateAccount {

#shift the object off the argument stack

my $self = shift;

#If the user is not logged in, return the displayLogin page

if($self->userId eq "1" || $self->userId eq $self->getSetting("ipvisitorId")) {

return $self->displayLogin;

}

#Return the superclass deactivateAccount method

return $self->SUPER::deactivateAccount("deactivateAccountConfirm");

}

 

Start out by shifting the session object off the argument stack as you usually do. Then, check the userId for the authentication session. If the userId is “1”, which is the userId of the visitor or that of the ipvisitor (ipvisitors should not be able to deactivate the ipvisitor account), show the user the displayLogin page. If this is a valid registered user, allow the superclass to do the rest of the work.

 

deactivateAccountConfirm()

As with the deactivateAccount method, the deactivateAccountConfirm() method of the superclass handles most of what you will need to do when deactivating WebGUI accounts. You can choose to override this method completely, but more than likely you'll just need to extend it to add functionality specific to your authentication plugin.

 

If you do choose to override this method, it simply checks to make sure the user attempting to deactivate the account is not the visitor or the admin, sets the user's account status to “Selfdestructed”, and logs the user out by calling the logout method. It's important to note that if you choose to implement the deactivateAccountConfirm method, that you also have implemented the logout method, otherwise your user will be presented with an error.

 

Extend the deactivateAccountConfirm method to ensure that the IP Visitor user account cannot be deactivated.

 

sub deactivateAccountConfirm {

#shift the object off the argument stack

my $self = shift;

#If the user is not logged in, return the displayLogin page

if($self->userId eq "1" || $self->userId eq $self->getSetting("ipvisitorId")) {

return $self->displayLogin;

}

#Return the superclass method

return $self->SUPER::deactivateAccountConfirm;

}

 

After shifting the session object off the argument stack, check the userId for the authentication session. If the userId is “1”, which is the userId of the visitor or that of the ipvisitor (ipvisitors should not be able to deactivate the ipvisitor account), show the user the displayLogin page. If this is a valid registered user, allow the superclass to do the rest of the work.

 

Creating Custom Methods

As shown in the displayAccount section, sometimes you want to create your own custom authentication methods. WebGUI's authentication plugin system is completely extensible, meaning you are not tied down to the methods talked about here so far. In fact, you could go as far as to use only the init() method, and implement your own custom solution.

 

Typically, however, you will want to use many of the convenience methods available and simply add the extra ones you need. Earlier in the chapter a URL was created which linked to a recoverPassword method. There is no superclass definition for such a method, but it is completely within your power to create one.

 

To add a custom method to your authentication plugin, simply update the callable stack to add the “recoverPassword” method, and then create the subroutine to do what you define. This example outputs some text which allows the user to send someone on Plain Black's staff an email, but you can imagine what you could do.

 

sub recoverPassword {

#shift the object off the argument stack

my $self = shift;

#If the user is logged, return the displayAccount page

if($self->userId ne "1" && $self->userId ne $self->getSetting("ipvisitorId")) {

return $self->displayAccount;

}

#Return a message to the user with instructions for recovering their password

my $message = q|

To recover your password, please send an email to

<a href="mailto:passwordrecovery@plainblack.com">

passwordrecovery@plainblack.com

</a>.

Someone from our staff will send you your new

password within 24 hours.

|;

return $message

}

 

Extending Existing Authentication Plugins

Many times you don't need to write a whole new authentication plugin. Sometimes one of the default plugins that comes with WebGUI, or one of the plugins you've created yourself, does almost everything you need, but simply requires a small tweak here or there. In those cases, you can simply extend those plugins directly, instead of WebGUI::Auth, by changing the inheritance statement from use base 'WebGUI::Auth' to use base 'WebGUI::Auth::plugin'.

 

There are a few caveats to watch out for when doing this. First, remember that all local settings are namespaced according to the name of your plugin. If the calling method uses namespaced settings, you will need to make sure you provide database entries and forms for creating those settings, otherwise you will be stuck with a situation where the superclass is looking for a property that doesn't exist.

 

Another thing to remember is that the form elements in the editUser and editUserSettings forms all live on the same page. If you plan to run both authentication plugins in the same WebGUI instance, it is vital to override those methods (you can typically copy them exactly) and change the names of the form elements. If you don't do that, you'll wind up with two or more form elements with the same name on the page, and your authentication settings won't be updated properly.

 

Finally, remember that any time you call the superclass you are not calling the method from WebGUI::Auth (unless the method you are calling is not implemented), but rather the plugin you inherited from. It's easy to get hung up trying to debug an error in the wrong place.

 

Follow these guidelines and it should be smooth sailing when you write authentication plugins.

 

Installing Your Authentication Plugin

Now that you know how to write an authentication plugin for WebGUI, you need to know how to install it into the system. While authentication plugins typically don't require their own tables, they do require settings fields to be created, otherwise the state will never hold in the database as the settings API won't save to a field that doesn't exist. Additionally, authentication plugins require templates and need to be added to the config file. You can accomplish this all manually, but it is generally better practice to create an installer and uninstaller for your plugin. How to do each is covered in this section so you have a complete understanding of the integration points and how to create install and uninstall scripts for your plugins.

 

Manually Installing Your Authentication Plugin

Manually installing an authentication plugin requires five steps:

 

  1. Copy your auth method into the proper folder.

  2. Insert any custom settings into the database.

  3. Create any template assets associated with your authentication plugin.

  4. Add the authentication plugin to WebGUI's configuration file.

  5. Configure the authentication settings.

 

Authentication plugins must be located in /data/WebGUI/lib/WebGUI/Auth/. Ensure that your plugin is copied here before you continue the installation.

 

For each of the custom settings that you have defined, you will need to create a row in the settings table with some sort of default data. When manually installing, you can leave these as NULL fields and configure the authentication settings via WebGUI's administrative interface. From a MYSQL prompt for the database for the site you are adding your authentication plugin to, type a series of insert statements that look as follows:

 

insert into settings (name, value) values ('settingName','settingValue');

 

Create your template assets by navigating to the import node and creating a new folder somewhere within. From here you will create templates with the proper namespaces using WebGUI's administrative interface. For more information on how to create templates in WebGUI, see the WebGUI Designers Guide.

 

You'll need to edit the WebGUI configuration file for the site in which you'd like to install the plugin, and add the identifier of your plugin to the authMethods key.

 

“authMethods” : [“LDAP”,”WebGUI”,”Yourplugin”]

 

The plugin identifier is the name of your Perl module with the “.pm”. If your module is called IP.pm, then the identifier for your module should be “IP” and so on. Be sure to restart mod_Perl once you have saved the configuration file.

 

Once you have the configuration file changed properly, your authentication method should be installed. Because this is a manual install, it is important to go to the WebGUI settings in the administrative interface and properly configure the settings for the authentication plugin, otherwise you could have null pointers for templates and other things for which you expect to have data.

 

Automating Your Authentication Plugin Installation

In reality, if you plan for anyone else to use your authentication method, you are going to need to provide an easy way to install your authentication method. Likewise, if you create this method and then want to install it somewhere else six months later, you will probably forget to do something and it will take extra time to run your installation. For this reason, it's important to create an installer for your authentication plugin that does all of the steps above with just a few easy keystrokes, and as you will see, gives you the power to do even more if you choose.

 

By creating an installer, you can cut the five steps involved in manually installing your plugin down to a two step process:

 

  1. Copy your auth method into the proper folder.

  2. Run the installation script for the authentication method.

 

Likewise, you can create an uninstaller that will reverse all of the manual steps if you ever want to remove your authentication method.

 

The install and uninstall scripts should be included at the bottom of your authentication method and should be exported so they can be called on the command line:

 

use base 'Exporter';

our @EXPORT = qw(install uninstall);

use WebGUI::Session;

 

This will allow users to call your install and uninstall methods from /data/WebGUI/lib/ as follows:

 

Perl -MWebGUI::Auth::Myplugin -e install www.mysite.com.conf

Perl -MWebGUI::Auth::Myplugin -e uninstall www.mysite.com.conf

 

Next, create your install method, which is going to relieve most of the tedium involved with the manual install. It will add your authentication plugin to WebGUI's configuration file, it will create your local authentication settings in the database and set some initial values, it will create a folder in the import node in which to store your templates, and then it will proceed to add all of your default templates and commit them.

 

One other thing that you can do in your install script that you had to do manually (and very well may forget the next time you try to install your authentication plugin) is to create your ipvisitor user so you can set it in your authentication settings, assuring you won't have to remember how or why you do this.

 

As you can see, all of the guess work has been taken out of the installation. All you need to do now is distribute the file along with very simple instructions for putting the file in the correct folder and running your install script.

 

sub install {

#First argument on the command line is the config file

my $config = $ARGV[0];

#Second argument on the command line is WebGUI's home dir

#If no second argument exists, use the default home dir

my $home = $ARGV[1] || "/data/WebGUI";

#Die if either $home or $config is empty

unless ($home && $config) {

die "usage: Perl -MWebGUI::Auth::IP -e install www.example.com.conf\n";

}

#Print out a status message to indicate the install is starting

print "Installing asset.\n";

#Open a new WebGUI session for the config file passed in

my $session = WebGUI::Session->open($home, $config);

#Add the authentication method to the config file

$session->config->addToArray("authMethods","IP");

#Create the ipvisitor user

my $ipvisitor = WebGUI::User->new($session,"new");

my $ipvisitorId = $ipvisitor->userId;

$ipvisitor->username("ipvisitor");

$ipvisitor->authMethod("IP");

#Create the local settings

$session->setting->add("ipIpvisitorId",$ipvisitorId);

$session->setting->add("ipAllowedIPAddress","");

$session->setting->add("ipAccountTemplateId","IPtmpl0000000000000001");

$session->setting->add("ipCreateAccountTemplateId","IPtmpl0000000000000002");

$session->setting->add("ipLoginTemplateId","IPtmpl0000000000000003");

# Create a folder asset to store the default template

# Get the import node

my $importNode = WebGUI::Asset->getImportNode($session);

#Add the folder

my $newFolder = $importNode->addChild({

className=>"WebGUI::Asset::Wobject::Folder",

title => "IP Auth Templates",

menuTitle => "IP Auth Templates",

url=> "ip_auth_folder",

groupIdView=>"3"

},"IPAuthFolder0000000001");

#Create the templates

#Account Template Code

my $accountTmpl = q|

<h2><tmpl_var title></h2>

<tmpl_if account.message>

<tmpl_var account.message>

</tmpl_if>

<tmpl_var account.form.header>

<table>

<tmpl_if account.form.karma>

<tr>

<td class="formDescription" valign="top">

<tmpl_var account.form.karma.label>

</td>

<td class="tableData">

<tmpl_var account.form.karma>

</td>

</tr>

</tmpl_if>

<tr>

<td class="formDescription" valign="top">

<tmpl_var form.password.label>

</td>

<td class="tableData">

<tmpl_var form.password>

</td>

</tr>

<tr>

<td class="formDescription" valign="top">

<tmpl_var form.passwordConfirm.label>

</td>

<td class="tableData">

<tmpl_var form.passwordConfirm>

</td>

</tr>

<tr>

<td class="formDescription" valign="top"></td>

<td class="tableData">

<tmpl_var account.form.submit>

</td>

</tr>

</table>

<tmpl_var account.form.footer>



<div class="accountOptions">

<ul>

<tmpl_loop account.options>

<li><tmpl_var options.display></li>

</tmpl_loop>

</ul>

</div>

|;

#Create Account Template Code

my $createAcctTmpl = q|

<h2><tmpl_var title></h2>

<tmpl_if create.message><tmpl_var create.message></tmpl_if>

<tmpl_var create.form.header>

<table>

<tr>

<td class="formDescription" valign="top">

<tmpl_var form.username.label>

</td>

<td class="tableData">

<tmpl_var form.username>

</td>

</tr>

<tr>

<td class="formDescription" valign="top">

<tmpl_var form.password.label>

</td>

<td class="tableData">

<tmpl_var form.password>

</td>

</tr>

<tr>

<td class="formDescription" valign="top">

<tmpl_var form.passConfirm.label>

</td>

<td class="tableData">

<tmpl_var form.passConfirm>

</td>

</tr>

<tmpl_loop create.form.profile>

<tr>

<td class="formDescription" valign="top">

<tmpl_var profile.formElement.label>

</td>

<td class="tableData">

<tmpl_var profile.formElement>

</td>

</tr>

</tmpl_loop>

<tr>

<td colspan="2">&nbsp;</td>

</tr>

<tr>

<td class="submitData" colspan="2">

<tmpl_var create.form.submit>

</td>

</tr>

</table>

<tmpl_var create.form.footer>

|;

#Login Template Code

my $loginTmpl = q|

<h2><tmpl_var title></h2>

<tmpl_if login.message>

<tmpl_var login.message>

</tmpl_if>

<tmpl_var login.form.header>

<tmpl_var login.form.hidden>

<table >

<tr>

<td class="formDescription" valign="top">

<tmpl_var login.form.username.label>

</td>

<td class="tableData">

<tmpl_var login.form.username>

</td>

</tr>

<tr>

<td class="formDescription" valign="top">

<tmpl_var login.form.password.label>

</td>

<td class="tableData">

<tmpl_var login.form.password>

</td>

</tr>

<tr>

<td class="formDescription" valign="top"></td>

<td class="tableData">

<tmpl_var login.form.submit>

</td>

</tr>

</table>

<tmpl_var login.form.footer>



<div class="accountOptions">

<ul>

<li>

<a href="<tmpl_var recoverPassword.url>">

<tmpl_var recoverPassword.label>

</a>

</li>

<tmpl_if anonymousRegistration.isAllowed>

<li>

<a href="<tmpl_var createAccount.url>">

<tmpl_var createAccount.label>

</a>

</li>

</tmpl_if>

</ul>

</div>

|;

#Add the templates to the folder

$newFolder->addChild({

className =>"WebGUI::Asset::Template",

ownerUserId =>'3',

groupIdView =>'7',

groupIdEdit =>'12',

title =>"IP Auth Account Template",

menuTitle =>"IP Auth Account Template",

url =>"ip_auth_account",

namespace =>"Auth/IP/Account",

template =>$accountTmpl,

}, 'IPtmpl0000000000000001'

);

$newFolder->addChild({

className =>"WebGUI::Asset::Template",

ownerUserId =>'3',

groupIdView =>'7',

groupIdEdit =>'12',

title =>"IP Auth Create Account",

menuTitle =>"IP Auth Create Account",

url =>"ip_auth_create",

namespace =>"Auth/IP/Create",

template =>$createAcctTmpl,

},'IPtmpl0000000000000002'

);



$newFolder->addChild({

className=>"WebGUI::Asset::Template",

ownerUserId=>'3',

groupIdView=>'7',

groupIdEdit=>'12',

title=>"IP Auth Login",

menuTitle=>"IP Auth Login",

url=>"ip_auth_login",

namespace=>"Auth/IP/Login",

template=>$loginTmpl,

},'IPtmpl0000000000000003'

);

#Commit the version tag

my $workingVersionId = WebGUI::VersionTag->getWorking($session)->getId;

my $tag = WebGUI::VersionTag->new($session,$workingVersionId);

if (defined $tag) {

print "Committing tag\n";

$tag->set({comments=>"IP Auth Install"});

$tag->requestCommit;

}

#Close the WebGUI Session and finish

$session->var->end;

$session->close;

print "Done. Please restart Apache.\n";

}

 

Begin by accepting the config file and home directory as arguments from the command line. If no home directory is supplied, assume it's the default home directory for a WebGUI install. Then, create a WebGUI session using the WebGUI::Session API. Once you have a WebGUI session open, you can start using all of the WebGUI API's to install your authentication plugin. Use the WebGUI::Session::Config API to add your plugin to the config file. Then, use the WebGUI::User API to create your IP Visitor User. You then use the WebGUI::Session::Setting API to add your local authentication settings to the database with default values including the userId of the IP Visitor User you just created.



Next, get the import node with the WebGUI::Asset API and add a folder to it by calling the addChild method. Proceed to add the templates to the folder in this same manner.

 

Finally, get the current working version tag and commit it so your templates are fully installed and ready for use. Close the session, and return a message to the user letting him/her know that he/she should restart Apache as you have updated WebGUI's configuration file.

 

Now that you have an automated installation method, you should also automate uninstalling your authentication plugin. The steps needed include:

 

  1. Remove all your custom settings from the database.

  2. Remove all your templates from the system.

  3. Remove the folder you created from the system.

  4. Remove your plugin from the configuration file.

  5. Remove the ipvisitor user.

  6. Handle all of the users that are currently using your IP auth module

 

To do all of this manually could be quite a lengthy job if you had thousands of users on your site using the IP method. However, you can automate all of this, and add a command line script similar to your installation file.

 

sub uninstall {

#First argument on the command line is the config file

my $config = $ARGV[0];

#Second argument on the command line is WebGUI's home dir

#If no second argument exists, use the default home dir

my $home = $ARGV[1] || "/data/WebGUI";

#Die if either $home or $config is empty

unless ($home && $config) {

die "usage: Perl -MWebGUI::Auth::IP -e uninstall www.example.com.conf\n";

}

#Print out a status message to indicate the uninstall is starting

print "Uninstalling asset.\n";

#Open a new WebGUI session for the config file passed in

my $session = WebGUI::Session->open($home, $config);

#Remove the authentication method from the config file

$session->config->deleteFromArray("authMethods","IP");

#Delete all of the authentication settings

$session->setting->remove("ipIpvisitorId");

$session->setting->remove("ipAllowedIPAddress");

$session->setting->remove("ipAccountTemplateId");

$session->setting->remove("ipCreateAccountTemplateId");

$session->setting->remove("ipLoginTemplateId");

#Reset the default IP setting if it is set to ours

if($session->setting->get("authMethod") eq "IP") {

$session->setting->set("authMethod","WebGUI");

}

#Get all the users that are using our authentication method

my $sql = q|

SELECT

userId

FROM

users

WHERE

authMethod = 'IP'

|;

my @users = $session->db->buildArray($sql);

#Delete all of the users using our authentication method

foreach my $userId (@users) {

my $user = WebGUI::User->new($session,$userId);

if(defined $user) {

$user->delete

}

}

#Clean up any of the remaining IP fields in the authentication table

$sql = q|

DELETE

FROM

authentication

WHERE

authMethod='IP'

|;

$session->db->write($sql);

#Get all of the templates that are in our namespace

$sql = q|

SELECT

assetId

FROM

template

WHERE

namespace IN ('Auth/IP/Create','Auth/IP/Account','Auth/IP/Login')

|;

my @assets = $session->db->buildArray($sql);

#Push the assetId of the folder onto the asset array

push(@assets,"IPAuthFolder0000000001");

#Purge all of the templates and the folder

foreach my $assetId (@assets) {

#Instantiate the asset

my $asset = WebGUI::Asset->newByDynamicClass($session,$assetId);

#Purge the template if it is defined

if(defined $asset) {

$asset->purge;

}

}

#Close the session and finish

$session->var->end;

$session->close;

print "Done. Please restart Apache.\n";

}

 

Begin by accepting the config file and home directory as arguments from the command line and creating the WebGUI session as you did in your install method. Then, use the WebGUI::Session::Config API to remove your plugin from the config file. Use the WebGUI::Session::Setting API to remove your local authentication settings from the database and make sure that the default authMethod setting is not your authentication method. If it is, set it back to the WebGUI authentication method.


Next, find all the users that currently use your IP auth method, which includes your IP Visitor user. If you were so inclined at this point, you could change all of the users to use a different authMethod, but for the sake of this example simply delete all of these users with the WebGUI::User API. Once you've done this, clean up any fields that might still exist in the database that reference your authMethod (users that may have once used your auth method, but no longer do, might still have properties configured).



Now, find all of the templates in your namespace from the template table, push on the assetId of the folder from the install script, and proceed to purge your templates and folders.

 

Once this is complete, your asset is uninstalled. Close the WebGUI session and alert the user that Apache must be restarted in order for the changes to take affect. You now know everything you need to create powerful authentication plugins to run your WebGUI site as you see fit.

Keywords: access authentication ip address ldap login logout permissions plugins privileges security template user account users

Search | Most Popular | Recent Changes | Wiki Home
© 2018 Plain Black Corporation | All Rights Reserved