package org.dcache.acl.mapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import org.dcache.acl.ACE;
import org.dcache.acl.ACL;
import org.dcache.acl.Permission;
import org.dcache.acl.enums.AccessMask;
import org.dcache.acl.enums.AceFlags;
import org.dcache.acl.enums.AceType;
import org.dcache.acl.enums.RsType;
import org.dcache.acl.enums.Who;
import org.dcache.acl.unix.ACLUnix;
import org.dcache.acl.unix.AMUnix;
/**
* Mapping Between NFSv4 and Unix ACLs.
*
* @author David Melkumyan, DESY Zeuthen
*
*/
public class AclUnixMapper {
private static final Logger logger = LoggerFactory.getLogger("logger.org.dcache.authorization." + AclUnixMapper.class.getName());
private static AclUnixMapper _SINGLETON;
static {
_SINGLETON = new AclUnixMapper();
}
private AclUnixMapper() {
super();
}
public static AclUnixMapper instance() {
return _SINGLETON;
}
public static ACLUnix map(ACL acl) {
final RsType rsType = acl.getRsType();
final List<ACE> aces = acl.getList();
List<ACE> accessACEs = aces;
List<ACE> defaultACEs;
int[] defaultMasks = null;
if ( rsType == RsType.DIR ) {
splitACEs(aces, accessACEs = new ArrayList<>(), defaultACEs = new ArrayList<>());
defaultMasks = getMasks(defaultACEs);
}
int[] accessMasks = getMasks(accessACEs);
return new ACLUnix(RsType.DIR.equals(rsType), accessMasks, defaultMasks);
}
private static int[] getMasks(List<ACE> aces) {
int[] masks = new int[ACLUnix.NUM_ACES];
Permission permOwner = new Permission();
Permission permGroup = new Permission();
Permission permEveryone = new Permission();
for (ACE ace : aces) {
Who who = ace.getWho();
boolean allowed = (AceType.ACCESS_ALLOWED_ACE_TYPE == ace.getType());
switch (who) {
case OWNER:
applyMask(permOwner, ace.getAccessMsk(), allowed);
break;
case OWNER_GROUP:
applyMask(permGroup, ace.getAccessMsk(), allowed);
break;
case EVERYONE:
int msk = applyMask(permEveryone, ace.getAccessMsk(), allowed);
if ( msk != 0 ) {
applyMask(permOwner, msk, allowed);
applyMask(permGroup, msk, allowed);
}
break;
default:
logger.info("Unsupported who: " + who);
}
}
masks[ACLUnix.OWNER_INDEX] = perm2accessMask(permOwner);
masks[ACLUnix.GROUP_OWNER_INDEX] = perm2accessMask(permGroup);
masks[ACLUnix.OTHER_INDEX] = perm2accessMask(permEveryone);
return masks;
}
private static int applyMask(Permission perm, int access_msk, boolean allowed) {
int msk = access_msk & (~perm.getDefMsk());
if ( msk != 0 ) {
perm.appendDefMsk(msk);
if ( allowed ) {
perm.appendAllowMsk(msk);
}
}
return msk;
}
private static int perm2accessMask(Permission perm) {
int allow_msk = perm.getAllowMsk();
int mask = 0;
if ( AccessMask.READ_DATA.matches(allow_msk) || AccessMask.LIST_DIRECTORY.matches(allow_msk) ) {
mask |= AMUnix.READ.getValue();
}
if ( AccessMask.WRITE_DATA.matches(allow_msk) || AccessMask.ADD_FILE.matches(allow_msk) || AccessMask.APPEND_DATA.matches(allow_msk)
|| AccessMask.ADD_SUBDIRECTORY.matches(allow_msk) || AccessMask.DELETE_CHILD.matches(allow_msk) )
{
mask |= AMUnix.WRITE.getValue();
}
if ( AccessMask.EXECUTE.matches(allow_msk) ) {
mask |= AMUnix.EXECUTE.getValue();
}
return mask;
}
/**
* If the ACL in question is for a directory, we split it into two ACLs, one
* purely effective and one purely inherited.
*
* @param aces
* List of ACEs
* @param effectiveACEs
* Returnes list of purely effective ACEs
* @param inheritedACEs
* Returns list of purely inherited ACEs
*
*/
private static void splitACEs(List<ACE> aces, List<ACE> effectiveACEs, List<ACE> inheritedACEs) {
for (ACE ace : aces) {
int flags = ace.getFlags();
if ( AceFlags.INHERIT_ONLY_ACE.matches(flags) == false ) {
effectiveACEs.add(ace);
}
if ( AceFlags.DIRECTORY_INHERIT_ACE.matches(flags) || AceFlags.FILE_INHERIT_ACE.matches(flags) ) {
inheritedACEs.add(ace);
}
}
}
}