blob: d62bb24e3082e18602084f77296c198dc4bff5d0 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/*
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
18package org.apache.harmony.javax.security.auth;
19
20import java.io.IOException;
21import java.io.ObjectInputStream;
22import java.io.Serializable;
23import java.security.Permission;
24import java.security.PermissionCollection;
25import java.security.Principal;
26import 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 */
52public 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}