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!