How to Use Unified Login
Draft 0.1
Nov 15, 2000
Ted Lee (tlee@startups.com)
1. Applicability
Read this document when any of the following are true:
-
You want to make sure a Startups.com user has logged in before accessing
a page in the site.
-
You want to make sure a Startups.com user has permission to access a page
in the site.
If this document doesn't meet your needs, you may want to look at:
Background
There are certain sections of our site which require a user to log in prior
to accessing the page. In order to log in, a user must provide a
username and a password. Users may obtain usernames and passwords
in several areas on the site. For instance, it is possible to sign
up as a registered client to obtain a username and password, or one can
sign up as a service provider to obtain a username and password.
It is undesirable to require a user to obtain separate usernames and passwords
to access each section of our site, so instead we introduce the concept
of roles.
A user role is added to a user when the user signs up for a particular
section of the site. For example, a new user signing up as a client
will obtain a username, a password, and internally will be assigned a client
role. Roles may be added to existing username/password pairs if the
user signs up for other sections of the site. For example, a user
who is already a client may sign up as a service provider; in this case,
the user would have a single username and password but would have two roles
assigned to her--client role and service provider role.
Certain sections of the site require the user have a particular role
in addition to being logged in. Some pages require not only the presence
of a particular role but also the presence of certain permissions within
that role.
The desired effect of using unified login is that when a user tries
to access a page which requires login, a login screen will appear.
If the login is successful and the user has the appropriate roles and permissions
required for that page, the user will be able to access the page.
2. Steps for Using Unified Login
[warning, the API described here does not exist yet]
In order to require login to access a page, you need to override one
or more methods in the Model object. If you don't know what
a Model object is, please see How
To Create Web Pages With Forms (Java Developer). Model defines
a number of methods that can be usefully overridden to change access protection
behavior:
-
public Class getRequiredRole()
-
public String[] getRequiredPermissions()
-
public String getLoginPageMessage()
-
public String getLoginPageTitle()
-
public String getAccessSuccessfulURL()
-
public String getPermissionDeniedURL()
These methods will be explained in more detail below.
2.1 Specify what role is required to access a given page
Model defines a method called getRequiredRole() which
returns a Class object. By default it returns null.
Override this method to return the Class object for the role that
is required to access the page. For example, if JobBoardRole
is required to access the page, you would implement the method like this:
public Class getRequiredRole() {
return JobBoardRole.class;
}
Note that in order for the role to be checked, the user must be logged
in. In effect, specifying a required role always specifies that the
user must be logged in.
Some roles may contain additional permissions. Permissions are
simple strings, the presence of which in the role indicates that the permission
is allowed. In order to check that the role specified in getRequiredRole()
contains permissions, override getRequiredPermissions():
public String[] getRequiredPermissions() {
return new String[] {"ResourceLocatorAdmin"};
}
2.2 Specify the Login Page Text and Title
By default, a generic login page will appear if the framework determines
that the user does not have the required role. If you wish the login
page to say something specific, like "In order to access the Job Connection,
please log in", you must override the getLoginPageMessage() method.
You may also specify the title of the page by overriding getLoginPageTitle():
public String getLoginPageMessage() {
return "In order to access the Job Connection,
please log in.";
}
public String getLoginPageTitle() {
return "Log in to Job Connection";
}
2.3 Specify the success URL
If the user successfully logs in and has the required role, by default
the user is sent to the page they were originally trying to access.
In some cases, it may be desirable to send the user to a specific page
rather than the page they tried to access. For example, if the user
tries to enter step 3 of a 5 page application by typing in the URL for
page 3 in their browser, we might want to send them to step 1 regardless
of the fact that they were trying to go to step 3. For example,
public String getAccessSuccessfulURL() {
return "/jobboard/index.jsp";
}
2.4 Specify the permission denied URL
It is possible to have a user log in successfully but not have the role
required to access a page, or if they have the role they may not have the
required permission. In this situation, the user should be sent to
a different page. Typically it would be a signup page. For
example:
public String getPermissionDeniedURL() {
return "/jobboard/freeposting/SingleFreePosting.jsp";
}
By default, the behavior is to show a page that says something to the
effect of "Congratulations, you've successfully logged in. Unfortunately
you don't have access to the page you tried to access. Here are the
sections of the site that you have access to:" and it will list the
roles that the user currently has. In most situations this is not
the desired behavior, so it will usually be the case that the getPermissionDeniedURL()
should be overridden.
Tip:
If you have many pages with the same access protection, create an abstract
subclass of Model, override the methods discussed above, and extend
it for individual Model pages.
3. Design Discussion
The goal of the unified login framework is to support the following behavior:
-
Support login at a framework level so we don't have to code many different
login pages.
-
Be able to protect areas of the site so that if a user types in the URL
of a protected page they will be forced to log in before being able to
access it.
-
Be able to specify which roles are required to access a page on a page-by-page
basis.
-
Be able to specify the URL to send the user to upon successful login.
This may not be the page that the user attempted to access (for example,
if they tried to jump into the middle of a 3-page form it should send them
to the first page).
-
Be able to specify the URL to send the user to if a required role is missing
but the username and password match. This might be a signup page, for instance.
-
Be able to specify a message that will appear on the login page, so that
instructions or explanation can be given to the user (e.g. "In order to
access job connection, you need to first log in").
-
Not require the implementor to be aware of any http session user variables
We have chosen to make the implementors of Model objects (from the MVC
pattern) responsible for adding access permissions to the page models.
We could have made a centralized lookup table of permissions and pages,
but someone would have to maintain it and we would have needed a separate
maintenance application. It seems reasonable that the implementor
of a page and its logic should know what permissions are necessary to access
that page. The drawback is that changing the permissions for a page
requires a recompile.
Role object granularity
-
Multiple roles vs multiple permissions in a single role. Most of
the time it seems as though multiple permissions in a role is overkill
and confusing--when do you decide to make a permission vs another role?
However, one clear case where it seems that permissions would be useful
is in admin roles. We could go crazy with permissions hierarchies
but we chose to go with simple string permissions.
Known Problems
-
Current version of enterState() is inadequate. The enterState()
method defined in ControllerServlet is the logical place to check access
permissions. However, it just takes a URL as a parameter, so there
is no convenient way to get the model to check the permissions on.
The enterState() method should really accept another parameter,
the class of the model for the state to enter. Using this class,
the implementation of the enterState() method can check the permissions.
-
Fixing enterState() doesn't prevent a user from attempting to
reach a page by typing in a URL in the browser. The enterState()
method is only called from within a servlet. If someone types in
a URL of a jsp, they will be able to get to that page unless we put in
an include to that jsp which checks permissions. This isn't done
yet.
-
Nothing exists to integrate this stuff with Vivid's login code. One
place where this makes a difference is in the login/logout button at the
upper right of our current web page. Making this work properly requires
an understanding of Vivid's login code, which is somewhat involved
to say the least. It will not only involve code, but probably some
data conversion. For instance, we'll have to add ServiceProviderRoles
to all of our existing customers who have this role in the Vivid code if
we don't want them getting permissions problems on new pages in service
provider sections of our site.
4 Version History
0.1 First Version