Skip to content

Instantly share code, notes, and snippets.

@consh004
Forked from davidmc24/AbstractWaffleRealm.java
Created January 3, 2018 15:47
Show Gist options
  • Save consh004/5c1aded50dad9b3494bef3cf2584cd10 to your computer and use it in GitHub Desktop.
Save consh004/5c1aded50dad9b3494bef3cf2584cd10 to your computer and use it in GitHub Desktop.
Work in progress towards a Waffle Shiro realm
package waffle;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashingPasswordService;
import org.apache.shiro.authc.credential.PasswordMatcher;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.crypto.hash.Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import waffle.windows.auth.IWindowsAuthProvider;
import waffle.windows.auth.IWindowsIdentity;
import waffle.windows.auth.impl.WindowsAuthProviderImpl;
import com.sun.jna.platform.win32.Win32Exception;
/**
* A {@link org.apache.shiro.realm.Realm} that authenticates with Active
* Directory using WAFFLE. Authorization is left for subclasses to define by
* implementing the {@link #buildAuthorizationInfo} method.
*/
public abstract class AbstractWaffleRealm extends AuthorizingRealm {
private static final Logger log = LoggerFactory.getLogger(AbstractWaffleRealm.class);
private static final String realmName = "WAFFLE";
private final IWindowsAuthProvider provider = new WindowsAuthProviderImpl();
@Override
protected final AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken)
throws AuthenticationException {
AuthenticationInfo authenticationInfo = null;
if (!(authToken instanceof UsernamePasswordToken)) {
return null;
}
UsernamePasswordToken token = (UsernamePasswordToken) authToken;
String username = token.getUsername();
IWindowsIdentity identity = null;
try {
log.debug("Attempting login for user {}", username);
identity = provider.logonUser(username, new String(token.getPassword()));
if (identity.isGuest()) {
log.debug("Guest identity for user {}; denying access", username);
throw new AuthenticationException("Guest identities are not allowed access");
} else {
Object principal = new WaffleFqnPrincipal(identity);
authenticationInfo = buildAuthenticationInfo(token, principal);
log.debug("Successful login for user {}", username);
}
} catch (Win32Exception ex) {
log.debug("Failed login for user {}", username, ex);
throw new AuthenticationException("Login failed", ex);
} finally {
if (identity != null) {
identity.dispose();
}
}
return authenticationInfo;
}
private AuthenticationInfo buildAuthenticationInfo(UsernamePasswordToken token, Object principal) {
AuthenticationInfo authenticationInfo;
HashingPasswordService hashService = getHashService();
if (hashService != null) {
Hash hash = hashService.hashPassword(token.getPassword());
ByteSource salt = hash.getSalt();
authenticationInfo = new SimpleAuthenticationInfo(principal, hash, salt, realmName);
} else {
Object creds = token.getCredentials();
authenticationInfo = new SimpleAuthenticationInfo(principal, creds, realmName);
}
return authenticationInfo;
}
@Override
protected final AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
WaffleFqnPrincipal principal = principals.oneByType(WaffleFqnPrincipal.class);
if (principal != null) {
return buildAuthorizationInfo(principal);
} else {
return null;
}
}
/**
* Assembles the appropriate authorization information for the specified
* principal.
*
* @param principal the principal for which to assemble authorization
* information
* @return the authorization information for the specified principal
*/
protected abstract AuthorizationInfo buildAuthorizationInfo(WaffleFqnPrincipal principal);
private HashingPasswordService getHashService() {
CredentialsMatcher matcher = getCredentialsMatcher();
if (matcher instanceof PasswordMatcher) {
PasswordMatcher passwordMatcher = (PasswordMatcher) matcher;
PasswordService passwordService = passwordMatcher.getPasswordService();
if (passwordService instanceof HashingPasswordService) {
return (HashingPasswordService) passwordService;
}
}
return null;
}
}
package waffle;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
/**
* A {@link org.apache.shiro.realm.Realm} that authenticates with Active
* Directory using WAFFLE and assigns roles to users based on a mapping from
* their groups. To define permissions based on these roles, set a
* {@link org.apache.shiro.authz.permission.RolePermissionResolver}.
*/
public class GroupMappingWaffleRealm extends AbstractWaffleRealm {
private final Map<String, String> groupRolesMap = new HashMap<String, String>();
/**
* Sets the translation from group names to role names. If not set, the map
* is empty, resulting in no users getting roles.
*/
public void setGroupRolesMap(Map<String, String> groupRolesMap) {
this.groupRolesMap.clear();
if (groupRolesMap != null) {
this.groupRolesMap.putAll(groupRolesMap);
}
}
/**
* This method is called by to translate group names to role names. This
* implementation uses the groupRolesMap to map group names to role names.
*
* @param groupNames the group names that apply to the current user
* @return a collection of roles that are implied by the given role names
* @see {@link #setGroupRolesMap}
*/
protected Collection<String> getRoleNamesForGroups(Collection<String> groupNames) {
Set<String> roleNames = new HashSet<String>();
for (String groupName : groupNames) {
String roleName = groupRolesMap.get(groupName);
if (roleName != null) {
roleNames.add(roleName);
}
}
return roleNames;
}
/**
* Builds an {@link AuthorizationInfo} object based on the user's groups.
* The groups are translated to roles names by using the configured
* groupRolesMap.
*
* @param principal the principal of Subject that is being authorized
* @return the AuthorizationInfo for the given Subject principal
*
* @see {@link #setGroupRolesMap}
* @see {@link #getRoleNamesForGroups}
*/
protected AuthorizationInfo buildAuthorizationInfo(WaffleFqnPrincipal principal) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRoles(getRoleNamesForGroups(principal.getGroupFqns()));
return authorizationInfo;
}
}
package waffle;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import waffle.windows.auth.IWindowsAccount;
import waffle.windows.auth.IWindowsIdentity;
public class WaffleFqnPrincipal implements Serializable {
private static final long serialVersionUID = 1;
private final String fqn;
private final Set<String> groupFqns = new HashSet<String>();
WaffleFqnPrincipal(IWindowsIdentity identity) {
fqn = identity.getFqn();
for (IWindowsAccount group : identity.getGroups()) {
groupFqns.add(group.getFqn());
}
}
/**
* Returns the fully qualified name of the user
*/
public String getFqn() {
return fqn;
}
/**
* Returns the fully qualified names of all groups that the use belongs to
*/
public Set<String> getGroupFqns() {
return Collections.unmodifiableSet(groupFqns);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof WaffleFqnPrincipal) {
return fqn.equals(((WaffleFqnPrincipal) obj).fqn);
}
return false;
}
@Override
public int hashCode() {
return fqn.hashCode();
}
@Override
public String toString() {
return "{" + getClass().getSimpleName() + ":" + fqn + "}";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment