Seam Identity Management

Home/Seam, Security/Seam Identity Management

During a recent coding getaway to Maine (see my post on the 2011 HackFestaThon) I decided to write a basic Seam project as a starting point for my future Seam based web applications.  The idea is to provide common features such as Login, Logout, Registration, Forgot Password, User Management, Audit Logging, Image Upload Handling, Video Upload Handling, etc… so next time I have an idea that I want to hack together I won’t have to re-write or copy-paste in basic functionality like that.

I spent about a day working on things before I discovered that I really should be using the Seam framework’s Identity Management feature.  So I threw out everything I’d done, and started by re-reading the docs, and went from there.  Seam’s Identity Management framework is VERY powerful, but is also a little complicated to get going and in many cases it seems like it would easier to just write stuff from scratch.  I’m banking on the powerful stuff being worth the initial learning curve and a little extra pain.

When I get the starter project in a more complete state I will be open sourcing the whole thing to help others along, but I wanted to share a few things I’ve learned so far:

In order to use the Email address as the login instead of a username, you need to remove the username property from your UserAccount entity and annotate the Email address property like so:

@NotNull
@UserPrincipal
@Email
public String getEmail() {
    return email;
}

Actions like Registration need a RunAsOperation inner class to handle the fine grained security controls that the Identity Management framework enforces:

    public void register() {
	verified = (confirm != null && confirm.equals(password));

	if (!verified) {
	    FacesMessages.instance().addToControl("confirmPassword", "Passwords do not match");
	}
	new RunAsOperation() {
	    public void execute() {
		try {
		    // Check if email address has already been used
		    if (identityManager.userExists(getEmail())) {
			FacesMessages.instance().addToControl("email", "Email has already been used.");
			return;
		    }
		    identityManager.createUser(email, password, mFirstName, mLastName);
		} catch (IdentityManagementException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
		identityManager.grantRole(email, "member");
	    }
	}.addRole("admin").run();

	// Login the user
	identity.getCredentials().setUsername(email);
	identity.getCredentials().setPassword(password);
	identity.login();
    }

Populating custom properties on the User during things like registration requires observing events:

    @Observer(JpaIdentityStore.EVENT_PRE_PERSIST_USER)
    public void prePersistUser(UserAccount pNewUser) {
	// Setup additional UserAccount properties before the user is created
	pNewUser.setRegistrationDate(new Date());
	pNewUser.setOptIn(isOptIn());
    }

You can log audit events with the user’s IP address by doing things like this:

@Scope(ScopeType.EVENT)
@Name("userEvents")
public class UserEvents {
    @Logger
    private Log mLog;

    @Observer(JpaIdentityStore.EVENT_USER_AUTHENTICATED)
    public void loginSuccessful(UserAccount pUser) {
	mLog.info("User logged in with email: #0", pUser.getEmail());
	pUser.setLastLoginDate(new Date());
	Contexts.getSessionContext().set("currentUser", pUser);
	AuditEvent loginEvent = new AuditEvent(((ServletRequest) FacesContext.getCurrentInstance().getExternalContext()
		.getRequest()).getRemoteAddr(), pUser.getId(), "Login Success", null);
	Events.instance().raiseEvent("auditEvent", loginEvent);
    }
}

Hopefully I’ll have the starter project ready soon and will share it with you all. In the meantime, happy hacking!

By | 2017-05-18T15:15:48+00:00 July 11th, 2011|Seam, Security|3 Comments

About the Author:

3 Comments

  1. M M Islam Chisty September 11, 2011 at 9:47 pm - Reply

    A simple question. Please consider the following scenario:

    – OSR is a government organization where there are 50 roles (e.g. Manager, Operations manager, Director, Secured developer, Chief operator, Systems director, ….. etc etc etc etc etc). A web based application is developed for OSR using Seam2.2, richfaces/JSF, JBoss5.1.

    – In this web application, consider a XHTML page which had 2 buttons (“Save” and “Delete”). The “Save” button will be viewable (rendered=”true”) to 25 roles and the “Delete” button will visible to another 25 roles. In Seam2 (with Richfaces), one way to accomplish it is like this:

    Similarly, for the “Delete” button, we do this conditional check for rendering for another 25 roles and for 25 times. In case of 100s of pages with different role based conditions for different UI components (buttons, hyperlinks, textfields, checkboxes, texareas, radio buttons), this will be a complete mess. Can you see the problem? Is this a good programming practice?

    My question is: is there a better way to do this? JBoss drools/rules does not seem to help much in this case. Does seam3 had some better option to provide in such cases?

    Thanks,
    … Chisty

    • Devon September 22, 2011 at 8:37 pm - Reply

      Chisty,

      you need to use permissions and assign groups of permissions to roles. Then use the s:hasPermission call in the rendered= attribute to manage things like Save and Delete button access.

      Read sections: 15.6.1, 15.6.2, and 15.6.3 from the Seam 2 docs.

      regards,

      Devon

  2. M M Islam Chisty September 11, 2011 at 9:47 pm - Reply

    Why does it removed all the codes that I included in my comments ???

Leave A Comment