1. /*
  2. * @(#)PrivateCredentialPermission.java 1.27 03/01/27
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.security.auth;
  8. import java.util.*;
  9. import java.text.MessageFormat;
  10. import java.security.Permission;
  11. import java.security.PermissionCollection;
  12. import java.security.Principal;
  13. import sun.security.util.ResourcesMgr;
  14. /**
  15. * This class is used to protect access to private Credentials
  16. * belonging to a particular <code>Subject</code>. The <code>Subject</code>
  17. * is represented by a Set of Principals.
  18. *
  19. * <p> The target name of this <code>Permission</code> specifies
  20. * a Credential class name, and a Set of Principals.
  21. * The only valid value for this Permission's actions is, "read".
  22. * The target name must abide by the following syntax:
  23. *
  24. * <pre>
  25. * CredentialClass {PrincipalClass "PrincipalName"}*
  26. * </pre>
  27. *
  28. * For example, the following permission grants access to the
  29. * com.sun.PrivateCredential owned by Subjects which have
  30. * a com.sun.Principal with the name, "duke". Note that although
  31. * this example, as well as all the examples below, do not contain
  32. * Codebase, SignedBy, or Principal information in the grant statement
  33. * (for simplicity reasons), actual policy configurations should
  34. * specify that information when appropriate.
  35. *
  36. * <pre>
  37. *
  38. * grant {
  39. * permission javax.security.auth.PrivateCredentialPermission
  40. * "com.sun.PrivateCredential com.sun.Principal \"duke\"",
  41. * "read";
  42. * };
  43. * </pre>
  44. *
  45. * If CredentialClass is "*", then access is granted to
  46. * all private Credentials belonging to the specified
  47. * <code>Subject</code>.
  48. * If "PrincipalName" is "*", then access is granted to the
  49. * specified Credential owned by any <code>Subject</code> that has the
  50. * specified <code>Principal</code> (the actual PrincipalName doesn't matter).
  51. * For example, the following grants access to the
  52. * a.b.Credential owned by any <code>Subject</code> that has
  53. * an a.b.Principal.
  54. *
  55. * <pre>
  56. * grant {
  57. * permission javax.security.auth.PrivateCredentialPermission
  58. * "a.b.Credential a.b.Principal "*"",
  59. * "read";
  60. * };
  61. * </pre>
  62. *
  63. * If both the PrincipalClass and "PrincipalName" are "*",
  64. * then access is granted to the specified Credential owned by
  65. * any <code>Subject</code>.
  66. *
  67. * <p> In addition, the PrincipalClass/PrincipalName pairing may be repeated:
  68. *
  69. * <pre>
  70. * grant {
  71. * permission javax.security.auth.PrivateCredentialPermission
  72. * "a.b.Credential a.b.Principal "duke" c.d.Principal "dukette"",
  73. * "read";
  74. * };
  75. * </pre>
  76. *
  77. * The above grants access to the private Credential, "a.b.Credential",
  78. * belonging to a <code>Subject</code> with at least two associated Principals:
  79. * "a.b.Principal" with the name, "duke", and "c.d.Principal", with the name,
  80. * "dukette".
  81. *
  82. * @version 1.27, 01/27/03
  83. */
  84. public final class PrivateCredentialPermission extends Permission {
  85. private static final long serialVersionUID = 5284372143517237068L;
  86. /**
  87. * @serial
  88. */
  89. private String credentialClass;
  90. /**
  91. * @serial The Principals associated with this permission.
  92. * The set contains elements of type,
  93. * <code>PrivateCredentialPermission.CredOwner</code>.
  94. */
  95. private Set principals;
  96. /**
  97. * @serial
  98. */
  99. private boolean testing = false;
  100. /**
  101. * Convenience function to create a PrivateCredentialPermission
  102. * from a Credential class String and a Set of Permissions.
  103. */
  104. static String buildTarget(String credentialClass, Set principals) {
  105. if (credentialClass == null ||
  106. principals == null ||
  107. principals.size() == 0)
  108. throw new IllegalArgumentException
  109. (ResourcesMgr.getString("invalid null input(s)"));
  110. String name = credentialClass;
  111. Iterator i = principals.iterator();
  112. while (i.hasNext()) {
  113. Principal p = (Principal)i.next();
  114. name += " " + p.getClass().getName() + " \"" + p.getName() + "\"";
  115. }
  116. return name;
  117. }
  118. Set getPrincipalSet() {
  119. return principals;
  120. }
  121. /**
  122. * Create a new <code>PrivateCredentialPermission</code>
  123. * with the specified <code>credentialClass</code>
  124. * and an empty set of Principals.
  125. */
  126. PrivateCredentialPermission(String credentialClass, Set principals) {
  127. super(credentialClass);
  128. this.credentialClass = credentialClass;
  129. this.principals = principals;
  130. }
  131. /**
  132. * Creates a new <code>PrivateCredentialPermission</code>
  133. * with the specified <code>name</code>. The <code>name</code>
  134. * specifies both a Credential class and a <code>Principal</code> Set.
  135. *
  136. * <p>
  137. *
  138. * @param name the name specifying the Credential class and
  139. * <code>Principal</code> Set. <p>
  140. *
  141. * @param actions the actions specifying that the Credential can be read.
  142. *
  143. * @throws IllegalArgumentException if <code>name</code> does not conform
  144. * to the correct syntax or if <code>actions</code> is not "read".
  145. */
  146. public PrivateCredentialPermission(String name, String actions) {
  147. super(name);
  148. if (!"read".equalsIgnoreCase(actions))
  149. throw new IllegalArgumentException
  150. (ResourcesMgr.getString("actions can only be 'read'"));
  151. init(name);
  152. }
  153. /**
  154. * Returns the Class name of the Credential associated with this
  155. * <code>PrivateCredentialPermission</code>.
  156. *
  157. * <p>
  158. *
  159. * @return the Class name of the Credential associated with this
  160. * <code>PrivateCredentialPermission</code>.
  161. */
  162. public String getCredentialClass() {
  163. return credentialClass;
  164. }
  165. /**
  166. * Returns the <code>Principal</code> classes and names
  167. * associated with this <code>PrivateCredentialPermission</code>.
  168. * The information is returned as a two-dimensional array (array[x][y]).
  169. * The 'x' value corresponds to the number of <code>Principal</code>
  170. * class and name pairs. When (y==0), it corresponds to
  171. * the <code>Principal</code> class value, and when (y==1),
  172. * it corresponds to the <code>Principal</code> name value.
  173. * For example, array[0][0] corresponds to the class name of
  174. * the first <code>Principal</code> in the array. array[0][1]
  175. * corresponds to the <code>Principal</code> name of the
  176. * first <code>Principal</code> in the array.
  177. *
  178. * <p>
  179. *
  180. * @return the <code>Principal</code> class and names associated
  181. * with this <code>PrivateCredentialPermission</code>.
  182. */
  183. public String[][] getPrincipals() {
  184. if (principals == null) {
  185. // this should never happen
  186. return new String[0][0];
  187. }
  188. String[][] pArray = new String[principals.size()][2];
  189. Iterator pIterator = principals.iterator();
  190. int i = 0;
  191. while (pIterator.hasNext()) {
  192. CredOwner co = (CredOwner)pIterator.next();
  193. pArray[i][0] = co.principalClass;
  194. pArray[i][1] = co.principalName;
  195. i++;
  196. }
  197. return pArray;
  198. }
  199. /**
  200. * Checks if this <code>PrivateCredentialPermission</code> implies
  201. * the specified <code>Permission</code>.
  202. *
  203. * <p>
  204. *
  205. * This method returns true if:
  206. * <p><ul>
  207. * <li> <i>p</i> is an instanceof PrivateCredentialPermission and <p>
  208. * <li> the target name for <i>p</i> is implied by this object's
  209. * target name. For example:
  210. * <pre>
  211. * [* P1 "duke"] implies [a.b.Credential P1 "duke"].
  212. * [C1 P1 "duke"] implies [C1 P1 "duke" P2 "dukette"].
  213. * [C1 P2 "dukette"] implies [C1 P1 "duke" P2 "dukette"].
  214. * </pre>
  215. * </ul>
  216. *
  217. * <p>
  218. *
  219. * @param p the <code>Permission</code> to check against.
  220. *
  221. * @return true if this <code>PrivateCredentialPermission</code> implies
  222. * the specified <code>Permission</code>, false if not.
  223. */
  224. public boolean implies(Permission p) {
  225. if (p == null || !(p instanceof PrivateCredentialPermission))
  226. return false;
  227. PrivateCredentialPermission that = (PrivateCredentialPermission)p;
  228. if (!impliesCredentialClass(credentialClass, that.getCredentialClass()))
  229. return false;
  230. return impliesPrincipalSet(principals, that.getPrincipalSet());
  231. }
  232. /**
  233. * Checks two <code>PrivateCredentialPermission</code> objects for
  234. * equality. Checks that <i>obj</i> is a
  235. * <code>PrivateCredentialPermission</code>,
  236. * and has the same credential class as this object,
  237. * as well as the same Principals as this object.
  238. * The order of the Principals in the respective Permission's
  239. * target names is not relevant.
  240. *
  241. * <p>
  242. *
  243. * @param obj the object we are testing for equality with this object.
  244. *
  245. * @return true if obj is a <code>PrivateCredentialPermission</code>,
  246. * has the same credential class as this object,
  247. * and has the same Principals as this object.
  248. */
  249. public boolean equals(Object obj) {
  250. if (obj == this)
  251. return true;
  252. if (! (obj instanceof PrivateCredentialPermission))
  253. return false;
  254. PrivateCredentialPermission that = (PrivateCredentialPermission)obj;
  255. return (this.implies(that) && that.implies(this));
  256. }
  257. /**
  258. * Returns the hash code value for this object.
  259. *
  260. * @return a hash code value for this object.
  261. */
  262. public int hashCode() {
  263. return this.getCredentialClass().hashCode();
  264. }
  265. /**
  266. * Returns the "canonical string representation" of the actions.
  267. * This method always returns the String, "read".
  268. *
  269. * <p>
  270. *
  271. * @return the actions (always returns "read").
  272. */
  273. public String getActions() {
  274. return "read";
  275. }
  276. /**
  277. * Return a homogeneous collection of PrivateCredentialPermissions
  278. * in a <code>PermissionCollection</code>.
  279. * No such <code>PermissionCollection</code> is defined,
  280. * so this method always returns <code>null</code>.
  281. *
  282. * <p>
  283. *
  284. * @return null in all cases.
  285. */
  286. public PermissionCollection newPermissionCollection() {
  287. return null;
  288. }
  289. private void init(String name) {
  290. principals = new HashSet();
  291. StringTokenizer tokenizer = new StringTokenizer(name, " ", true);
  292. String principalClass = null;
  293. String principalName = null;
  294. if (testing)
  295. System.out.println("whole name = " + name);
  296. // get the Credential Class
  297. credentialClass = tokenizer.nextToken();
  298. if (testing)
  299. System.out.println("Credential Class = " + credentialClass);
  300. if (tokenizer.hasMoreTokens() == false) {
  301. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  302. ("permission name [name] syntax invalid: "));
  303. Object[] source = {name};
  304. throw new IllegalArgumentException
  305. (form.format(source) + ResourcesMgr.getString
  306. ("Credential Class not followed by a " +
  307. "Principal Class and Name"));
  308. }
  309. while (tokenizer.hasMoreTokens()) {
  310. // skip delimiter
  311. tokenizer.nextToken();
  312. // get the Principal Class
  313. principalClass = tokenizer.nextToken();
  314. if (testing)
  315. System.out.println(" Principal Class = " + principalClass);
  316. if (tokenizer.hasMoreTokens() == false) {
  317. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  318. ("permission name [name] syntax invalid: "));
  319. Object[] source = {name};
  320. throw new IllegalArgumentException
  321. (form.format(source) + ResourcesMgr.getString
  322. ("Principal Class not followed by a Principal Name"));
  323. }
  324. // skip delimiter
  325. tokenizer.nextToken();
  326. // get the Principal Name
  327. principalName = tokenizer.nextToken();
  328. if (!principalName.startsWith("\"")) {
  329. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  330. ("permission name [name] syntax invalid: "));
  331. Object[] source = {name};
  332. throw new IllegalArgumentException
  333. (form.format(source) + ResourcesMgr.getString
  334. ("Principal Name must be surrounded by quotes"));
  335. }
  336. if (!principalName.endsWith("\"")) {
  337. // we have a name with spaces in it --
  338. // keep parsing until we find the end quote,
  339. // and keep the spaces in the name
  340. while (tokenizer.hasMoreTokens()) {
  341. principalName = principalName + tokenizer.nextToken();
  342. if (principalName.endsWith("\""))
  343. break;
  344. }
  345. if (!principalName.endsWith("\"")) {
  346. MessageFormat form = new MessageFormat
  347. (ResourcesMgr.getString
  348. ("permission name [name] syntax invalid: "));
  349. Object[] source = {name};
  350. throw new IllegalArgumentException
  351. (form.format(source) + ResourcesMgr.getString
  352. ("Principal Name missing end quote"));
  353. }
  354. }
  355. if (testing)
  356. System.out.println("\tprincipalName = '" + principalName + "'");
  357. principalName = principalName.substring
  358. (1, principalName.length() - 1);
  359. if (principalClass.equals("*") &&
  360. !principalName.equals("*")) {
  361. throw new IllegalArgumentException(ResourcesMgr.getString
  362. ("PrivateCredentialPermission Principal Class " +
  363. "can not be a wildcard (*) value if Principal Name " +
  364. "is not a wildcard (*) value"));
  365. }
  366. if (testing)
  367. System.out.println("\tprincipalName = '" + principalName + "'");
  368. CredOwner co = new CredOwner(principalClass, principalName);
  369. principals.add(co);
  370. }
  371. }
  372. private boolean impliesCredentialClass(String thisC, String thatC) {
  373. // this should never happen
  374. if (thisC == null || thatC == null)
  375. return false;
  376. if (testing)
  377. System.out.println("credential class comparison: " +
  378. thisC + "/" + thatC);
  379. if (thisC.equals("*"))
  380. return true;
  381. /**
  382. * XXX let's not enable this for now --
  383. * if people want it, we'll enable it later
  384. */
  385. /*
  386. if (thisC.endsWith("*")) {
  387. String cClass = thisC.substring(0, thisC.length() - 2);
  388. return thatC.startsWith(cClass);
  389. }
  390. */
  391. return thisC.equals(thatC);
  392. }
  393. private boolean impliesPrincipalSet(Set thisP, Set thatP) {
  394. // this should never happen
  395. if (thisP == null || thatP == null)
  396. return false;
  397. if (testing) {
  398. Iterator i = thisP.iterator();
  399. for (int j = 0; j < thisP.size(); j++) {
  400. CredOwner co = (CredOwner)i.next();
  401. System.out.println("this permission set [" + j + "]= " +
  402. co.toString());
  403. }
  404. }
  405. if (thatP.size() == 0)
  406. return true;
  407. if (thisP.size() == 0)
  408. return false;
  409. // make sure thatP "contains all" of the principals in thisP
  410. //
  411. // XXX we can not simply call containsAll on the sets
  412. // because we're not doing an "equals" --
  413. // we're doing an "implies"
  414. Iterator thisI = thisP.iterator();
  415. while (thisI.hasNext()) {
  416. CredOwner thisOwner = (CredOwner)thisI.next();
  417. Iterator thatI = thatP.iterator();
  418. boolean foundMatch = false;
  419. while (thatI.hasNext()) {
  420. CredOwner thatOwner = (CredOwner)thatI.next();
  421. if (thisOwner.implies(thatOwner)) {
  422. foundMatch = true;
  423. break;
  424. }
  425. }
  426. if (!foundMatch)
  427. return false;
  428. }
  429. return true;
  430. }
  431. /**
  432. * Reads this object from a stream (i.e., deserializes it)
  433. */
  434. private void readObject(java.io.ObjectInputStream s) throws
  435. java.io.IOException,
  436. ClassNotFoundException {
  437. s.defaultReadObject();
  438. // perform new initialization from the permission name
  439. if (getName().indexOf(" ") == -1 && getName().indexOf("\"") == -1) {
  440. // name only has a credential class specified
  441. credentialClass = getName();
  442. principals = new HashSet();
  443. } else {
  444. // perform regular initialization
  445. init(getName());
  446. }
  447. }
  448. /**
  449. * @serial include
  450. */
  451. static class CredOwner implements java.io.Serializable {
  452. private static final long serialVersionUID = -5607449830436408266L;
  453. /**
  454. * @serial
  455. */
  456. String principalClass;
  457. /**
  458. * @serial
  459. */
  460. String principalName;
  461. CredOwner(String principalClass, String principalName) {
  462. this.principalClass = principalClass;
  463. this.principalName = principalName;
  464. }
  465. public boolean implies(Object obj) {
  466. if (obj == null || !(obj instanceof CredOwner))
  467. return false;
  468. CredOwner that = (CredOwner)obj;
  469. if (principalClass.equals("*") ||
  470. principalClass.equals(that.principalClass)) {
  471. if (principalName.equals("*") ||
  472. principalName.equals(that.principalName)) {
  473. return true;
  474. }
  475. }
  476. /**
  477. * XXX no code yet to support a.b.*
  478. */
  479. return false;
  480. }
  481. public String toString() {
  482. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  483. ("CredOwner:\n\tPrincipal Class = class\n\t" +
  484. "Principal Name = name"));
  485. Object[] source = {principalClass, principalName};
  486. return (form.format(source));
  487. }
  488. }
  489. }