I've created an umbrella issue to track the work we do on the security module [1], and the first task is to review and discuss the Identity API. This API forms the core of the security module, and all other features build on top of it to implement a complete security framework.

The Identity interface represents an individual, current user of an application and its implementation must be session-scoped to provide security services for the entirety of a user's session. I'm going to paste the (slightly modified) code from Seam as it's mostly well documented, and so we have a baseline from which to further discuss:

public interface Identity {
public static final String RESPONSE_LOGIN_SUCCESS = "success";
public static final String RESPONSE_LOGIN_FAILED = "failed";
public static final String RESPONSE_LOGIN_EXCEPTION = "exception";
/**
* Simple check that returns true if the user is logged in, without attempting to authenticate
*
* @return true if the user is logged in
*/
@Secures
@LoggedIn
boolean isLoggedIn();
/**
* Returns true if the currently authenticated user has provided their correct credentials
* within the verification window configured by the application.
*
* @return
*/
boolean isVerified();
/**
* Will attempt to authenticate quietly if the user's credentials are set and they haven't * authenticated already. A quiet authentication doesn't throw any exceptions if authentication
* fails.
*
* @return true if the user is logged in, false otherwise
*/
boolean tryLogin();
/**
* Returns the currently authenticated user
*
* @return
*/
User getUser();

/**
* Attempts to authenticate the user. This method raises the following events in response * to whether authentication is successful or not. The following events may be raised
* during the call to login():
* <p/>
* org.apache.deltaspike.security.events.LoggedInEvent - raised when authentication is successful * org.apache.deltaspike.security.events.LoginFailedEvent - raised when authentication fails * org.apache.deltaspike.security.events.AlreadyLoggedInEvent - raised if the user is already authenticated
*
* @return String returns RESPONSE_LOGIN_SUCCESS if user is authenticated,
* RESPONSE_LOGIN_FAILED if authentication failed, or
* RESPONSE_LOGIN_EXCEPTION if an exception occurred during authentication. These response * codes may be used to control user navigation. For deferred authentication methods, such as Open ID * the login() method will return an immediate result of RESPONSE_LOGIN_FAILED (and subsequently fire * a LoginFailedEvent) however in these conditions it is the responsibility of the Authenticator * implementation to take over the authentication process, for example by redirecting the user to
* another authentication service.
*
*/
String login();
/**
* Attempts a quiet login, suppressing any login exceptions and not creating
* any faces messages. This method is intended to be used primarily as an
* internal API call, however has been made public for convenience.
*/
void quietLogin();
/**
* Logs out the currently authenticated user
*/
void logout();
/**
* Checks if the authenticated user is a member of the specified role.
*
* @param role String The name of the role to check * @param group String the name of the group in which the role exists
* @return boolean True if the user is a member of the specified role
*/
boolean hasRole(String role, String group);
/**
* Adds a role to the authenticated user. If the user is not logged in,
* the role will be added to a list of roles that will be granted to the
* user upon successful authentication, but only during the authentication
* process.
*
* @param role The name of the role to add * @param group The name of the group in which to create the role
*/
boolean addRole(String role, String group);
/**
* Checks if the authenticated user is a member of the specified group
*
* @param name The name of the group
* @return true if the user is a member of the group
*/
boolean inGroup(String name);
/**
* Adds the user to the specified group. See hasRole() for semantics in
* relationship to the authenticated status of the user.
*
* @param name The name of the group
* @return true if the group was successfully added
*/
boolean addGroup(String name);
/**
* Removes the currently authenticated user from the specified group
*
* @param name The name of the group
*/
void removeGroup(String name);
/**
* Removes a role from the authenticated user
*
* @param role The name of the role to remove
*/
void removeRole(String role, String group);
/**
* Checks that the current authenticated user is a member of
* the specified role.
*
* @param role String The name of the role to check
* @throws AuthorizationException if the authenticated user is not a member of the role
*/
void checkRole(String role, String group);
/**
* @param group
* @param groupType
*/
void checkGroup(String group);
    /**
* Returns an immutable set containing all the current user's granted roles
*
* @return
*/
Set<Role> getRoles();
/**
* Returns an immutable set containing all the current user's group memberships
*
* @return
*/
Set<Group> getGroups();
}


Some particular points to review:

1. Should we attempt to use the security classes provided by Java SE, such as Principal, Subject, etc or use our own User API - this will affect what is returned by the getUser() method above. Keep in note that we will have at least a simple User/Role/Group API as part of Identity Management. In Seam 2 we originally used the built-in Java classes (which made more sense because the authentication process was based on JAAS), however in Seam 3 (where we removed JAAS because it doesn't support asynchronous authentication as required by OpenID etc) we based the security module on the PicketLink User API. IMHO, this is not a critical choice either way - the Java security classes have the advantage of being familiar to many users, while on the flipside if we provide our own User API we have the flexibility of being able to extend it in the future. So both options have their own advantages.

2. The addRole() and addGroup() methods are intended to be only used during the authentication process to grant particular user memberships for the duration of their session only. A few users have found this a little confusing, as they were using identity management, and expected these methods to grant a permanent membership for the user. One solution may be to simply rename these methods to addSessionRole() and addSessionGroup() - thoughts?

3. We're touching a little bit on the authorization API here also with the hasRole() / inGroup() methods. I'll provide a quick description of these core security API concepts here:

* User - represents an individual user of an application. Can either be human or non-human, and can represent either a user managed locally (i.e. through the IDM API) or an externally authenticated User, such as one that has logged in with OpenID. * Group - a collection of users and other groups. The intent is that privileges can be either assigned to individual users, groups or roles. Groups have a hierarchical structure and can be a member of zero or more other groups. * Role - represent a particular real life role of a user. Roles are defined as a three-way relationship between user, group and role type. For example, user "Bob" might be an "accounts clerk" (the role type) at "head office" (the group). It is also possible for a user to have a role in a group, without being a member of that group.



[1] https://issues.apache.org/jira/browse/DELTASPIKE-76
[2] https://github.com/seam/security/blob/develop/api/src/main/java/org/jboss/seam/security/Identity.java

Reply via email to