Shiro Authorization populate authorization with remote roles
java, shiro, tapestry
Solution
Extending `AuthorizingRealm` is a good place to start if you need both authentication and authorization. Also, as PepperBob has already said, while you're at it, the `Account` interface and its `SimpleAccount` implementation support both authentication and authorization in a single interface, so you don't need much separate code for `doGetAuthenticationInfo()` and `doGetAuthorizationInfo()` and can just return the same object from both methods.
To get the authorization information, you need to do two things:
- Get an available principal from the principal collection passed as a parameter (which, in most cases, just contains one principal anyway) via the `getAvailablePrincipal()` method (neatly predefined in `AuthorizingRealm`).
- Load your roles and pass them to `setRoles()` on your account object.
...and you're done.
Edited to add:
This would be a very simple way to store the roles until you need them. Note that the actual authentication is done in the realm, which has a dependency on `RemoteLoginClient`.
public class MyRealm extends AuthorizingRealm {
private RemoteLoginClient client = ...;
private final Map<String, Set<String>> emailToRoles = new ConcurrentHashMap<>();
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) {
final UsernamePasswordToken userPass = (UsernamePasswordToken) token;
final RemoteSubject authenticate = this.client.authenticate(
userPass.getUserName(), userPass.getPassword());
if (authenticate != null) { //assuming this means success
this.emailToRoles.put(userPass.getUserName(), authenticate.getRoles());
return new SimpleAuthenticationInfo(...);
} else {
return null;
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
final String username = (String) principals.getPrimaryPrincipal();
final Set<String> roles = this.emailToRoles.get(username);
return new SimpleAuthorizationInfo(roles);
}
}
Problem
I'm using Tapestry-Security which uses Apache Shiro I have a custom realm which handles authorization and authentication. Our authentication technically happens using a remote service, which returns a username and a set of roles. I just pass the username into my custom AuthenticationToken which allows me to query our local db and set the SimpleAuthenticationInfo. I can't figure out how to populate the AuthorizationInfo doGetAuthorizationInfo method using the list of roles returned to me from our remote service. Below is the code I'm using to populate the realm. Login.class ``` //Remote authentication service RemoteLoginClient client = new RemoteLoginClient(); RemoteSubject authenticate = client.authenticate(username, password); //tapestry security authentication Subject currentUser = SecurityUtils.getSubject(); CustomAuthenticationToken token = new CustomAuthenticationToken(authenticate.getUsername()); System.out.println("roles" + authenticate.getRoles()); currentUser.login(token); ``` AuthorizationInfo method inside customRealm public class CustomRealm extends AuthorizingRealm { ``` protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { CustomAuthenticationToken upToken = (CustomAuthenticationToken ) token; String email = upToken.getUsername(); ApplicationUser applicationUser = (ApplicationUser) session.createCriteria(ApplicationUser.class) .add(Restrictions.like("email", email + "%")) .uniqueResult(); if (applicationUser == null) { throw new UnknownAccountException("User doesn't exist in EPRS database"); } return buildAuthenticationInfo(applicationUser.getId()); } protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //Not sure how to populate the principle or //read the principle to populate the SimpleAuthorizationInfo return new SimpleAuthorizationInfo(roleNames); } ```