The Security class defines the logic used to authenticate the users of your application and to give them appropriate authorizations, and can implement additional operations such as self-registration and password reset. The class is defined in a file located in the Groovy classpath root (/WEB-INF/groovy/Security.groovy):
Fig. 1: the location of Security.groovy in a Portofino application. Mind the capital S!
The Security class must implement a certain API. By providing your own implementation of its methods, you will be able to handle authentication and authorization in a very flexible way: be it with database queries over a custom schema, LDAP/Active Directory, Single Sign-On, or any other method. The Security class cooperates with the login page (i.e., LoginAction and the associated JSP pages).
The default application comes with a minimal Security.groovy that only knows about hardcoded admin/admin and guest/guest users. However, if you create a new application using the wizard and specify the database tables containing users and roles, a sample Security.groovy file is created, replacing the default one. You can use it as a starting point, but you should customize it before deploying your application in production. In particular, be aware that the generated Security.groovy will still allow users to log in as admin/admin! This is to avoid you accidentally locking out of the application because none of the users on the database have administration privileges.
The Security.groovy API
The Security class is an Apache Shiro Realm implementation, and it also acts as a limited DAO over objects representing users of the system. It must implement the interface com.manydesigns.portofino.shiro.PortofinoRealm. In most cases, though, you should extend the class com.manydesigns.portofino.shiro.AbstractPortofinoRealm that relieves you from implementing some boilerplate. AbstractPortofinoRealm still forces you to implement certain methods, while for others it provides empty defaults that you can optionally override.
You can look at the Javadocs to get an overview of PortofinoRealm. You can also observe a real-world Security.groovy in the demo-tt application.
Let's look at the API breaking it down by functionality. Unless otherwise specified, if you extend AbstractPortofinoRealm it is not mandatory to implement the described methods.
Fig. 2: a few lines of code taken from a sample Security.groovy
Authentication and authorization
The methods involved in authentication and authorization are:
AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)(mandatory) - this method directly pertains to the Apache Shiro Realm API. It performs authentication with credentials coming from a certain token. By default, this will be a UsernamePasswordToken - the standard authentication in Portofino is based on username and password; however, depending on the features you decide to enable, you could also receive a PasswordResetToken, a SignUpToken, or a ServletContainerSecurityToken, as well as any custom token if you use a customized login action. After running your logic (e.g. to issue a database query), the outcome of the method can be one of the following:
- an AuthenticationInfo object is returned (generally, an instance of SimpleAuthenticationInfo; refer to the Apache Shiro documentation). That object holds the user's identity or identities. In a typical configuration, in Portofino the first and only identity is the user object (a map) loaded from the database. This is what the wizard generates.
- an AuthenticationException is thrown. This means that the login procedure failed. You can throw a more specific subclass of AuthenticationException to signal particular conditions (e.g. wrong password, username not known, etc.), although the standard Portofino login page will never show that information to the user, for security reasons.
Collection<String> loadAuthorizationInfo(Serializable principal)- returns the list of groups associated to the user identified by the provided principal (identity; see above). This method is called when checking the permissions of an authenticated user. There are a few built-in groups that are already handled by AbstractPortofinoRealm, so you only have to override this method if you need to handle application-specific groups.
Fig. 3: the lists of users and groups are shown in the permissions page.
These methods expose to Portofino some details of the users and groups of your application; see the Javadocs for their description:
Map<Serializable, String> getUsers()(mandatory)
Serializable getUserById(String encodedUserId)
Serializable getUserByEmail(String email)
String getUserPrettyName(Serializable user)
Serializable getUserId(Serializable user) (mandatory)
These methods (all optional) implement parts of advanced functionality such as password reset and self-registration; see the Javadocs for their description:
void verifyUser(Serializable user)
void changePassword(Serializable user, String oldPassword, String newPassword) throws IncorrectCredentialsException
String generateOneTimeToken(Serializable user)
String saveSelfRegisteredUser(Object user) throws RegistrationException