Home | History | Annotate | Download | only in auth
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package org.apache.harmony.javax.security.auth;
     19 
     20 import java.io.IOException;
     21 import java.io.ObjectInputStream;
     22 import java.io.Serializable;
     23 import java.security.Permission;
     24 import java.security.PermissionCollection;
     25 import java.security.Principal;
     26 import java.util.Set;
     27 
     28 
     29 
     30 /**
     31  * Protects private credential objects belonging to a {@code Subject}. It has
     32  * only one action which is "read". The target name of this permission has a
     33  * special syntax:
     34  *
     35  * <pre>
     36  * targetName = CredentialClass {PrincipalClass &quot;PrincipalName&quot;}*
     37  * </pre>
     38  *
     39  * First it states a credential class and is followed then by a list of one or
     40  * more principals identifying the subject.
     41  * <p>
     42  * The principals on their part are specified as the name of the {@code
     43  * Principal} class followed by the principal name in quotes. For example, the
     44  * following file may define permission to read the private credentials of a
     45  * principal named "Bob": "com.sun.PrivateCredential com.sun.Principal \"Bob\""
     46  * <p>
     47  * The syntax also allows the use of the wildcard "*" in place of {@code
     48  * CredentialClass} or {@code PrincipalClass} and/or {@code PrincipalName}.
     49  *
     50  * @see Principal
     51  */
     52 public final class PrivateCredentialPermission extends Permission {
     53 
     54     private static final long serialVersionUID = 5284372143517237068L;
     55 
     56     // allowed action
     57     private static final String READ = "read"; //$NON-NLS-1$
     58 
     59     private String credentialClass;
     60 
     61     // current offset
     62     private transient int offset;
     63 
     64     // owners set
     65     private transient CredOwner[] set;
     66 
     67     /**
     68      * Creates a new permission for private credentials specified by the target
     69      * name {@code name} and an {@code action}. The action is always
     70      * {@code "read"}.
     71      *
     72      * @param name
     73      *            the target name of the permission.
     74      * @param action
     75      *            the action {@code "read"}.
     76      */
     77     public PrivateCredentialPermission(String name, String action) {
     78         super(name);
     79         if (READ.equalsIgnoreCase(action)) {
     80             initTargetName(name);
     81         } else {
     82             throw new IllegalArgumentException("auth.11"); //$NON-NLS-1$
     83         }
     84     }
     85 
     86     /**
     87      * Creates a {@code PrivateCredentialPermission} from the {@code Credential}
     88      * class and set of principals.
     89      *
     90      * @param credentialClass
     91      *            the credential class name.
     92      * @param principals
     93      *            the set of principals.
     94      */
     95     PrivateCredentialPermission(String credentialClass, Set<Principal> principals) {
     96         super(credentialClass);
     97         this.credentialClass = credentialClass;
     98 
     99         set = new CredOwner[principals.size()];
    100         for (Principal p : principals) {
    101             CredOwner element = new CredOwner(p.getClass().getName(), p.getName());
    102             // check for duplicate elements
    103             boolean found = false;
    104             for (int ii = 0; ii < offset; ii++) {
    105                 if (set[ii].equals(element)) {
    106                     found = true;
    107                     break;
    108                 }
    109             }
    110             if (!found) {
    111                 set[offset++] = element;
    112             }
    113         }
    114     }
    115 
    116     /**
    117      * Initialize a PrivateCredentialPermission object and checks that a target
    118      * name has a correct format: CredentialClass 1*(PrincipalClass
    119      * "PrincipalName")
    120      */
    121     private void initTargetName(String name) {
    122 
    123         if (name == null) {
    124             throw new NullPointerException("auth.0E"); //$NON-NLS-1$
    125         }
    126 
    127         // check empty string
    128         name = name.trim();
    129         if (name.length() == 0) {
    130             throw new IllegalArgumentException("auth.0F"); //$NON-NLS-1$
    131         }
    132 
    133         // get CredentialClass
    134         int beg = name.indexOf(' ');
    135         if (beg == -1) {
    136             throw new IllegalArgumentException("auth.10"); //$NON-NLS-1$
    137         }
    138         credentialClass = name.substring(0, beg);
    139 
    140         // get a number of pairs: PrincipalClass "PrincipalName"
    141         beg++;
    142         int count = 0;
    143         int nameLength = name.length();
    144         for (int i, j = 0; beg < nameLength; beg = j + 2, count++) {
    145             i = name.indexOf(' ', beg);
    146             j = name.indexOf('"', i + 2);
    147 
    148             if (i == -1 || j == -1 || name.charAt(i + 1) != '"') {
    149                 throw new IllegalArgumentException("auth.10"); //$NON-NLS-1$
    150             }
    151         }
    152 
    153         // name MUST have one pair at least
    154         if (count < 1) {
    155             throw new IllegalArgumentException("auth.10"); //$NON-NLS-1$
    156         }
    157 
    158         beg = name.indexOf(' ');
    159         beg++;
    160 
    161         // populate principal set with instances of CredOwner class
    162         String principalClass;
    163         String principalName;
    164 
    165         set = new CredOwner[count];
    166         for (int index = 0, i, j; index < count; beg = j + 2, index++) {
    167             i = name.indexOf(' ', beg);
    168             j = name.indexOf('"', i + 2);
    169 
    170             principalClass = name.substring(beg, i);
    171             principalName = name.substring(i + 2, j);
    172 
    173             CredOwner element = new CredOwner(principalClass, principalName);
    174             // check for duplicate elements
    175             boolean found = false;
    176             for (int ii = 0; ii < offset; ii++) {
    177                 if (set[ii].equals(element)) {
    178                     found = true;
    179                     break;
    180                 }
    181             }
    182             if (!found) {
    183                 set[offset++] = element;
    184             }
    185         }
    186     }
    187 
    188     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    189         ois.defaultReadObject();
    190         initTargetName(getName());
    191     }
    192 
    193     /**
    194      * Returns the principal's classes and names associated with this {@code
    195      * PrivateCredentialPermission} as a two dimensional array. The first
    196      * dimension of the array corresponds to the number of principals. The
    197      * second dimension defines either the name of the {@code PrincipalClass}
    198      * [x][0] or the value of {@code PrincipalName} [x][1].
    199      * <p>
    200      * This corresponds to the the target name's syntax:
    201      *
    202      * <pre>
    203      * targetName = CredentialClass {PrincipalClass &quot;PrincipalName&quot;}*
    204      * </pre>
    205      *
    206      * @return the principal classes and names associated with this {@code
    207      *         PrivateCredentialPermission}.
    208      */
    209     public String[][] getPrincipals() {
    210 
    211         String[][] s = new String[offset][2];
    212 
    213         for (int i = 0; i < s.length; i++) {
    214             s[i][0] = set[i].principalClass;
    215             s[i][1] = set[i].principalName;
    216         }
    217         return s;
    218     }
    219 
    220     @Override
    221     public String getActions() {
    222         return READ;
    223     }
    224 
    225     /**
    226      * Returns the class name of the credential associated with this permission.
    227      *
    228      * @return the class name of the credential associated with this permission.
    229      */
    230     public String getCredentialClass() {
    231         return credentialClass;
    232     }
    233 
    234     @Override
    235     public int hashCode() {
    236         int hash = 0;
    237         for (int i = 0; i < offset; i++) {
    238             hash = hash + set[i].hashCode();
    239         }
    240         return getCredentialClass().hashCode() + hash;
    241     }
    242 
    243     @Override
    244     public boolean equals(Object obj) {
    245         if (obj == this) {
    246             return true;
    247         }
    248 
    249         if (obj == null || this.getClass() != obj.getClass()) {
    250             return false;
    251         }
    252 
    253         PrivateCredentialPermission that = (PrivateCredentialPermission) obj;
    254 
    255         return credentialClass.equals(that.credentialClass) && (offset == that.offset)
    256                 && sameMembers(set, that.set, offset);
    257     }
    258 
    259     @Override
    260     public boolean implies(Permission permission) {
    261 
    262         if (permission == null || this.getClass() != permission.getClass()) {
    263             return false;
    264         }
    265 
    266         PrivateCredentialPermission that = (PrivateCredentialPermission) permission;
    267 
    268         if (!("*".equals(credentialClass) || credentialClass //$NON-NLS-1$
    269                 .equals(that.getCredentialClass()))) {
    270             return false;
    271         }
    272 
    273         if (that.offset == 0) {
    274             return true;
    275         }
    276 
    277         CredOwner[] thisCo = set;
    278         CredOwner[] thatCo = that.set;
    279         int thisPrincipalsSize = offset;
    280         int thatPrincipalsSize = that.offset;
    281         for (int i = 0, j; i < thisPrincipalsSize; i++) {
    282             for (j = 0; j < thatPrincipalsSize; j++) {
    283                 if (thisCo[i].implies(thatCo[j])) {
    284                     break;
    285                 }
    286             }
    287             if (j == thatCo.length) {
    288                 return false;
    289             }
    290         }
    291         return true;
    292     }
    293 
    294     @Override
    295     public PermissionCollection newPermissionCollection() {
    296         return null;
    297     }
    298 
    299     /**
    300      * Returns true if the two arrays have the same length, and every member of
    301      * one array is contained in another array
    302      */
    303     private boolean sameMembers(Object[] ar1, Object[] ar2, int length) {
    304         if (ar1 == null && ar2 == null) {
    305             return true;
    306         }
    307         if (ar1 == null || ar2 == null) {
    308             return false;
    309         }
    310         boolean found;
    311         for (int i = 0; i < length; i++) {
    312             found = false;
    313             for (int j = 0; j < length; j++) {
    314                 if (ar1[i].equals(ar2[j])) {
    315                     found = true;
    316                     break;
    317                 }
    318             }
    319             if (!found) {
    320                 return false;
    321             }
    322         }
    323         return true;
    324     }
    325 
    326     private static final class CredOwner implements Serializable {
    327 
    328         private static final long serialVersionUID = -5607449830436408266L;
    329 
    330         String principalClass;
    331 
    332         String principalName;
    333 
    334         // whether class name contains wildcards
    335         private transient boolean isClassWildcard;
    336 
    337         // whether pname contains wildcards
    338         private transient boolean isPNameWildcard;
    339 
    340         // Creates a new CredOwner with the specified Principal Class and Principal Name
    341         CredOwner(String principalClass, String principalName) {
    342             super();
    343             if ("*".equals(principalClass)) { //$NON-NLS-1$
    344                 isClassWildcard = true;
    345             }
    346 
    347             if ("*".equals(principalName)) { //$NON-NLS-1$
    348                 isPNameWildcard = true;
    349             }
    350 
    351             if (isClassWildcard && !isPNameWildcard) {
    352                 throw new IllegalArgumentException("auth.12"); //$NON-NLS-1$
    353             }
    354 
    355             this.principalClass = principalClass;
    356             this.principalName = principalName;
    357         }
    358 
    359         // Checks if this CredOwner implies the specified Object.
    360         boolean implies(Object obj) {
    361             if (obj == this) {
    362                 return true;
    363             }
    364 
    365             CredOwner co = (CredOwner) obj;
    366 
    367             if (isClassWildcard || principalClass.equals(co.principalClass)) {
    368                 if (isPNameWildcard || principalName.equals(co.principalName)) {
    369                     return true;
    370                 }
    371             }
    372             return false;
    373         }
    374 
    375         // Checks two CredOwner objects for equality.
    376         @Override
    377         public boolean equals(Object obj) {
    378             if (obj == this) {
    379                 return true;
    380             }
    381             if (obj instanceof CredOwner) {
    382                 CredOwner that = (CredOwner) obj;
    383                 return principalClass.equals(that.principalClass)
    384                     && principalName.equals(that.principalName);
    385             }
    386             return false;
    387         }
    388 
    389         // Returns the hash code value for this object.
    390         @Override
    391         public int hashCode() {
    392             return principalClass.hashCode() + principalName.hashCode();
    393         }
    394     }
    395 }
    396