Status |
Proposal under development |
Target Release |
Roller Weblogger 4.1 |
Original Authors |
Dave Johnson |
Abstract
This is a proposal to make it possible to externalize user management so that Roller can pull user profile and role information from a separate user management system.
Background
Here's an explanation of Roller's current user/role management, the perceived problems and proposed solutions.
Roller manages it's own users and roles
So that it can stand-alone without an external user repository, Roller stores users and role information in it's own database tables.
create table rolleruser ( id varchar(48) not null primary key, username varchar(255) not null, passphrase varchar(255) not null, screenname varchar(255) not null, fullname varchar(255) not null, emailaddress varchar(255) not null, activationcode varchar(48), datecreated timestamp not null, locale varchar(20), timezone varchar(50), isenabled boolean not null ) create table userrole ( id varchar(48) not null primary key, rolename varchar(255) not null, username varchar(255) not null, userid varchar(48) not null )
There are two distinct roles:
- editor: user able to login and see main menu
- admin: user is able edit site-wide settings and any blog in system
Those tables are represented in Roller by the User and UserRole objects, simple POJO objects stored by Roller's ORM based UserManager.
The problem
For sites that have an external user repository or user permissions system, this is a problem. Some would like user information like email address, locale, timezone, fullname and etc. pulled from an external system. Some would like user's roles to be pulled from an external system. Some would like both.
The solution
Reduce the User class down to just two fields, id and username. Everything else goes into a new UserProfile class. UserProfiles can be stored externally so Roller should obtain them through a new User Respotory API
User Respository API, part 1/2 UserRespository interface methods public UserProfile getUserProfile(String userid) public void saveUserProfile(UserProfile userProfile) UserProfile bean properties username password screenName emailAddress locale timezone biography etc.
Roller authentication is manages via Acegi
Roller uses a framework called Acegi to handle authentication and authorization. Instead of relying on the authentication and authorization features built into the container on which Roller runs, Roller relies on Acegi.
When Acegi is authenticating a user it pulls username, password and role information the RollerUserDetailsService, which in turn fetches that information from the User and UserRole objects via UserManager.
Acegi is implemented as a Servlet Filter, which intercepts each request and decides if the user is authenticated, needs to login first, etc. Acegi takes care of routing the user to the login page and back to the original page that the user requested. Acegi wraps the ServletRequest so that it can return the
right value when request.getUserPrincipal()
is called by the application.
The problem
Using Acegi makes Roller installation painless in standalone situations, but some Acegi skills are required to reconfigure Roller to authenticate against an LDAP system. And the only SSO system that Acegi supports out of the box is Yale CAS. And some folks would like to disable Acegi to take advantage of the auth & auth services that are built into containers now, e.g. SSO support that's built into Websphere, Glassfish, etc.
The Solution
Make it possible to turn off Acegi by modifying web.xml and to extend the Roller application class RollerContext to init without Acegi. Moving forward, we should not introduce further dependencies on Acegi in Roller.
Acegi manages URI authorization
These rules in Acegi's configuration (security.xml) file govern URI based
authorization used in Roller.
/roller-ui/login-redirect**=admin,editor /roller-ui/profile**=admin,editor /roller-ui/createWeblog**=admin,editor /roller-ui/menu**=admin,editor /roller-ui/authoring/**=admin,editor /roller-ui/admin/**=admin /rewrite-status*=admin
The Problem? There's no problem here. When operating without Acegi, Roller will
have to be configured with a web.xml file that specifies those contraints.
Roller manages all other authorization involving user roles
Roller also does it's own authorization checks on user Roles.
For each new user session, Roller creates a RollerSession object. RollerSession
calls request.getUserPrincipal().getName() to get the user name, fetches
corresponing User object from UserManager and holds on to that User object.
The RollerSession provides access to the session's authenticated user:
public User getAuthenticatedUser()
The User object provides read/write access to user's roles:
public boolean hasRole(String roleName) public void grantRole(String roleName) public Set getRoles()
As of Roller 4.0, Roller calls hasRole() for one reason, to ensure that only
those with the admin role can:
- View set the Admin options in the tabbed menu
- Modify another user's profile
- Edit any weblog
- Set pinned field of a weblog entry
- Use RAP web services
The Problem
Because Roller uses the ORM system to load a User's Roles, the roles must come from the database. And because Roles are part of the User, some sort of join must happen to load each User object with Roles.
The Solution
Instead of relying on ORM supported role methods in the user object, Roller front-end code should call the user manager:
UserManager interface methods
public boolean isUserInRole(String username) public Set<String> getUserRoles(String username) public void revokeRole(String userid, String rolename) public void grantRole(String userid, String rolename)
And instead of calling role-related methods on the user object, Roller code should use either request.isUserInRole() or the isUserInRole() method provided by the UserManager.
Our UserManager implemenation will in turn call the User Repository API.
User Respository API, part 2/3
UserRespository interface methods
public boolean isUserInRole(String username) public Set<String> getUserRoles(String username) public void revokeRole(String userid, String rolename) public void grantRole(String userid, String rolename)
Roller will include a User Repository API that stores data in the Roller database. Other implementations can be plugged in via DI.
Requirements
Requirements satisfied by this proposal
Issues
Issues to be considered
Design
List and describe new manager methods, Struts actions, JSP pages, macros, etc.
Comments
Other can leave commments here.