package org.dcache.gplazma.validation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.Subject;
import java.security.Principal;
import java.util.Set;
import org.dcache.auth.GidPrincipal;
import org.dcache.auth.UidPrincipal;
import org.dcache.auth.UserNamePrincipal;
import org.dcache.auth.attributes.HomeDirectory;
import org.dcache.auth.attributes.RootDirectory;
import org.dcache.gplazma.AuthenticationException;
import org.dcache.gplazma.LoginReply;
import static org.dcache.gplazma.util.Preconditions.checkAuthentication;
/**
* Door specific validation strategy
* which checks that there is one and only one user principal, one and only one
* Uid Principals, one and only one primary GID principal present in principals,
* and there is one and only one of each home and root directories in the
* attributes
* @author timur
*/
public class DoorValidationStrategy implements ValidationStrategy {
private static final Logger LOGGER = LoggerFactory.getLogger(DoorValidationStrategy.class);
@Override
public void validate(LoginReply loginReply)
throws AuthenticationException {
LOGGER.debug("Validating loginReply {}",loginReply);
if(loginReply == null) {
throw new NullPointerException ("loginReply is null");
}
Set<Principal> principals = getPrincipalsFromLoginReply(loginReply);
validatePrincipals(principals);
Set<Object> attributes = getSessionAttributesFromLoginReply(loginReply);
validateAttributes(attributes);
}
/**
* checks if authorizedPrincipals set contain at one and only one
* instance of each type of
* {@link UidPrincipal UidPrincipal},
* {@link GidPrincipal GidPrincipal} and
* {@link UserNamePrincipal UserNamePrincipal}
* @param principals
* @throws AuthenticationException if check fails
*/
private static void validatePrincipals(Set<Principal> principals)
throws AuthenticationException {
boolean hasUserName = false;
boolean hasUid = false;
boolean hasPrimaryGid = false;
for(Principal principal:principals) {
if(principal instanceof UserNamePrincipal) {
checkAuthentication(!hasUserName, "multiple usernames");
hasUserName = true;
continue;
}
if(principal instanceof UidPrincipal) {
checkAuthentication(!hasUid, "multiple UIDs");
hasUid = true;
continue;
}
if(principal instanceof GidPrincipal) {
GidPrincipal gidPrincipal = (GidPrincipal) principal;
if(gidPrincipal.isPrimaryGroup()) {
checkAuthentication(!hasPrimaryGid, "multiple GIDs");
hasPrimaryGid = true;
}
}
}
checkAuthentication(hasUserName && hasUid && hasPrimaryGid,
principalsErrorMessage(hasUserName, hasUid, hasPrimaryGid));
}
private static String principalsErrorMessage(boolean hasUserName,
boolean hasUid, boolean hasPrimaryGid)
{
StringBuilder errorMessage = new StringBuilder();
if(!hasUserName) {
errorMessage.append("no username");
}
if(!hasUid) {
appendWithComma(errorMessage, "no UID");
}
if(!hasPrimaryGid) {
appendWithComma(errorMessage, "no primary GID");
}
return errorMessage.toString();
}
/**
* checks if {@link attributes} has at one and only one of each of
* HomeDirectory and RootDirectory
* @param attributes
* @throws AuthenticationException if check fails
*/
private static void validateAttributes(Set<Object> attributes)
throws AuthenticationException
{
boolean hasHome = false;
boolean hasRoot = false;
for(Object attribute:attributes) {
if(attribute instanceof HomeDirectory) {
checkAuthentication(!hasHome, "multiple home-directories");
hasHome = true;
}
if(attribute instanceof RootDirectory) {
checkAuthentication(!hasRoot, "multiple root-directories");
hasRoot = true;
}
}
checkAuthentication(hasHome && hasRoot, attributesErrorMessage(hasHome, hasRoot));
}
private static String attributesErrorMessage(boolean hasHome, boolean hasRoot)
{
StringBuilder errorMsg = new StringBuilder();
if (!hasHome) {
errorMsg.append("no home-directory");
}
if (!hasRoot) {
if (!hasHome) {
errorMsg.append(", ");
}
errorMsg.append("no root-directory");
}
return errorMsg.toString();
}
private static StringBuilder appendWithComma(StringBuilder sb,
String message)
{
if(sb.length() > 0) {
sb.append(", ");
}
return sb.append(message);
}
private static Set<Object> getSessionAttributesFromLoginReply(LoginReply loginReply)
throws AuthenticationException
{
Set<Object> attributes = loginReply.getSessionAttributes();
checkAuthentication(attributes != null, "attributes is null");
return attributes;
}
private static Set<Principal> getPrincipalsFromLoginReply(LoginReply loginReply)
throws AuthenticationException
{
Subject subject = loginReply.getSubject();
checkAuthentication(subject != null, "subject is null");
Set<Principal> principals = subject.getPrincipals();
checkAuthentication(principals != null, "subject principals is null");
return principals;
}
}