Package org.globus.workspace.groupauthz

Source Code of org.globus.workspace.groupauthz.DecisionLogic

/*
* Copyright 1999-2008 University of Chicago
*
* 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.
*/

package org.globus.workspace.groupauthz;

import org.globus.workspace.service.binding.vm.VirtualMachine;
import org.globus.workspace.service.binding.vm.VirtualMachineDeployment;
import org.globus.workspace.service.binding.vm.VirtualMachinePartition;
import org.globus.workspace.service.binding.authorization.Decision;
import org.nimbustools.api.services.rm.AuthorizationException;
import org.nimbustools.api.services.rm.ResourceRequestDeniedException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.security.NoSuchAlgorithmException;
import java.net.URI;
import java.net.URISyntaxException;

public class DecisionLogic {

    private static final Log logger = LogFactory.getLog(Group.class.getName());

    public  DecisionLogic()
    {
    }
    /**
     *
     * @param dn may not be null
     * @param rights may not be null
     * @param bindings may not be null, can be zerolen array
     * @param elapsedMins may not be null or negative
     * @param reservedMins may not be null or negative
     * @param numWorkspaces number of OTHER, current workspaces
     * @param chargeRatio ratio to compute the real minutes charge, typically <= 1.0 and > 0
     * @return Decision Integer
     * @throws AuthorizationException processing problem
     * @throws ResourceRequestDeniedException error w/ explanation to client
     */
    public Integer decide(String dn,
                          GroupRights rights,
                          VirtualMachine[] bindings,
                          Long elapsedMins,
                          Long reservedMins,
                          int numWorkspaces,
                          double chargeRatio)

            throws AuthorizationException,
                   ResourceRequestDeniedException {

        if (!HashUtil.isInitialized()) {
            throw new AuthorizationException("Cannot give an authorization " +
                    "decision without a properly initialized hashing system");
        }

        if (dn == null) {
            throw new IllegalArgumentException("dn is null");
        }

        if (rights == null) {
            throw new IllegalArgumentException("rights is null");
        }

        if (bindings == null) {
            throw new IllegalArgumentException("bindings is null");
        }

        if (elapsedMins == null) {
            throw new IllegalArgumentException("elapsedMins is null");
        } else if (elapsedMins.longValue() < 0) {
            throw new IllegalArgumentException("elapsedMins is negative");
        }

        if (reservedMins == null) {
            throw new IllegalArgumentException("reservedMins is null");
        } else if (reservedMins.longValue() < 0) {
            throw new IllegalArgumentException("reservedMins is negative");
        }

        if (chargeRatio <= 0) {
            throw new IllegalArgumentException("expecting charge ratio to be positive");
        }

        final StringBuffer buf = new StringBuffer("\n\nConsidering caller: '");
        buf.append(dn)
           .append("'.\nCurrent elapsed minutes: ")
           .append(elapsedMins)
           .append(".\nCurrent reserved minutes: ")
           .append(reservedMins)
           .append(".\nNumber of VMs in request: ")
           .append(bindings.length)
           .append(".\nCharge ratio for request: ")
           .append(chargeRatio)
           .append(".\nNumber of VMs caller is already currently running: ")
           .append(numWorkspaces)
           .append(".\nRights:\n")
           .append(rights)
           .append("\n\n");

        final int maxCurrentPolicy = (int) rights.getMaxWorkspaceNumber();
        if (maxCurrentPolicy > 0) {

            if (numWorkspaces + bindings.length > maxCurrentPolicy) {

                final StringBuffer newbuf =
                        new StringBuffer("\nDenied: Request for ");
                newbuf.append(bindings.length)
                      .append(" workspaces");

                if (numWorkspaces != 0) {
                    newbuf.append(", together with number of currently " +
                                  "running workspaces (")
                          .append(numWorkspaces)
                          .append("),");
                }

                newbuf.append(" exceeds the maximum, which is ")
                      .append(maxCurrentPolicy)
                      .append(" concurrently running workspaces.");

                final String msg = newbuf.toString();
                buf.append(msg);
                logger.warn(buf.toString());
                throw new ResourceRequestDeniedException(msg);
            }
        }

        long requestDur = 0;
        for (int i = 0; i < bindings.length; i++) {

            final VirtualMachineDeployment dep = bindings[i].getDeployment();
            if (dep == null) {
                final String msg = "ERROR: No deployment information in " +
                        "binding, can't make decision.";
                buf.append(msg);
                logger.error(buf.toString());
                throw new AuthorizationException(msg);
            }

            final long seconds = dep.getMinDuration();
            requestDur += seconds / 60;
        }

        final Double doubleRequestDur = requestDur * chargeRatio;
        requestDur = doubleRequestDur.longValue();

        if (bindings.length > 1) {
            buf.append("Duration total of all requests in group: ");
        } else {
            buf.append("Duration request: ");
        }

        buf.append(requestDur)
           .append("\n");

        // zero or below means no check should be made
        if (rights.getMaxCPUs() > 0) {
            final long maxCPUs = rights.getMaxCPUs();
            for (int i = 0; i < bindings.length; i++) {

                final VirtualMachineDeployment dep = bindings[i].getDeployment();
                if (dep == null) {
                    final String msg = "ERROR: No deployment information in " +
                            "binding, can't make decision.";
                    buf.append(msg);
                    logger.error(buf.toString());
                    throw new AuthorizationException(msg);
                }
                final long currentCPUs = dep.getIndividualCPUCount();
                if (currentCPUs > maxCPUs) {

                    buf.append("\nDenied: Requested CPU count (")
                       .append(currentCPUs)
                       .append(") + is greater or equal to maximum CPU count (")
                       .append(maxCPUs)
                       .append(").\n");

                    logger.warn(buf.toString());

                    throw new ResourceRequestDeniedException(
                                "You requested too many CPUs (" +
                                        currentCPUs + "), the " +
                                        "maximum is " +
                                        maxCPUs + " CPUs.");
                    }
            }
        }

        // zero or below means no check should be made
        if (rights.getMaxReservedMinutes() > 0) {
            final long max = rights.getMaxReservedMinutes();
            final long current = reservedMins.longValue();
            if (requestDur + current > max) {

                buf.append("\nDenied: Request duration (")
                   .append(requestDur)
                   .append(") + current reserved tally (")
                   .append(current)
                   .append(") + is greater or equal to maximum reserved (")
                   .append(max)
                   .append(").\n");

                logger.warn(buf.toString());

                throw new ResourceRequestDeniedException(
                            "Your request is for too much time (" +
                                    requestDur + "), the " +
                                    "maximum reserved at once is " +
                                    max + " minutes.  You currently have " +
                                    current + " other reserved minutes.");
            }
        }

        // zero or below means no check should be made
        if (rights.getMaxElapsedReservedMinutes() > 0) {
            final long max = rights.getMaxElapsedReservedMinutes();
            final long currentElapsed = elapsedMins.longValue();
            final long currentReserved = reservedMins.longValue();
            final long tally = currentElapsed + currentReserved;
            if (requestDur + tally > max) {

                buf.append("\nDenied: Request duration (")
                   .append(requestDur)
                   .append(") + current reserved+elapsed tally (")
                   .append(tally)
                   .append(") + is greater or equal to maximum reserved+elapsed (")
                   .append(max)
                   .append(").\n");

                logger.warn(buf.toString());

                throw new ResourceRequestDeniedException(
                            "Your request is for too much time (" +
                                requestDur + "), this would exceed the " +
                                "maximum you can have both used in the " +
                                "past and have reserved currently. " +
                                "This maximum is " +
                                max + " minutes.  You currently have " +
                                currentElapsed + " elapsed minutes " +
                                "and " + currentReserved +
                                " reserved minutes and the request for " +
                                requestDur + " minutes would exceed this.");
            }
        }

        final String dnhash;
        if (rights.isDirHashMode()) {
            try {
                dnhash = HashUtil.hashDN(dn);
            } catch (NoSuchAlgorithmException e) {
                final String msg = "ERROR: DN hash required but it " +
                        "is not available: " + e.getMessage();
                buf.append(msg);
                logger.error(buf.toString());
                throw new AuthorizationException(msg);
            }
        } else {
            dnhash = null;
        }

        for (int i = 0; i < bindings.length; i++) {

            final VirtualMachinePartition[] parts =
                                bindings[i].getPartitions();

            if (parts == null) {
               final String msg = "ERROR: No partition information in " +
                        "binding, can't make decision.";
                buf.append(msg);
                logger.error(buf.toString());
                throw new AuthorizationException(msg);
            }

            checkImages(parts, rights, buf, dn, dnhash);
        }

        buf.append("\n");
        logger.info(buf.toString());
        return Decision.PERMIT;
    }

    protected void checkImages(VirtualMachinePartition[] parts,
                                    GroupRights rights,
                                    StringBuffer buf,
                                    String dn,
                                    String dnhash)

            throws AuthorizationException,
                   ResourceRequestDeniedException {

        final String basedir = rights.getImageBaseDirectory();
        final String comparisonDir = normalize(basedir, buf);
        if (basedir != null && !basedir.equals(comparisonDir)) {
            logger.debug("Configured base directory policy normalized from '" +
                    basedir + "' into '" + comparisonDir + "'");
        }

        final String hostname = rights.getImageNodeHostname();

        String subdir = null;
        if (dnhash != null) {
            subdir = comparisonDir + "/" + dnhash;
        }

        for (int i = 0; i < parts.length; i++) {

            if (!parts[i].isPropRequired() && !parts[i].isUnPropRequired()) {
                logger.debug("groupauthz not examining '" +
                        parts[i].getImage() + "': no prop/unprop needed");
                continue;
            }

            final String imgString = parts[i].getImage();
            final String altTargetString = parts[i].getAlternateUnpropTarget();
            try {

                final URI imgURI = new URI(imgString);
                URI altTargetURI = null;
                if (altTargetString != null) {
                    altTargetURI = new URI(altTargetString);
                }

                if (hostname != null) {
                    checkNodeHostname(hostname, buf, imgURI, altTargetURI);
                }

                if (basedir != null) {
                    checkNodeBasedir(comparisonDir,
                                     buf,
                                     imgURI,
                                     altTargetURI);
                }

                if (subdir != null) {
                    checkNodeBasedir(subdir,
                                     buf,
                                     imgURI,
                                     altTargetURI);
                }



            } catch (URISyntaxException e) {
                final String msg = "ERROR: Partition in " +
                    "binding is not a valid URI? Can't make decision. " +
                        " Error message: " + e.getMessage();
                buf.append(msg);
                logger.error(buf.toString());
                throw new AuthorizationException(msg);
            }
        }
    }

    protected void checkNodeBasedir(String path,
                                         StringBuffer buf,
                                         URI imgURI,
                                         URI altTargetURI)
            throws ResourceRequestDeniedException {

        if (!imgURI.getPath().startsWith(path)) {

            buf.append("Request denied because image path in request ('")
               .append(imgURI.getPath())
               .append("') does not start with directory in rights ('")
               .append(path)
               .append("').\n");
            logger.warn(buf.toString());
            throw new ResourceRequestDeniedException(
                    "You may only use images under directory '" + path + "'");
        }

        if (altTargetURI != null &&
                !altTargetURI.getPath().startsWith(path)) {

            buf.append("Request denied because alternate target" +
                    " image path in request ('")
               .append(altTargetURI.getHost())
               .append("') does not start with directory in rights ('")
               .append(path)
               .append("').\n");
            logger.warn(buf.toString());
            throw new ResourceRequestDeniedException(
                    "You may only save images to alternate " +
                            "locations starting with base directory '" +
                            path + "'");
        }
    }

    protected void checkNodeHostname(String hostname,
                                          StringBuffer buf,
                                          URI imgURI,
                                          URI altTargetURI)
            throws ResourceRequestDeniedException {
       
        if (!hostname.equalsIgnoreCase(imgURI.getHost())) {

            buf.append("Request denied because image node in request ('")
               .append(imgURI.getHost())
               .append("') does not match image node in rights ('")
               .append(hostname)
               .append("').\n");
            logger.warn(buf.toString());
            throw new ResourceRequestDeniedException(
                    "You may only use images from host '" + hostname + "'");
        }

        if (altTargetURI != null &&
                !imgURI.getHost().equalsIgnoreCase(
                                    altTargetURI.getHost())) {

            buf.append("Request denied because alternate target" +
                    " image node in request ('")
               .append(altTargetURI.getHost())
               .append("') does not match approved image source " +
                       "node in request ('")
               .append(imgURI.getHost())
               .append("').\n");
            logger.warn(buf.toString());
            throw new ResourceRequestDeniedException(
                    "You may only save images to alternate " +
                            "locations on host '" + hostname + "'");
        }
    }

    public Integer checkNewAltTargetURI(GroupRights rights,
                                               URI altTargetURI,
                                               String dn)
            throws AuthorizationException {

        if (altTargetURI == null) {
            throw new IllegalArgumentException("altTargetURI may not be null");
        }

        if (rights == null) {
            throw new IllegalArgumentException("rights may not be null");
        }

        if (dn == null) {
            throw new IllegalArgumentException("DN may not be null");
        }

        final String dnhash;
        if (rights.isDirHashMode()) {
            try {
                dnhash = HashUtil.hashDN(dn);
            } catch (NoSuchAlgorithmException e) {
                final String msg = "ERROR: DN hash required but it " +
                        "is not available: " + e.getMessage();
                throw new AuthorizationException(msg);
            }
        } else {
            dnhash = null;
        }

        final String hostname = rights.getImageNodeHostname();
        if (hostname != null) {
            if (!hostname.equalsIgnoreCase(altTargetURI.getHost())) {
                throw new AuthorizationException(
                        "You may only use images from host '" + hostname + "'");
            }
        }

        final String basedir = rights.getImageBaseDirectory();
        if (basedir == null) {
            return Decision.PERMIT; // *** EARLY RETURN ***
        }

        final String comparisonDir;
       
        try {
            comparisonDir = normalize(basedir, null);
        } catch (ResourceRequestDeniedException e) {
            throw new AuthorizationException(e.getMessage(), e);
        }
       
        if (!basedir.equals(comparisonDir)) {
            logger.debug("Configured base directory policy normalized from '" +
                    basedir + "' into '" + comparisonDir + "'");
        }

        String subdir = null;
        if (dnhash != null) {
            subdir = comparisonDir + "/" + dnhash;
        }

        if (!altTargetURI.getPath().startsWith(comparisonDir)) {

            throw new AuthorizationException(
                    "You may only save images to alternate " +
                            "locations starting with base directory '" +
                            comparisonDir + "'");
        }

        if (subdir != null &&
                !altTargetURI.getPath().startsWith(subdir)) {

            throw new AuthorizationException(
                    "You may only save images to alternate " +
                            "locations starting with base directory '" +
                            subdir + "'");
        }

        return Decision.PERMIT;
    }

    protected String normalize(String input, StringBuffer buf)
            throws ResourceRequestDeniedException {

        if (input == null) {
            return "";
        }

        if (input.trim().length() == 0) {
            return "";
        }

        final String prefix = "Input path '" + input + "' contains " +
                    "illegal relative path construction";

        final String[] parts = input.split("/");
        for (int i = 0; i < parts.length; i++) {

            String msg = null;
            if (parts[i].equals(".")) {
                msg = prefix + " '/./' (it's benign but unhandled in " +
                        "matching code)";
            } else if (parts[i].equals("..")) {
                msg = prefix + " '/../'";
            }

            if (msg != null) {
                if (buf != null) {
                    buf.append(msg);
                    logger.error(buf.toString());
                }
                throw new ResourceRequestDeniedException(msg);
            }
        }

        final StringBuffer newbuf = new StringBuffer(input.length());

        final char[] chars = input.trim().toCharArray();

        boolean lastWasSeparator = false;
        for (int i = 0; i < chars.length; i++) {
            if (lastWasSeparator && chars[i] == '/') {
                continue;
            } else if (lastWasSeparator) {
                lastWasSeparator = false;
            } else if (chars[i] == '/') {
                lastWasSeparator = true;
            }
            newbuf.append(chars[i]);
        }

        return newbuf.toString();
    }

    void testNormalize() throws Exception {

        final String[] inputs =
                {"///abc/def/g", "/abc/def//g", "/abc/def///", "  ", null, "/asd\n"};
        final String[] expects =
                {"/abc/def/g",   "/abc/def/g""/abc/def/",   "",   "",   "/asd"};

        for (int i = 0; i < inputs.length; i++) {
            final String ret = normalize(inputs[i], null);
            if (!ret.equals(expects[i])) {
                throw new Exception("'" + ret + "' from '" + inputs[i] + "'");
            }
        }

        final String[] allShouldFail =
                {"/../abc/def/g", "/abc/def//g/../asd", "/abc/def///../..//"};

        for (int i = 0; i < allShouldFail.length; i++) {
            try {
                normalize(allShouldFail[i], null);
            } catch (ResourceRequestDeniedException e) {
                continue;
            }
            throw new Exception("'" + allShouldFail[i] + "' did not fail");
        }

    }
}
TOP

Related Classes of org.globus.workspace.groupauthz.DecisionLogic

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.