package org.dcache.gplazma.plugins;
import org.globus.gsi.gssapi.jaas.GlobusPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.kerberos.KerberosPrincipal;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.dcache.auth.EmailAddressPrincipal;
import org.dcache.auth.GidPrincipal;
import org.dcache.auth.OidcSubjectPrincipal;
import org.dcache.auth.UidPrincipal;
import org.dcache.auth.UserNamePrincipal;
import org.dcache.gplazma.AuthenticationException;
import org.dcache.gplazma.plugins.exceptions.GplazmaParseMapFileException;
import org.dcache.util.Args;
public class GplazmaMultiMapFile
{
private static final Logger LOG = LoggerFactory.getLogger(GplazmaMultiMapFile.class);
private File file;
private long lastLoaded;
private Map<Principal,Set<Principal>> map = Collections.emptyMap();
private static final String[] principalTypes = new String[]{"dn",
"email",
"username",
"kerberos",
"oidc",
"uid",
"gid" };
public GplazmaMultiMapFile(String path)
{
this(new File(path));
}
public GplazmaMultiMapFile(File file)
{
this.file = file;
}
public synchronized void ensureUpToDate() throws AuthenticationException
{
if (lastLoaded <= file.lastModified()) {
LOG.debug("Reading file {}", file);
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
map = parseMapFile(reader);
lastLoaded = System.currentTimeMillis();
} catch (IOException e) {
throw new AuthenticationException(
String.format("failed to read %s: %s", file.getName(), e.getMessage()));
}
}
}
private static Map<Principal,Set<Principal>> parseMapFile(BufferedReader reader) throws IOException
{
Map<Principal,Set<Principal>> map = new HashMap<>();
String line;
String lineOrig;
int lineCount = 0;
while ((line = reader.readLine()) != null) {
lineOrig = line = line.trim();
lineCount++;
if (line.isEmpty() || line.charAt(0) == '#') continue;
try {
List<Principal> principals = parse(line);
if (!principals.isEmpty()) {
Iterator<Principal> iterator = principals.iterator();
Set<Principal> mapSet = new LinkedHashSet<>();
Principal key = iterator.next();
while (iterator.hasNext()) {
mapSet.add(iterator.next());
}
if (!mapSet.isEmpty()) {
if (!map.containsKey(key)) {
map.put(key, mapSet);
} else {
LOG.warn("{}: Ignored Additional Mapping for key {}",
lineCount, lineOrig, key);
}
} else {
LOG.warn("{}: Empty Map", lineCount, lineOrig);
}
}
} catch (GplazmaParseMapFileException e) {
LOG.warn("{}: {}", lineCount, e.getMessage());
}
}
return map;
}
private static Principal createPrincipal(String predicate, String principal) throws GplazmaParseMapFileException {
try {
switch (predicate) {
case "oidc":
return new OidcSubjectPrincipal(principal);
case "email":
return new EmailAddressPrincipal(principal);
case "username":
return new UserNamePrincipal(principal);
case "dn":
return new GlobusPrincipal(principal);
case "kerberos":
return new KerberosPrincipal(principal);
case "uid":
return new UidPrincipal(principal);
case "gid":
return createGidPrincipal(principal);
default:
throw new GplazmaParseMapFileException("Not supported predicate [" + predicate + "]");
}
} catch (IllegalArgumentException e) {
throw new GplazmaParseMapFileException("Illegal Value [" +
principal +
"] to predicate [" +
predicate +
"]");
}
}
private static Principal createGidPrincipal(String principal) throws GplazmaParseMapFileException
{
if (countNumCommas(principal) > 1) {
throw new GplazmaParseMapFileException("Illegal Value [" + principal +"] for gid");
} else {
String[] splits = principal.split(",");
if (splits.length == 2) {
return new GidPrincipal(splits[0], Boolean.parseBoolean(splits[1]));
} else {
return new GidPrincipal(principal, false);
}
}
}
private static List<Principal> parse(CharSequence line) throws GplazmaParseMapFileException
{
final List<Principal> principals = new LinkedList<>();
Args args = new Args(line);
for (int i = 0; i < args.argc(); i++) {
String argument = args.argv(i);
int colon = argument.indexOf(':');
if (colon != -1) {
principals.add(createPrincipal(argument.substring(0, colon),
argument.substring(colon + 1, argument.length())));
} else {
throw new GplazmaParseMapFileException("Missing colon in \"" + argument+"\"");
}
}
return principals;
}
private static int countNumCommas(String principal) {
return principal.length() - principal.replace(",", "").length();
}
public synchronized Set<Principal> getMappedPrincipals(Principal principal)
{
Set<Principal> out = map.get(principal);
return (out == null) ? Collections.emptySet(): out;
}
}