1. /*
  2. * @(#)PrivateCredentialPermission.java 1.31 03/12/19
  3. *
  4. * Copyright 2004 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.31, 12/19/03
  83. */
  84. public final class PrivateCredentialPermission extends Permission {
  85. private static final long serialVersionUID = 5284372143517237068L;
  86. private static final CredOwner[] EMPTY_PRINCIPALS = new CredOwner[0];
  87. /**
  88. * @serial
  89. */
  90. private String credentialClass;
  91. /**
  92. * @serial The Principals associated with this permission.
  93. * The set contains elements of type,
  94. * <code>PrivateCredentialPermission.CredOwner</code>.
  95. */
  96. private Set principals; // ignored - kept around for compatibility
  97. private transient CredOwner[] credOwners;
  98. /**
  99. * @serial
  100. */
  101. private boolean testing = false;
  102. /**
  103. * Create a new <code>PrivateCredentialPermission</code>
  104. * with the specified <code>credentialClass</code> and Principals.
  105. */
  106. PrivateCredentialPermission(String credentialClass, Set principals) {
  107. super(credentialClass);
  108. this.credentialClass = credentialClass;
  109. synchronized(principals) {
  110. if (principals.size() == 0) {
  111. this.credOwners = EMPTY_PRINCIPALS;
  112. } else {
  113. this.credOwners = new CredOwner[principals.size()];
  114. int index = 0;
  115. Iterator i = principals.iterator();
  116. while (i.hasNext()) {
  117. Principal p = (Principal)i.next();
  118. this.credOwners[index++] = new CredOwner
  119. (p.getClass().getName(),
  120. p.getName());
  121. }
  122. }
  123. }
  124. }
  125. /**
  126. * Creates a new <code>PrivateCredentialPermission</code>
  127. * with the specified <code>name</code>. The <code>name</code>
  128. * specifies both a Credential class and a <code>Principal</code> Set.
  129. *
  130. * <p>
  131. *
  132. * @param name the name specifying the Credential class and
  133. * <code>Principal</code> Set. <p>
  134. *
  135. * @param actions the actions specifying that the Credential can be read.
  136. *
  137. * @throws IllegalArgumentException if <code>name</code> does not conform
  138. * to the correct syntax or if <code>actions</code> is not "read".
  139. */
  140. public PrivateCredentialPermission(String name, String actions) {
  141. super(name);
  142. if (!"read".equalsIgnoreCase(actions))
  143. throw new IllegalArgumentException
  144. (ResourcesMgr.getString("actions can only be 'read'"));
  145. init(name);
  146. }
  147. /**
  148. * Returns the Class name of the Credential associated with this
  149. * <code>PrivateCredentialPermission</code>.
  150. *
  151. * <p>
  152. *
  153. * @return the Class name of the Credential associated with this
  154. * <code>PrivateCredentialPermission</code>.
  155. */
  156. public String getCredentialClass() {
  157. return credentialClass;
  158. }
  159. /**
  160. * Returns the <code>Principal</code> classes and names
  161. * associated with this <code>PrivateCredentialPermission</code>.
  162. * The information is returned as a two-dimensional array (array[x][y]).
  163. * The 'x' value corresponds to the number of <code>Principal</code>
  164. * class and name pairs. When (y==0), it corresponds to
  165. * the <code>Principal</code> class value, and when (y==1),
  166. * it corresponds to the <code>Principal</code> name value.
  167. * For example, array[0][0] corresponds to the class name of
  168. * the first <code>Principal</code> in the array. array[0][1]
  169. * corresponds to the <code>Principal</code> name of the
  170. * first <code>Principal</code> in the array.
  171. *
  172. * <p>
  173. *
  174. * @return the <code>Principal</code> class and names associated
  175. * with this <code>PrivateCredentialPermission</code>.
  176. */
  177. public String[][] getPrincipals() {
  178. if (credOwners == null || credOwners.length == 0) {
  179. return new String[0][0];
  180. }
  181. String[][] pArray = new String[credOwners.length][2];
  182. for (int i = 0; i < credOwners.length; i++) {
  183. pArray[i][0] = credOwners[i].principalClass;
  184. pArray[i][1] = credOwners[i].principalName;
  185. }
  186. return pArray;
  187. }
  188. /**
  189. * Checks if this <code>PrivateCredentialPermission</code> implies
  190. * the specified <code>Permission</code>.
  191. *
  192. * <p>
  193. *
  194. * This method returns true if:
  195. * <p><ul>
  196. * <li> <i>p</i> is an instanceof PrivateCredentialPermission and <p>
  197. * <li> the target name for <i>p</i> is implied by this object's
  198. * target name. For example:
  199. * <pre>
  200. * [* P1 "duke"] implies [a.b.Credential P1 "duke"].
  201. * [C1 P1 "duke"] implies [C1 P1 "duke" P2 "dukette"].
  202. * [C1 P2 "dukette"] implies [C1 P1 "duke" P2 "dukette"].
  203. * </pre>
  204. * </ul>
  205. *
  206. * <p>
  207. *
  208. * @param p the <code>Permission</code> to check against.
  209. *
  210. * @return true if this <code>PrivateCredentialPermission</code> implies
  211. * the specified <code>Permission</code>, false if not.
  212. */
  213. public boolean implies(Permission p) {
  214. if (p == null || !(p instanceof PrivateCredentialPermission))
  215. return false;
  216. PrivateCredentialPermission that = (PrivateCredentialPermission)p;
  217. if (!impliesCredentialClass(credentialClass, that.credentialClass))
  218. return false;
  219. return impliesPrincipalSet(credOwners, that.credOwners);
  220. }
  221. /**
  222. * Checks two <code>PrivateCredentialPermission</code> objects for
  223. * equality. Checks that <i>obj</i> is a
  224. * <code>PrivateCredentialPermission</code>,
  225. * and has the same credential class as this object,
  226. * as well as the same Principals as this object.
  227. * The order of the Principals in the respective Permission's
  228. * target names is not relevant.
  229. *
  230. * <p>
  231. *
  232. * @param obj the object we are testing for equality with this object.
  233. *
  234. * @return true if obj is a <code>PrivateCredentialPermission</code>,
  235. * has the same credential class as this object,
  236. * and has the same Principals as this object.
  237. */
  238. public boolean equals(Object obj) {
  239. if (obj == this)
  240. return true;
  241. if (! (obj instanceof PrivateCredentialPermission))
  242. return false;
  243. PrivateCredentialPermission that = (PrivateCredentialPermission)obj;
  244. return (this.implies(that) && that.implies(this));
  245. }
  246. /**
  247. * Returns the hash code value for this object.
  248. *
  249. * @return a hash code value for this object.
  250. */
  251. public int hashCode() {
  252. return this.credentialClass.hashCode();
  253. }
  254. /**
  255. * Returns the "canonical string representation" of the actions.
  256. * This method always returns the String, "read".
  257. *
  258. * <p>
  259. *
  260. * @return the actions (always returns "read").
  261. */
  262. public String getActions() {
  263. return "read";
  264. }
  265. /**
  266. * Return a homogeneous collection of PrivateCredentialPermissions
  267. * in a <code>PermissionCollection</code>.
  268. * No such <code>PermissionCollection</code> is defined,
  269. * so this method always returns <code>null</code>.
  270. *
  271. * <p>
  272. *
  273. * @return null in all cases.
  274. */
  275. public PermissionCollection newPermissionCollection() {
  276. return null;
  277. }
  278. private void init(String name) {
  279. if (name == null || name.trim().length() == 0) {
  280. throw new IllegalArgumentException("invalid empty name");
  281. }
  282. ArrayList pList = new ArrayList();
  283. StringTokenizer tokenizer = new StringTokenizer(name, " ", true);
  284. String principalClass = null;
  285. String principalName = null;
  286. if (testing)
  287. System.out.println("whole name = " + name);
  288. // get the Credential Class
  289. credentialClass = tokenizer.nextToken();
  290. if (testing)
  291. System.out.println("Credential Class = " + credentialClass);
  292. if (tokenizer.hasMoreTokens() == false) {
  293. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  294. ("permission name [name] syntax invalid: "));
  295. Object[] source = {name};
  296. throw new IllegalArgumentException
  297. (form.format(source) + ResourcesMgr.getString
  298. ("Credential Class not followed by a " +
  299. "Principal Class and Name"));
  300. }
  301. while (tokenizer.hasMoreTokens()) {
  302. // skip delimiter
  303. tokenizer.nextToken();
  304. // get the Principal Class
  305. principalClass = tokenizer.nextToken();
  306. if (testing)
  307. System.out.println(" Principal Class = " + principalClass);
  308. if (tokenizer.hasMoreTokens() == false) {
  309. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  310. ("permission name [name] syntax invalid: "));
  311. Object[] source = {name};
  312. throw new IllegalArgumentException
  313. (form.format(source) + ResourcesMgr.getString
  314. ("Principal Class not followed by a Principal Name"));
  315. }
  316. // skip delimiter
  317. tokenizer.nextToken();
  318. // get the Principal Name
  319. principalName = tokenizer.nextToken();
  320. if (!principalName.startsWith("\"")) {
  321. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  322. ("permission name [name] syntax invalid: "));
  323. Object[] source = {name};
  324. throw new IllegalArgumentException
  325. (form.format(source) + ResourcesMgr.getString
  326. ("Principal Name must be surrounded by quotes"));
  327. }
  328. if (!principalName.endsWith("\"")) {
  329. // we have a name with spaces in it --
  330. // keep parsing until we find the end quote,
  331. // and keep the spaces in the name
  332. while (tokenizer.hasMoreTokens()) {
  333. principalName = principalName + tokenizer.nextToken();
  334. if (principalName.endsWith("\""))
  335. break;
  336. }
  337. if (!principalName.endsWith("\"")) {
  338. MessageFormat form = new MessageFormat
  339. (ResourcesMgr.getString
  340. ("permission name [name] syntax invalid: "));
  341. Object[] source = {name};
  342. throw new IllegalArgumentException
  343. (form.format(source) + ResourcesMgr.getString
  344. ("Principal Name missing end quote"));
  345. }
  346. }
  347. if (testing)
  348. System.out.println("\tprincipalName = '" + principalName + "'");
  349. principalName = principalName.substring
  350. (1, principalName.length() - 1);
  351. if (principalClass.equals("*") &&
  352. !principalName.equals("*")) {
  353. throw new IllegalArgumentException(ResourcesMgr.getString
  354. ("PrivateCredentialPermission Principal Class " +
  355. "can not be a wildcard (*) value if Principal Name " +
  356. "is not a wildcard (*) value"));
  357. }
  358. if (testing)
  359. System.out.println("\tprincipalName = '" + principalName + "'");
  360. pList.add(new CredOwner(principalClass, principalName));
  361. }
  362. this.credOwners = new CredOwner[pList.size()];
  363. pList.toArray((CredOwner[])this.credOwners);
  364. }
  365. private boolean impliesCredentialClass(String thisC, String thatC) {
  366. // this should never happen
  367. if (thisC == null || thatC == null)
  368. return false;
  369. if (testing)
  370. System.out.println("credential class comparison: " +
  371. thisC + "/" + thatC);
  372. if (thisC.equals("*"))
  373. return true;
  374. /**
  375. * XXX let's not enable this for now --
  376. * if people want it, we'll enable it later
  377. */
  378. /*
  379. if (thisC.endsWith("*")) {
  380. String cClass = thisC.substring(0, thisC.length() - 2);
  381. return thatC.startsWith(cClass);
  382. }
  383. */
  384. return thisC.equals(thatC);
  385. }
  386. private boolean impliesPrincipalSet(CredOwner[] thisP, CredOwner[] thatP) {
  387. // this should never happen
  388. if (thisP == null || thatP == null)
  389. return false;
  390. if (thatP.length == 0)
  391. return true;
  392. if (thisP.length == 0)
  393. return false;
  394. for (int i = 0; i < thisP.length; i++) {
  395. boolean foundMatch = false;
  396. for (int j = 0; j < thatP.length; j++) {
  397. if (thisP[i].implies(thatP[j])) {
  398. foundMatch = true;
  399. break;
  400. }
  401. }
  402. if (!foundMatch) {
  403. return false;
  404. }
  405. }
  406. return true;
  407. }
  408. /**
  409. * Reads this object from a stream (i.e., deserializes it)
  410. */
  411. private void readObject(java.io.ObjectInputStream s) throws
  412. java.io.IOException,
  413. ClassNotFoundException {
  414. s.defaultReadObject();
  415. // perform new initialization from the permission name
  416. if (getName().indexOf(" ") == -1 && getName().indexOf("\"") == -1) {
  417. // name only has a credential class specified
  418. credentialClass = getName();
  419. credOwners = EMPTY_PRINCIPALS;
  420. } else {
  421. // perform regular initialization
  422. init(getName());
  423. }
  424. }
  425. /**
  426. * @serial include
  427. */
  428. static class CredOwner implements java.io.Serializable {
  429. private static final long serialVersionUID = -5607449830436408266L;
  430. /**
  431. * @serial
  432. */
  433. String principalClass;
  434. /**
  435. * @serial
  436. */
  437. String principalName;
  438. CredOwner(String principalClass, String principalName) {
  439. this.principalClass = principalClass;
  440. this.principalName = principalName;
  441. }
  442. public boolean implies(Object obj) {
  443. if (obj == null || !(obj instanceof CredOwner))
  444. return false;
  445. CredOwner that = (CredOwner)obj;
  446. if (principalClass.equals("*") ||
  447. principalClass.equals(that.principalClass)) {
  448. if (principalName.equals("*") ||
  449. principalName.equals(that.principalName)) {
  450. return true;
  451. }
  452. }
  453. /**
  454. * XXX no code yet to support a.b.*
  455. */
  456. return false;
  457. }
  458. public String toString() {
  459. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  460. ("CredOwner:\n\tPrincipal Class = class\n\t" +
  461. "Principal Name = name"));
  462. Object[] source = {principalClass, principalName};
  463. return (form.format(source));
  464. }
  465. }
  466. }