Created
December 21, 2012 12:40
-
-
Save davidmc24/4352563 to your computer and use it in GitHub Desktop.
Work in progress towards a Waffle Shiro realm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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