org.glite.authz.pep.pip.provider.InInfoFileIssuerDNMatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.glite.authz.pep.pip.provider.InInfoFileIssuerDNMatcher.java

Source

// Copyright (c) FOM-Nikhef 2016-
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Authors:
// 2016-
// Rens Visser <rensv@nikhef.nl>
// NIKHEF Amsterdam, the Netherlands
// <grid-mw-security@nikhef.nl>

package org.glite.authz.pep.pip.provider;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;

import java.nio.charset.StandardCharsets;

import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;

import java.security.KeyStoreException;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.Set;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.InvalidNameException;
import javax.print.DocFlavor.URL;

import org.glite.authz.common.model.Attribute;
import org.glite.authz.common.model.Request;
import org.glite.authz.common.model.Resource;
import org.glite.authz.common.model.Subject;

import org.glite.authz.pep.pip.PIPProcessingException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.commons.lang.StringUtils;

//import org.bouncycastle.jcajce.provider.symmetric.AES.OFB;

import eu.emi.security.authn.x509.impl.CertificateUtils;
import eu.emi.security.authn.x509.impl.CertificateUtils.Encoding;
import eu.emi.security.authn.x509.proxy.ProxyUtils;

/**
 * @author Rens Visser
 * @version 1.0
 * @since 1.0
 * 
 *        The InInfoFileIssuerDNMatcher PIP uses a pre-extracted issuer DN from
 *        the incoming request. The PIP uses the Issuer DN to find info files
 *        containing the issuer DN. After finding the required *.info files, the
 *        XACML request is populated and can be used by the PEPD.
 */
public class InInfoFileIssuerDNMatcher extends AbstractPolicyInformationPoint {
    /**
     * Class logger.
     */
    private final Logger log = LoggerFactory.getLogger(InInfoFileIssuerDNMatcher.class);

    /**
     * Default String of issuer DN attribute(s): {@value}
     */
    private final static String ATTRIBUTE_IDENTIFIER_X509_ISSUER = "http://authz-interop.org/xacml/subject/subject-x509-issuer";

    /**
     * Default String of CA policy names attribute(s): {@value}
     */
    private final static String ATTRIBUTE_IDENTIFIER_CA_POLICY_NAMES = "http://authz-interop.org/xacml/subject/ca-policy-names";

    /**
     * Contains a string of the certificate issuer DN.
     */
    protected static String CertificateIssuerDN;

    /**
     * Contains a string of trusted .info file and certificates location.
     */
    private static String acceptedtrustInfoDir;

    /**
     * The constructor
     * 
     * @param pipid
     *            String consisting of the identifier of the pip.
     * @param acceptedtrustInfoDirLocal
     *            Directory containing info files
     */
    public InInfoFileIssuerDNMatcher(String pipid, String acceptedtrustInfoDirLocal) {
        super(pipid);

        if (!acceptedtrustInfoDirLocal.endsWith("//")) {
            acceptedtrustInfoDirLocal = acceptedtrustInfoDirLocal + "//";
        }

        acceptedtrustInfoDir = acceptedtrustInfoDirLocal;
    }

    /**
     * When the incoming request has no subjects then this PIP will NOT run.
     * This PIP will throw an Exception when CertificateIssuerDN is empty. This
     * PIP takes an incoming request, it extracts the Issuer DN incoming
     * request. The extracted issuer DN is then compared to certificate issuer
     * in all *.info files on the server. This PIP fails when, there are no info
     * files.
     *
     * The method that does all the work. The Argus framework makes sure that
     * when a PIP does apply to a request, the populateRequest(Request request)
     * method is always run.
     * 
     * @param request
     *            Request object containing all information of the incoming
     *            request.
     * 
     * @throws PIPProcessingException
     *             Thrown when a unexpected error occurs.
     * 
     * @return boolean
     */
    /** {@inheritDoc} */
    public boolean populateRequest(Request request) throws PIPProcessingException {
        boolean PIP_applied = false;
        try {
            // Make the request editable and readable in other parts of the java
            // code.
            Set<Subject> subjects = request.getSubjects();
            // List of all files in the grid-security directory
            List<String> allInfoFiles = findAllInfoFiles();
            String file = null;

            if (allInfoFiles == null || allInfoFiles.size() == 0) {
                throw new PIPProcessingException("No info files!");
            }

            if (subjects.isEmpty()) {
                throw new PIPProcessingException("No subject found in request!!");
            }

            // Start iteration to find correct info files.
            for (Subject subject : subjects) {
                // Gets the Issuer DN from the subject and stores it in the
                // CerificateIssuerDN variable.
                CertificateIssuerDN = getIssuerDNFromSubject(subject.getAttributes());
                // CertificateIssuerDN = (CertificateIssuerDN);
                // Checks if the certificate issuer equals null, if it equals
                // null, skip the rest of the code and continue with a new loop.
                if (CertificateIssuerDN == null) {
                    throw new Exception("Certificate issuer attribute is not set");
                }

                // Create the attributes to be send to PEPD.
                Attribute policyInformation = new Attribute(ATTRIBUTE_IDENTIFIER_CA_POLICY_NAMES);
                policyInformation.setDataType(Attribute.DT_STRING);
                log.debug("allInfoFiles.size() = " + allInfoFiles.size());
                // Loop over all found info files.
                for (int i = 0; i < allInfoFiles.size(); i++) {
                    // Use one specific info files
                    file = allInfoFiles.get(i);

                    // Matches CertificateIssuerDN to contents if info file
                    // specified in "file".
                    log.debug("File: " + file);
                    if (issuerDNParser(file)) {
                        PIP_applied = true;

                        policyInformation.getValues().add(file.replace(".info", ""));
                    }
                }
                // Actually adding all the information being send to the PEPD.
                subject.getAttributes().add(policyInformation);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return PIP_applied;
    }

    /**
     * Decodes the URL in the String urlToDecode.
     * 
     * @param urlToDecode
     *            The String to decode
     * @return The decoded string
     */
    private String urlDecode(String urlToDecode) {
        int index;

        // Loop over the urlToDecode string, check if % are present.
        while ((index = urlToDecode.indexOf('%')) != -1) {
            // Get the substring containing the encoded character.
            String subSTR = urlToDecode.substring(index + 1, index + 3);
            // Concatenate the decoded URL back together.
            urlToDecode = urlToDecode.substring(0, index) + (char) Integer.parseInt(subSTR, 16)
                    + urlToDecode.substring(index + 3);
        }
        // Return entire urlToDecode String.
        return urlToDecode;
    }

    /**
     * Adds all .info files from the grid-security/certificates folder to the
     * variable infoFilesAll. As last the method returns a list containing
     * Strings. The Strings contain all *.info files.
     * 
     * @return A list of strings. The strings represent *.info file.
     */
    private List<String> findAllInfoFiles() throws IOException {
        List<String> infoFilesAll = new ArrayList<String>();
        DirectoryStream<Path> stream = null;
        try {
            stream = Files.newDirectoryStream(Paths.get(acceptedtrustInfoDir), "*.info");
        } catch (Exception e) {
            log.error(e.getMessage());
            return infoFilesAll;
        }

        try {
            for (Path entry : stream) {
                if (Files.isRegularFile(entry, LinkOption.NOFOLLOW_LINKS)) {
                    infoFilesAll.add(entry.getFileName().toString());
                }
            }

        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            stream.close();
        }

        return infoFilesAll;
    }

    /**
     * Gathers the issuer DN from Attributes. Return the issuer DN when found,
     * if not found the method returns null.
     * 
     * @param attributes
     *            The request where the issuer DN is extracted from. from.
     * @return A string with "failed" or the issuer DN.
     */
    protected String getIssuerDNFromSubject(Set<Attribute> attributes) {
        StringBuilder strBuilder = new StringBuilder();
        for (Attribute att : attributes) {
            if (att.getId().matches(ATTRIBUTE_IDENTIFIER_X509_ISSUER) == true) {
                strBuilder.append(att.getValues().iterator().next());
                log.debug(ATTRIBUTE_IDENTIFIER_X509_ISSUER + " = " + strBuilder.toString().trim());
                return strBuilder.toString().trim();
            }
        }
        return null;
    }

    /**
     * Parses the file from the input String. Method parses all lines in the
     * .info file. All lines are parsed one by one. Checks if line ends with a
     * "\" if yes, remove the slash. Checks if line contains hask. If yes,
     * removes the # and all text behind it.
     * 
     * @param fileName
     *            The {@link String} of the file to parse.
     * @return true if info file is found, false otherwise.
     * @throws IOException
     *             Throws an exception when the file can't be passed.
     * 
     * @throws Exception
     *             Thrown when file descriptor does not close properly
     */
    protected Boolean issuerDNParser(String fileName) throws IOException, Exception {
        StringBuilder stringBuilder = new StringBuilder();
        FileReader readerFile = new FileReader(acceptedtrustInfoDir + fileName);
        BufferedReader br = new BufferedReader(readerFile);
        String contentLine = null;
        Pattern pattern = Pattern.compile("^subjectdn\\s*=\\s*");

        try {
            while ((contentLine = br.readLine()) != null) {
                if (contentLine.contains("#")) {
                    contentLine = removeHashAndRestOfline(contentLine);
                    contentLine.contains("contentLine = " + contentLine);
                    if (contentLine.isEmpty()) {
                        continue;
                    }
                }

                if (contentLine.endsWith("\\")) {
                    contentLine = removeTrailingSlash(contentLine);
                    stringBuilder.append(contentLine);
                    continue;

                } else {
                    stringBuilder.append(contentLine);
                    contentLine = stringBuilder.toString();
                    stringBuilder = new StringBuilder();
                }

                contentLine = contentLine.trim();
                if (pattern.matcher(contentLine).lookingAt() && !contentLine.isEmpty()) {
                    contentLine = contentLine.replaceFirst("^(subjectdn(\\s)*=(\\s)*)", "");
                    return issuerDNMatcher(contentLine);
                }
            }
        } catch (Exception e) {
            log.debug(e.getMessage());
        } finally {
            br.close();
        }
        return false;
    }

    /**
     * Matches all lines till known issuerDN and the searched issuerDN are
     * matched. After the match either a true on success, or false on failure is
     * returned. Splits the string into an array, the array is then searched.
     * 
     * @param input
     *            String to be checked.
     * @return Boolean true on success, false on failure.
     */
    private Boolean issuerDNMatcher(String input) {
        String[] issuerDNInfoFileArray = input.split("(?<=\\\"),");
        String decodedCertificateIssuerDN;
        for (int b = 0; b < issuerDNInfoFileArray.length; b++) {
            decodedCertificateIssuerDN = urlDecode(issuerDNInfoFileArray[b].trim());
            if (decodedCertificateIssuerDN.matches("\"" + CertificateIssuerDN + "\"")) {
                return true;
            }
        }
        return false;
    }

    /**
     * Removes the "#" and everything behind the "#" from the input string.
     * 
     * @param input
     *            The String to be modified.
     * @return String The modified string.
     */
    private String removeHashAndRestOfline(String input) {
        int i = input.indexOf("#");

        if (i == 0) {
            return "";
        } else {
            return input.substring(0, i - 1);
        }
    }

    /**
     * Removes the trailing slash from the input {@link String} and returns the
     * modified string.
     * 
     * @param inputString
     *            The String where the trailing slash is removed from.
     * @return The modified String
     */
    private String removeTrailingSlash(String inputString) {
        StringBuilder toReturn = new StringBuilder(inputString);
        int i = inputString.lastIndexOf("\\");

        if (i != -1) {
            toReturn.deleteCharAt(i);
        }
        return toReturn.toString();
    }
}