All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.security.services.common.SecureServiceAccessPermission Maven / Gradle / Ivy

There is a newer version: 4.1.2.181
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.security.services.common;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.security.BasicPermission;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.enterprise.util.LocalStringManagerImpl;


public class SecureServiceAccessPermission extends BasicPermission {

    private static final long serialVersionUID = -274181305911341984L;
        
    public static final String RW_ACTION = "read,write";
    public static final String READ_ACTION = "read";
    public static final String WRITE_ACTION = "write";

    private static final Logger _log =
            Logger.getLogger("org.glassfish.security.services");
    private static LocalStringManagerImpl localStrings =
            new LocalStringManagerImpl(SecureServiceAccessPermission.class);

    /**
     * Read action.
     */
    private final static int READ    = 0x1;

    /**
     * Write action.
     */
    private final static int WRITE   = 0x2;
    /**
     * All actions (read,write);
     */
    private final static int ALL     = READ|WRITE;

    private final static int NONE    = 0x0;

    private transient int mask;

    private transient boolean wildcard;

    private transient String path;
        
    private String target;  //not used for now
    private String actions; //not used for now

    /**
     * 
     * @param accessPermissionName  the permission name used inside the 'Secure' annotation of the protected service
     */
    public SecureServiceAccessPermission(String accessPermissionName) {
        this(accessPermissionName, null);
    }

    
    /**
     * 
     * @param accessPermissionName the permission name used inside the 'Secure' annotation of the protected service
     * @param actions  use null (not used for now) 
     */
    public SecureServiceAccessPermission(String accessPermissionName, String actions) {
        super(accessPermissionName, actions);
        this.actions = actions;
        initWildCardPath(accessPermissionName);
        init(getMask(actions));
    }

    /**
     * 
     * @param accessPermissionName the permission name used inside the 'Secure' annotation of the protected service
     * @param actions use null (not used for now)
     * @param targetName use null (not used for now)
     */
    public SecureServiceAccessPermission(String accessPermissionName, String actions,
            String targetName) {
        this(accessPermissionName, actions);
        this.target = targetName;
    }
        
    private void init(int mask)
    {

        if ((mask & ALL) != mask)
            throw new IllegalArgumentException(
                localStrings.getLocalString("perm.invalid.action", "invalid actions mask"));

        if (getName() == null)
            throw new NullPointerException(
                localStrings.getLocalString("perm.null.name", "name can't be null"));

        this.mask = mask;
    }

    //base on J2SE implementation
    private static int getMask(String actions) {

        int mask = NONE;

        if (actions == null) {
            return mask;
        }

        // Check against use of constants (used heavily within the JDK)
        if (actions.equalsIgnoreCase(READ_ACTION)) {
            return READ;
        } if (actions.equalsIgnoreCase(WRITE_ACTION)) {
            return WRITE;
        } else if (actions.equalsIgnoreCase(RW_ACTION)) {
            return READ|WRITE;
        }

        char[] a = actions.toCharArray();

        int i = a.length - 1;
        if (i < 0)
            return mask;

        while (i != -1) {
            char c;

            // skip whitespace
            while ((i!=-1) && ((c = a[i]) == ' ' ||
                               c == '\r' ||
                               c == '\n' ||
                               c == '\f' ||
                               c == '\t'))
                i--;

            // check for the known strings
            int matchlen;

            if (i >= 3 && (a[i-3] == 'r' || a[i-3] == 'R') &&
                          (a[i-2] == 'e' || a[i-2] == 'E') &&
                          (a[i-1] == 'a' || a[i-1] == 'A') &&
                          (a[i] == 'd' || a[i] == 'D'))
            {
                matchlen = 4;
                mask |= READ;

            } else if (i >= 4 && (a[i-4] == 'w' || a[i-4] == 'W') &&
                                 (a[i-3] == 'r' || a[i-3] == 'R') &&
                                 (a[i-2] == 'i' || a[i-2] == 'I') &&
                                 (a[i-1] == 't' || a[i-1] == 'T') &&
                                 (a[i] == 'e' || a[i] == 'E'))
            {
                matchlen = 5;
                mask |= WRITE;

            } else {
                // parse error
                throw new IllegalArgumentException(
                                localStrings.getLocalString(
                                                "perm.invalid.action", "invalid actions: {0}", actions));
            }

            // make sure we didn't just match the tail of a word
            // like "ackbarfaccept".  Also, skip to the comma.
            boolean seencomma = false;
            while (i >= matchlen && !seencomma) {
                switch(a[i-matchlen]) {
                case ',':
                    seencomma = true;
                    /*FALLTHROUGH*/
                case ' ': case '\r': case '\n':
                case '\f': case '\t':
                    break;
                default:
                    throw new IllegalArgumentException(
                        localStrings.getLocalString(
                                        "perm.invalid.actions", "invalid actions: {0}", actions));
                }
                i--;
            }

            // point i at the location of the comma minus one (or -1).
            i -= matchlen;
        }

        return mask;
    }

    @Override
    public String getActions() {
            return actions;
    }
        
    int getActionMask() {
        return mask;
    }

        
    public String getTarget() {
            return target;
    }
        
        @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;

        if ((obj == null) || (obj.getClass() != getClass()))
            return false;

        SecureServiceAccessPermission sp = (SecureServiceAccessPermission)obj;
        
        return twoStringEq(getName(), sp.getName()) &&
                   mask == sp.getActionMask() &&
                   twoStringEq(this.getTarget(), sp.getTarget());
    }

    //compare two strings
    private static boolean twoStringEq(String s1, String s2) {
            
            if (s1 == null && s2 == null)
                return true;
            
            if (s1 == null) {
                //s2 not null, s1 is null
                return false;
            } else 
                //s1 not null, 
                return s1.equals(s2);
    }
    
    @Override
    public int hashCode() {
        return getName().hashCode(); 
    }
        
        
    @Override
    public boolean implies(Permission p) {
        if ((p == null) || (p.getClass() != getClass()))
            return false;

        if (!(p instanceof SecureServiceAccessPermission))
            return false;
        
        SecureServiceAccessPermission that = (SecureServiceAccessPermission) p;

        
        boolean result = ((this.mask & that.mask) == that.mask) && nameImplies(that);
        
        if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE, "Implies for permission " + p + " return " + result);
        }
        
        return result;
    }
    
    private boolean nameImplies(SecureServiceAccessPermission that) {
        
        if (this.wildcard) {
            if (that.wildcard) {
                // one wildcard can imply another
                return that.path.startsWith(path);
            } else {
                // make sure ap.path is longer so a/b/* doesn't imply a/b
                return (that.path.length() > this.path.length()) &&
                    that.path.startsWith(this.path);
            }
        } else {
            if (that.wildcard) {
                // a non-wildcard can't imply a wildcard
                return false;
            }
            else {
                return this.path.equals(that.path);
            }
        }
    }
    

    private void initWildCardPath(String name)
    {
        if (name == null)
            throw new NullPointerException(
                        localStrings.getLocalString("perm.null.name", "name can't be null"));

        int len = name.length();

        if (len == 0) {
            throw new IllegalArgumentException(
                        localStrings.getLocalString("perm.empty.name", "name can't be empty"));
        }

        char last = name.charAt(len - 1);

        // Is wildcard or ends with "/*"?
        if (last == '*' && (len == 1 || name.charAt(len - 2) == '/')) {
            wildcard = true;
            if (len == 1) {
                path = "";
            } else {
                path = name.substring(0, len - 1);
            }
        } else {
            path = name;
        }
    }
    

    final String getCanonicalName() {
        return  getName();
    }

    public PermissionCollection newPermissionCollection() {
        return new SecurityAccessPermissionCollection(this.getClass(), _log, localStrings);
    }

}



final class SecurityAccessPermissionCollection extends PermissionCollection  {

        private static final long serialVersionUID = 2568719859057815986L;

        /**
         * Key is name, value is permission. All permission objects in collection
         * must be of the same type. Not serialized; see serialization section at
         * end of class.
         */
        private transient Map perms;

        /**
         * This is set to true if this SecurityAccessPermissionCollection
         * contains a BasicPermission with '*' as its permission name.
         * 
         * @see #serialPersistentFields
         */
        private boolean all_allowed;

        /**
         * The class to which all BasicPermissions in this SecurityAccessPermissionCollection
         * belongs.
         * 
         * @see #serialPersistentFields
         */
        private Class permClass;

        
        private Logger log;
        private LocalStringManagerImpl localStrings;

        public SecurityAccessPermissionCollection(Class clazz, Logger log, LocalStringManagerImpl localStrings) {
                perms = new HashMap(11);
                all_allowed = false;
                permClass = clazz;
                this.log = log;
                this.localStrings = localStrings;
        }

        /**
         * Adds a permission to the BasicPermissions. The key for the hash is
         * permission.path.
         * 
         * @param permission
         *            the Permission object to add.
         * 
         * @exception IllegalArgumentException
         *                - if the permission is not a BasicPermission, or if the
         *                permission is not of the same Class as the other
         *                permissions in this collection.
         * 
         * @exception SecurityException
         *                - if this SecurityAccessPermissionCollection object has been marked
         *                readonly
         */

        public void add(Permission permission) {
                if (!(permission instanceof SecureServiceAccessPermission))
                        throw new IllegalArgumentException("invalid permission: "
                                        + permission);
                if (isReadOnly())
                        throw new SecurityException(
                                        localStrings.getLocalString("perm.readonly",
                                        "attempt to add a Permission to a readonly PermissionCollection"));

                SecureServiceAccessPermission bp = (SecureServiceAccessPermission) permission;

                // make sure we only add new SecureServiceAccessPermissions of the same class
                // Also check null for compatibility with deserialized form from
                // previous versions.
                if (permClass == null) {
                        // adding first permission
                        permClass = bp.getClass();
                } else {
                        if (bp.getClass() != permClass)
                                throw new IllegalArgumentException(
                        localStrings.getLocalString(                                            
                                        "perm.invalid.perm", "invalid permission: {0}", permission));
                                                 
                }

                synchronized (this) {
                        perms.put(bp.getCanonicalName(), permission);
                }

                // No sync on all_allowed; staleness OK
                if (!all_allowed) {
                        if (bp.getCanonicalName().equals("*"))
                                all_allowed = true;
                }
        }

        /**
         * Check and see if this set of permissions implies the permissions
         * expressed in "permission".
         * 
         * @param p
         *            the Permission object to compare
         * 
         * @return true if "permission" is a proper subset of a permission in the
         *         set, false if not.
         */

        public boolean implies(Permission permission) {
                if (!(permission instanceof SecureServiceAccessPermission))
                        return false;

                SecureServiceAccessPermission bp = (SecureServiceAccessPermission) permission;

                // random subclasses of SecureServiceAccessPermission do not imply each other
                if (bp.getClass() != permClass)
                        return false;

                // short circuit if the "*" Permission was added
                if (all_allowed)
                        return true;

                // strategy:
                // Check for full match first. Then work our way up the
                // path looking for matches on a.b..*

                String path = bp.getCanonicalName();
                // System.out.println("check "+path);

                Permission x;

                synchronized (this) {
                        x = perms.get(path);
                }

                if (x != null) {
                        // we have a direct hit!
                        return x.implies(permission);
                }

                // work our way up the tree...
                int last, offset;

                offset = path.length() - 1;

                while ((last = path.lastIndexOf("/", offset)) != -1) {

                        path = path.substring(0, last + 1) + "*";
                        // System.out.println("check "+path);

                        synchronized (this) {
                                x = perms.get(path);
                        }

                        if (x != null) {
                                return x.implies(permission);
                        }
                        offset = last - 1;
                }

                if (log.isLoggable(Level.FINE))
                        log.log(Level.FINE, "pemission collection returns false");
                
                // we don't have to check for "*" as it was already checked
                // at the top (all_allowed), so we just return false
                return false;
        }

        /**
         * Returns an enumeration of all the SecureServiceAccessPermission objects in the
         * container.
         * 
         * @return an enumeration of all the SecureServiceAccessPermission objects.
         */

        public Enumeration elements() {
                // Convert Iterator of Map values into an Enumeration
                synchronized (this) {
                        return Collections.enumeration(perms.values());
                }
        }

        // Need to maintain serialization interoperability with earlier releases,
        // which had the serializable field:
        //
        // @serial the Hashtable is indexed by the SecureServiceAccessPermission name
        //
        // private Hashtable permissions;
        /**
         * @serialField
         *                  permissions java.util.Hashtable The SecureServiceAccessPermissions in
         *                  this SecurityAccessPermissionCollection. All SecureServiceAccessPermissions in
         *                  the collection must belong to the same class. The
         *                  Hashtable is indexed by the SecureServiceAccessPermission name; the
         *                  value of the Hashtable entry is the permission.
         * @serialField
         *                  all_allowed boolean This is set to true if
         *                  this SecurityAccessPermissionCollection contains a
         *                  SecureServiceAccessPermission with '*' as its permission name.
         * @serialField
         *                  permClass java.lang.Class The class to which all
         *                  SecureServiceAccessPermissions in this SecurityAccessPermissionCollection
         *                  belongs.
         */
        private static final ObjectStreamField[] serialPersistentFields = {
                        new ObjectStreamField("permissions", Hashtable.class),
                        new ObjectStreamField("all_allowed", Boolean.TYPE),
                        new ObjectStreamField("permClass", Class.class), };

        /**
         * @serialData Default fields.
         */
        /*
         * Writes the contents of the perms field out as a Hashtable for
         * serialization compatibility with earlier releases. all_allowed and
         * permClass unchanged.
         */
        private void writeObject(ObjectOutputStream out) throws IOException {
            // Don't call out.defaultWriteObject()
    
            // Copy perms into a Hashtable
            Hashtable permissions =
                    new Hashtable(perms.size()*2);
    
            synchronized (this) {
                permissions.putAll(perms);
            }
    
            // Write out serializable fields
            ObjectOutputStream.PutField pfields = out.putFields();
            pfields.put("all_allowed", all_allowed);
            pfields.put("permissions", permissions);
            pfields.put("permClass", permClass);
            out.writeFields();
        }

        /**
         * readObject is called to restore the state of the
         * SecurityAccessPermissionCollection from a stream.
         */
        private void readObject(java.io.ObjectInputStream in) throws IOException,
                        ClassNotFoundException {
            // Don't call defaultReadObject()

            // Read in serialized fields
            ObjectInputStream.GetField gfields = in.readFields();

            // Get permissions
            Hashtable permissions = (Hashtable) gfields
                            .get("permissions", null);
            perms = new HashMap(permissions.size() * 2);
            perms.putAll(permissions);

            // Get all_allowed
            all_allowed = gfields.get("all_allowed", false);

            // Get permClass
            permClass = (Class) gfields.get("permClass", null);

            if (permClass == null) {
                    // set permClass
                    Enumeration e = permissions.elements();
                    if (e.hasMoreElements()) {
                            Permission p = e.nextElement();
                            permClass = p.getClass();
                    }
            }
        }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy