Extending user service
The IBM® WebSphere® sMash user service leverages the Global Context to make it easy to add your own user service implementation.
Configuring a custom user registry
The following example shows a configuration file to register your user service implementation:# current defaults are file and ldap /config/security/userservice/registryType="database" # must implement zero.core.security.userservice.UserService /config/security/userservice/registryImpl/database="zero.example.DatabaseUserService"
registryType and registryImpl/<registryType>. This provides the mapping between the registryType and the actual implementation class.
Customizing the user registry implementation
To demonstrate how this could be implemented, the code forDatabaseUserService has been provided in the following example.
package zero.example;
import java.security.Principal;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import zero.core.security.GroupImpl;
import zero.core.security.PrincipalImpl;
import zero.core.security.userservice.UserService;
import zero.core.security.userservice.UserServiceException;
import zero.data.Manager;
public class DatabaseUserService implements UserService {
private static final String CLASS_NAME =
DatabaseUserService.class.getCanonicalName();
private static final Logger logger = Logger.getLogger(CLASS_NAME);
public Set<Principal> getUser(String username) throws UserServiceException {
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLASS_NAME, "getUser","username ->" + username);
}
if(username == null || username.trim() == "") {
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, "login",
"user name is null or empty, "+
"returning empty set of principals");
}
return new HashSet<Principal>();
}
Manager authDB = Manager.create("authenticationDB");
if(authDB == null) {
throw new UserServiceException("failed to create DB manager");
}
List<Map<String, Object>> results =
authDB.queryList("SELECT * FROM users "+
"WHERE username = ?", username);
if ( results != null && (!results.isEmpty())) {
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, "getUser",
"returning set of principals");
}
return getPrincipal (username);
}
else {
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, "getUser",
"returning empty set of principals");
}
return new HashSet<Principal>();
}
}
public Set<Principal> login(String username, String passwd)
throws UserServiceException {
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLASS_NAME,
"login","username ->" + username);
}
if(username == null || username.trim() == "") {
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, "login",
"user name is null or empty, "+
"returning empty set of principals");
}
return new HashSet<Principal>();
}
Manager authDB = Manager.create("authenticationDB");
if(authDB == null) {
throw new UserServiceException("failed to create DB manager");
}
List<Map<String, Object>> results =
authDB.queryList("SELECT * FROM users WHERE " + "" +
"username = ? AND password = ?",
username, passwd);
if ( results != null && (!results.isEmpty())) {
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, "login",
"returning set of principals");
}
return getPrincipal (username);
}
else {
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, "login",
"returning empty set of principals");
}
return new HashSet<Principal>();
}
}
private Set<Principal> getPrincipal(String username)
throws UserServiceException{
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLASS_NAME, "getPrincipal","username ->" + username);
}
// add user information for principal
Set<Principal> userSet = new HashSet<Principal>();
Principal user = new PrincipalImpl(username);
userSet.add(user);
// get group information for user
Manager authDB = Manager.create("authenticationDB");
if(authDB == null) {
throw new UserServiceException("failed to create DB manager");
}
List<Map<String, Object>> results =
authDB.queryList("SELECT * FROM usertogroupmapping " +
"WHERE username = ?", username);
Set<Principal> groups = new TreeSet<Principal>();
for (Map item : results) {
String groupName = (String) item.get ("groups");
groups.add((new GroupImpl (groupName)));
}
userSet.addAll(groups);
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, "getPrincipal",
"primcipal ->" + userSet);
}
return userSet;
}
}
DatabaseUserService class provides a database user registry that contains user names, passwords and groups.
Using the data access extension
To leverage the data access extension that is included as part of WebSphere sMash using a MySQL database, some additional configuration is required for theDatabaseUserService registry example. The configuration stanza for this example is shown in the following example.
/config/db/authenticationDB = {
"class" : "com.mysql.jdbc.jdbc2.optional.MysqlDataSource",
"serverName" : "localhost",
"portNumber" : 3306,
"databaseName" : "authentication",
"user" : "root",
"password" : "root"
}
Validating the DatabaseUserService registry
The authentication database contains the following tables:-
users - Contains the list of username and passwords for each user.
-
usertogroupmapping - Contains the list of groups each user belongs to.
UserService has validated the user name and password matches that were passed to the login method, a subsequent database call is made against the usertogroupmapping table to obtain the list of groups this user belongs to.
The following example data can be used to validate the DatabaseUserService.
Current database: authentication mysql> select * from users; +----------+----------+ | username | password | +----------+----------+ | user2 | pass2 | | user1 | pass1 | +----------+----------+ 2 rows in set (0.20 sec) mysql> select * from usertogroupmapping; +----------+--------+ | username | groups | +----------+--------+ | user1 | group1 | | user1 | group2 | | user1 | group3 | | user2 | group3 | +----------+--------+ 4 rows in set (0.00 sec)
Securing DatabaseUserService registry resources
Securing resources is independent of the UserRegistry. For this example, the following stanza was used.
#Authorization rules
@include "security/rule.config"{
"conditions": "/request/path =~ /basicauth/customers(/.*)?",
"authType" : "Basic",
"groups" : ["group1"]
}