/* * dCache - http://www.dcache.org/ * * Copyright (C) 2016 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.dcache.gplazma.util; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.SetMultimap; import org.globus.gsi.gssapi.jaas.GlobusPrincipal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Stream; import org.dcache.auth.IGTFPolicyPrincipal; import org.dcache.auth.IGTFStatusPrincipal; import org.dcache.gplazma.util.IGTFInfo.Status; import static com.google.common.base.Preconditions.checkArgument; import static org.dcache.gplazma.util.IGTFInfo.Type.POLICY; import static org.dcache.gplazma.util.IGTFInfo.Type.TRUST_ANCHOR; /** * Represents all *.info files in a directory. */ public class IGTFInfoDirectory { private static final Logger LOG = LoggerFactory.getLogger(IGTFInfoDirectory.class); private static final int MIN_STAT_DURATION = 2_000; private static final Map<Status,String> TO_NAME; static { ImmutableMap.Builder<Status,String> names = ImmutableMap.builder(); names.put(Status.ACCREDITED_CLASSIC, "classic"); names.put(Status.ACCREDITED_IOTA, "iota"); names.put(Status.ACCREDITED_MICS, "mics"); names.put(Status.ACCREDITED_SLCS, "slcs"); names.put(Status.DISCONTINUED, "discontinued"); names.put(Status.EXPERIMENTAL, "experimental"); names.put(Status.UNACCREDITED, "unaccredited"); TO_NAME = names.build(); } private final Path directory; private final Map<Path,IGTFInfoFile> files = new HashMap<>(); private final Map<GlobusPrincipal,IGTFStatusPrincipal> taStatus = new HashMap<>(); private final SetMultimap<GlobusPrincipal,IGTFPolicyPrincipal> taPolicies = HashMultimap.create(); private long lastStat; private FileTime lastScanned; public IGTFInfoDirectory(String directory) { this(Paths.get(directory)); } public IGTFInfoDirectory(Path directory) { checkArgument(Files.isDirectory(directory)); this.directory = directory; } public Set<Principal> getPrincipals(GlobusPrincipal certificateAuthority) { Set<Principal> principals = new HashSet<>(); try { verifyUptoDate(); } catch (IOException e) { LOG.warn("Problem scanning directory {}: {}", directory, e.toString()); } IGTFStatusPrincipal status = taStatus.get(certificateAuthority); if (status != null) { principals.add(status); } principals.addAll(taPolicies.get(certificateAuthority)); return principals; } private synchronized void verifyUptoDate() throws IOException { if (System.currentTimeMillis() - lastStat > MIN_STAT_DURATION) { lastStat = System.currentTimeMillis(); FileTime mtime = Files.getLastModifiedTime(directory); if (lastScanned == null || mtime.compareTo(lastScanned) > 0) { lastScanned = mtime; updateDirectory(); } } } public void updateDirectory() throws IOException { List<Path> contents = new ArrayList<>(); try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, "*.info")) { stream.forEach(contents::add); } files.keySet().retainAll(contents); contents.removeAll(files.keySet()); contents.forEach(p -> files.put(p, new IGTFInfoFile(p))); taStatus.clear(); taPolicies.clear(); files.values().stream() .map(IGTFInfoFile::get) .flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty()) .forEach(this::addPrincipals); } private void addPrincipals(IGTFInfo info) { switch (info.getType()) { case TRUST_ANCHOR: Status s = info.getStatus(); IGTFStatusPrincipal st = new IGTFStatusPrincipal(TO_NAME.get(s), s.isAccredited()); taStatus.put(info.getSubjectDN(), st); break; case POLICY: IGTFPolicyPrincipal policy = new IGTFPolicyPrincipal(info.getName()); info.getSubjectDNs().forEach(dn -> taPolicies.put(dn, policy)); break; } } }