diff -r 86abbf8be0b1 netx/net/sourceforge/jnlp/policy/Policy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/policy/Policy.java Fri Jun 24 10:21:30 2011 -0400 @@ -0,0 +1,245 @@ +/* Policy.java -- Store information about a policy. +Copyright (C) 2010 Red Hat + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package net.sourceforge.jnlp.policy; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; + +/** + * This class is to store information about a policy. + * + * @author Andrew Su (asu@redhat.com, andrew.su@utoronto.ca) + * + */ +public class Policy { + private static int nextIndex = 1; + + /* who signed this policy */ + private String signedBy; + /* codebase that this policy affects */ + private URL codeBase; + /* list of permissions for this policy */ + private final ArrayList permissionList; + /* list of principals for this policy */ + private final ArrayList principalList; + + private String name; + private final String defaultName; + private boolean isDefaultName = true; + + /** + * Create an empty policy. + * + * @throws MalformedURLException + */ + public Policy() throws MalformedURLException { + this(null, null); + } + + /** + * Create a policy with default signed by and codebase. + * + * @param signedBy + * @param codeBase + * @throws MalformedURLException + */ + public Policy(String signedBy, String codeBase) + throws MalformedURLException { + + setCodeBase(codeBase); + setSignedBy(signedBy); + + permissionList = new ArrayList(); + principalList = new ArrayList(); + + defaultName = getNextName(); + } + + /** + * Set the codebase for this policy. + * + * @param codeBase String containing the codebase. + * @throws MalformedURLException + */ + public void setCodeBase(String codeBase) throws MalformedURLException { + URL url = null; + if (codeBase != null) { + codeBase = codeBase.trim(); + url = (codeBase.length() == 0) ? null : new URL(codeBase); + } + + this.codeBase = url; + } + + /** + * Set who signed this policy. + * + * @param signedBy String containing the name of the signer. + */ + public void setSignedBy(String signedBy) { + // FIXME: Check if signer is valid (Check keystore?) + + if (signedBy != null) + signedBy = signedBy.trim(); + + if (signedBy != null && signedBy.length() == 0) + signedBy = null; + + this.signedBy = signedBy; + } + + /** + * Get the codebase associated with this policy. + * + * @return + */ + public URL getCodeBase() { + return codeBase; + } + + /** + * Get the signer for this policy. + * + * @return + */ + public String getSignedBy() { + return signedBy; + } + + /** + * Add a permission to the list of permissions. + * + * @param permission permission to be added. + */ + public void addPermissions(PolicyPermission permission) { + if (permission == null) + throw new NullPointerException("Permission can not be null."); + permissionList.add(permission); + } + + /** + * Add a principal to the list for principals. + * + * @param principal principal to be added. + */ + public void addPrincipal(PolicyPrincipal principal) { + if (principal == null) { + throw new NullPointerException("Principal can not be null."); + } + this.principalList.add(principal); + } + + /** + * Remove a permission. + * + * @param permission + */ + public boolean removePermission(Object permission) { + return permissionList.remove(permission); + } + + /** + * Removes a principal. + * + * @param principal + * @return + */ + public boolean removePrincipal(Object principal) { + return principalList.remove(principal); + } + + /** + * Return the current list permissions. (Not a copy) + * + * @return + */ + public ArrayList getPermissions() { + return permissionList; + } + + /** + * Return the current list principals. (Not a copy) + * + * @return + */ + public ArrayList getPrincipals() { + return principalList; + } + + /** + * Get the display name of the policy. + * + * @return + */ + public String getName() { + if (this.name == null) { + return this.defaultName; + } + return this.name; + } + + /** + * Set the display name of the policy. + * + * @param name + * @return + */ + public boolean setName(String name) { + int i = 0; + + if (name == null) { + this.isDefaultName = true; + this.name = name; + return true; + } + + boolean valid = false; + while (i < name.length() + && (valid = PolicyFormatter.isValidNameChar(name.charAt(i++)))) + ; + if (valid && (!name.equals(defaultName) || !this.isDefaultName)) { + this.isDefaultName = false; + this.name = name; + } + + return valid; + } + + public String toString() { + return getName(); + } + + /** + * Check whether this is using a default name. + * + * @return + */ + public boolean isDefaultName() { + return this.isDefaultName; + } + + /** + * Generate the next available default name. + * + * @return + */ + public static String getNextName() { + return "CustomPolicy" + nextIndex++; + } +} diff -r 86abbf8be0b1 netx/net/sourceforge/jnlp/policy/PolicyFormatter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/policy/PolicyFormatter.java Fri Jun 24 10:21:30 2011 -0400 @@ -0,0 +1,903 @@ +/* PolicyFormatter.java -- Format and parse a policy file. +Copyright (C) 2010 Red Hat + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package net.sourceforge.jnlp.policy; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.ArrayList; + +import net.sourceforge.jnlp.util.FileUtils; + +/** + * This class can format and parse a policy file. + * + * @author Andrew Su (asu@redhat.com, andrew.su@utoronto.ca) + * + */ +public class PolicyFormatter { + + private Policy[] policies = null; + + /* keyword constants. */ + final String strGrant = "grant"; + final String strSignedBy = "signedby"; + final String strCodeBase = "codebase"; + final String strPrincipal = "principal"; + final String strPermission = "permission"; + final String strName = "#NAME:"; + + public PolicyFormatter() { + } + + public PolicyFormatter(Policy[] policies) { + setPolicies(policies); + } + + /** + * This sets the policy file to be used when formatting. This does NOT work + * with copies, but the actual objects passed it. Modifying the policies + * while formatting will yield undetermined results. + * + * @param policies + */ + public void setPolicies(Policy[] policies) { + if (policies == null) { + throw new IllegalArgumentException("Policies can not be null."); + } + this.policies = policies; + } + + /** + * This escapes all quotation marks. + * + * @param str + * @return + */ + public String escape(String str) { + return str.replaceAll("\"", "\\\\\""); + } + + /** + * This converts all the escaped quotation marks back to original unescaped + * version. + * + * @param str + * @return + */ + public String unescape(String str) { + return str.replaceAll("\\\\\"", "\""); + } + + /** + * Surround the given string in quotations. + * + * @param str string to quote + * @return Quoted String + */ + public String quote(String str) { + return "\"" + str + "\""; + } + + /** + * Remove quotations from the given string. + * + * @param str String to unquote. + * @return String with first leading and first trailing double quotes + * removed. + */ + public String unquote(String str) { + if (str.charAt(0) != '"' || str.charAt(str.length() - 1) != '"') { + // FIXME: Maybe we could just return the string as is? + throw new IllegalArgumentException("String is not quoted"); + } + + return str.substring(1, str.length() - 1); + } + + /** + * Convert given string to a policy file styled comment. + * + * @param str + * @return + */ + public String makeComment(String str) { + return "/* " + str + " */"; + } + + /** + * This method will return a string of a properly formatted policy file. + * + * @return + */ + public String format() { + if (policies == null) { + throw new NullPointerException("Must have policies."); + } + + StringBuffer sb = new StringBuffer(); + boolean havePrev = false; + + for (Policy p : policies) { + if (havePrev) { + sb.append("\n\n"); + } + + havePrev = false; + + if (p == null) + continue; // We'll just skip it if it is null. + if (!p.isDefaultName()) { + String name = p.getName(); + sb.append(makeComment(this.strName + name) + "\n"); + } + sb.append("grant"); + if (p.getSignedBy() != null) { + sb.append(" signedBy "); + sb.append(quote(escape(p.getSignedBy()))); + havePrev = true; + } + + if (p.getCodeBase() != null) { + if (havePrev) { + sb.append(","); + } + + sb.append(" codeBase "); + sb.append(quote(p.getCodeBase().toString())); + havePrev = true; + } + + // Add the principals. + for (PolicyPrincipal principal : p.getPrincipals()) { + if (!principal.isValid()) + continue; + + if (havePrev) { + sb.append(",\n "); + } + + sb.append(" principal "); + if (!principal.isDefaultName()) { + String name = principal.getCommentName(); + sb.append(makeComment(this.strName + name) + " "); + } + sb.append(principal.getClassName() + " "); + sb.append(quote(principal.getName())); + havePrev = true; + } + + havePrev = false; + sb.append(" {\n"); + + // Add permissions here. + for (PolicyPermission permission : p.getPermissions()) { + if (!permission.isValid()) + continue; + + if (havePrev) { + sb.append(";\n"); + } + sb.append(" permission "); + if (!permission.isDefaultName()) { + String name = permission.getName(); + sb.append(makeComment(this.strName + name) + " "); + } + sb.append(permission.getClassName()); + if (permission.getTarget() != null) { + sb.append(" " + quote(escape(permission.getTarget()))); + } + if (permission.getAction() != null) { + sb.append(", " + quote(permission.getAction())); + } + + if (permission.getSignedBy() != null) { + sb.append(", signedBy "); + sb.append(quote(escape(permission.getSignedBy()))); + } + + havePrev = true; + } + + sb.append(((havePrev) ? ";" : "") + "\n};"); + havePrev = true; + + } + + return sb.toString(); + } + + /** + * Check if character is a space, tab, newline, or return carriage. + * + * @param c + * @return + */ + private boolean isSpace(char c) { + switch (c) { + case ' ': + case '\t': + case '\r': + case '\n': + return true; + default: + return false; + } + } + + /** + * Check if the character is a valid class name character. (a-z,A-Z,0-9,.,_) + * + * @param c + * @return + */ + public static boolean isValidClassChar(char c) { + return isValidNameChar(c) || c == '_' || c == '.'; + } + + /** + * Given the input converted to lowercase, check if it contains the keyword + * "grant" at location i. + * + * @param input + * @param i + * @return + */ + private boolean findGrant(String input, int i) { + + if (!input.startsWith(strGrant, i)) { + return false; + } + + // We want grant followed by a space, newline, return carriage, or tab. + int newIndex = i + strGrant.length(); + if (newIndex > input.length()) { + return false; + } + char c = input.charAt(newIndex); + return isSpace(c) || c == '{'; + } + + /** + * Get the signedBy value if it begins at the current index. + * + * @param input + * @param i + * @return + */ + private String findSignedBy(final String input, final String lcaseInput, + int i) { + + int newIndex = i + strSignedBy.length(); + String result = null; + + if (lcaseInput.startsWith(strSignedBy, i) && newIndex < input.length() + && isSpace(input.charAt(newIndex))) { + result = getQuotedText(input, newIndex); + } + + return result; + } + + /** + * Get the codeBase value if it begins at the current index. + * + * @param input + * @param i + * @return + */ + private String findCodeBase(final String input, final String lcaseInput, + int i) { + + int newIndex = i + strCodeBase.length(); + String result = null; + + if (lcaseInput.startsWith(strCodeBase, i) && newIndex < input.length() + && isSpace(input.charAt(newIndex))) { + result = getQuotedText(input, newIndex); + } + + return result; + } + + /** + * This will grab text enclosed in quotation, and it also checks for escaped + * quotations. Will return null if no quotations found. Throws + * IllegalArgumentException if it finds opening quotes but no closing on the + * same line. + * + * @param input + * @param i + * @return String containing text enclosed with quotations, else null. + */ + private String getQuotedText(final String input, int i) { + char c = input.charAt(i); + + /* + * We are skipping all the spaces here until we reach the first non + * blank character. It would be ideal if we can keep track of how much + * spaces we skipped so that we don't have to re-check later. But this + * can be overcome by doing an "indexOf" search for the value returned. + */ + while (i < input.length() && isSpace(c = input.charAt(i))) { + i++; + } + + String result = null; + if (c == '\"') { + /* + * Count the number of consecutive backslashes. If we have an odd + * number of backslashes, followed by double quotes that means it's + * an escaped character. + */ + int bs = 0; + int initial = i; + while (++i < input.length() + && ((c = input.charAt(i)) != '\"' || bs % 2 != 0)) { + if (c == '\r' || c == '\n') { + throw new IllegalArgumentException( + "End of line reached, could not find closing quotations."); + } else if (c == '\\') { + bs++; + } else { + bs = 0; + } + } + if (i++ > input.length()) { + throw new IllegalArgumentException( + "End of input reached, could not find closing quotations."); + } + + result = input.substring(initial, i); + } + + return result; + } + + /** + * Get all the characters that make up a class name. NOTE: It may not be a + * valid class name. For example "1...312.something.MyClass" + * + * @param input + * @param i + * @return + */ + private String getClassName(String input, int i) { + /* + * FIXME: To ensure a valid class name we look for must start with + * alphabet + */ + i = skipSpace(input, i); + + String fullClassName = null; + if (i < input.length()) { + + int initial = i; + while (i < input.length() && isValidClassChar(input.charAt(i))) { + i++; + } + fullClassName = input.substring(initial, i); + if (fullClassName.length() == 0) { + fullClassName = null; + } + } + + return fullClassName; + } + + /** + * Check if we detect the "principal" keyword. + * + * @param input + * @param i + * @return + */ + private boolean checkIfPrincipal(String input, int i) { + int index = input.indexOf(strPrincipal, i); + if (!input.startsWith(strPrincipal, i)) { + return false; + } + + // We want grant followed by a space, newline, return carriage, or tab. + int newIndex = index + strPrincipal.length(); + if (newIndex > input.length()) { + return false; + } + + return isSpace(input.charAt(newIndex)); + } + + /** + * Check if we detect the "permission" keyword. + * + * @param input + * @param i + * @return + */ + private boolean checkIfPermission(String input, int i) { + int index = input.indexOf(strPermission, i); + if (!input.startsWith(strPermission, i)) { + return false; + } + + // We want grant followed by a space, newline, return carriage, or tab. + int newIndex = index + strPermission.length(); + if (newIndex > input.length()) { + return false; + } + + return isSpace(input.charAt(newIndex)); + } + + /** + * Get the principal name. + * + * @param input + * @param i + * @return + */ + private String getPrincipalName(String input, int i) { + i = skipSpace(input, i); + + String fullClassName = null; + if (i < input.length()) { + fullClassName = getQuotedText(input, i); + } + return fullClassName; + } + + /** + * Get the permission's target. + * + * @param input String to search in. + * @param i offset to start searching from. + * @return + */ + private String getPermissionTarget(String input, int i) { + String result = null; + if (i < input.length()) { + i = skipSpace(input, i); + result = getQuotedText(input, i); + } + + return result; + } + + /** + * Get the next index which is a non-space character. + * + * @param input + * @param i + * @return + */ + private int skipSpace(final String input, int i) { + while (i < input.length() && isSpace(input.charAt(i))) + i++; + return i; + } + + /** + * Check if character is valid for names. [a-zA-Z0-9] + * + * @param c + * @return + */ + public static boolean isValidNameChar(char c) { + + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9'); + + } + + /** + * Get comment name from input. + * + * @param input + * @param i + * @return name or null if not found. + */ + private String parseNameComment(String input, int i) { + StringBuffer buf = new StringBuffer(); + if (input.startsWith(this.strName, i)) { + i += this.strName.length(); + char c; + while (isValidNameChar(c = input.charAt(i++))) { + buf.append(c); + } + } + if (buf.length() > 0) + return buf.toString(); + else + return null; + } + + /** + * Parse a given file for policies. + * + * @param file + * @return + * @throws IOException + */ + public Policy[] parse(File file) throws IOException { + String content = FileUtils.readContentFromFile(file); + return parse(content); + } + + /** + * parse a given String for policies. + * + * @param input + * @return + * @throws MalformedURLException Codebase specified in file is invalid. + */ + public Policy[] parse(final String input) throws MalformedURLException { + String lcaseInput = input.toLowerCase(); + + boolean foundGrant = false; + boolean inPolicy = false; + boolean signedBy = false; + boolean codeBase = false; + boolean principalFound = false; + boolean permissionFound = false; + boolean permissionClassFound = false; + boolean permissionTargetFound = false; + boolean action = false; + boolean reachedEnd = false; + boolean commaFound = false; + boolean fslashFound = false; + boolean asteriskFound = false; + boolean comment = false; + + boolean havePrev = false; + + String name = null; + + ArrayList policies = new ArrayList(); + Policy p = null; + String permissionClass = null; + String permissionTarget = null; + String permissionSignedBy = null; + String permissionAction = null; + int prevIndex = -1; + for (int index = 0; index < input.length();) { + if (prevIndex == index) { + // This shouldn't happen...but just in case, since we don't + // increment index normally.. + throw new InternalError("Infinite loop detected."); + } + prevIndex = index; + char c = input.charAt(index); + + /* + * This is to grab the comment name where appropriate. + */ + if (c != '*' && c != '/') { + asteriskFound = false; + fslashFound = false; + if (comment && name == null) { + if (!inPolicy) { + if (!foundGrant) { + name = parseNameComment(input, index); + } else if (principalFound) { + name = parseNameComment(input, index); + } + } else { + // This will mean we are right between keyword + // "permission" and the class associated with it. + if (permissionFound && !permissionClassFound + && !permissionTargetFound) { + name = parseNameComment(input, index); + } + } + } + } + + /* + * This is a list of characters that have significant meaning. + */ + switch (c) { + case '*': + if (fslashFound && !comment) { + comment = true; + fslashFound = false; + } else { + asteriskFound = true; + } + index++; + continue; + case '/': + if (asteriskFound && comment) { + comment = false; + asteriskFound = false; + } else { + fslashFound = true; + } + index++; + continue; + case ',': + if (!comment) { + if (commaFound || principalFound) { + throw new IllegalArgumentException( + "Unecpected ',' detected."); + } + commaFound = true; + } + index++; + continue; + case ';': + if (!comment) { + if (!permissionFound && !reachedEnd) { + throw new IllegalArgumentException( + "Unexpected ';' detected."); + } + if (permissionTargetFound) { + if (commaFound) { + throw new IllegalArgumentException( + "Could not find ACTION or signedBy. [Unexpected end of line with ';']"); + } + PolicyPermission policyPermission = new PolicyPermission( + permissionClass, permissionTarget, + permissionAction, permissionSignedBy); + policyPermission.setName(name); + p.addPermissions(policyPermission); + permissionFound = false; + permissionClassFound = false; + permissionTargetFound = false; + commaFound = false; + action = false; + name = null; + } + if (reachedEnd) { + // Reaching this condition means that we have + // reached the end and have a valid policy file up + // to this point. Reset everything except index. + + foundGrant = false; + reachedEnd = false; + signedBy = false; + commaFound = false; + havePrev = false; + + permissionClass = null; + permissionTarget = null; + permissionSignedBy = null; + permissionAction = null; + + policies.add(p); + p = null; + } + } + + case '\n': + case '\r': + case '\t': + case ' ': + index++; + continue; + case '{': + if (!comment) { + if (commaFound) { + throw new IllegalArgumentException( + "Unexpected ',' detected."); + } + if (inPolicy) { + throw new IllegalArgumentException( + "Unexpected '{' detected."); + } + havePrev = false; + signedBy = false; + codeBase = false; + inPolicy = true; + } + index++; + continue; + case '}': + if (!comment) { + if (!inPolicy || permissionClassFound) { + throw new IllegalArgumentException( + "Unexpected '}' detected."); + } + inPolicy = false; + reachedEnd = true; + } + index++; + continue; + } + + if (comment) { + index++; + continue; + } + + if (!inPolicy) { + if (!foundGrant) { + foundGrant = findGrant(lcaseInput, index); + if (!foundGrant) { + // Suppose to be first keyword, but not found. + throw new IllegalArgumentException( + "Missing GRANT keyword (case insensitive)."); + } + if (p != null) { // This should never happen. + throw new IllegalArgumentException( + "Something has gone horribly wrong."); + } + p = new Policy(); + p.setName(name); + name = null; + index += strGrant.length(); + } else { + /* + * We are still not in the body, check for signedBy, + * codebase, and principal. Also need to check for comma + * between each entry. + */ + String strSignedBy = findSignedBy(input, lcaseInput, index); + if (!signedBy + && strSignedBy != null + && ((havePrev && commaFound) || (!havePrev && !commaFound))) { + commaFound = false; + signedBy = true; + index = input.indexOf(strSignedBy, index) + + strSignedBy.length(); + p.setSignedBy(unescape(unquote(strSignedBy))); + havePrev = true; + continue; // We can create an else block and nest the + // next condition in it, but it is messy. + } else if (signedBy && strSignedBy != null) { + throw new IllegalArgumentException( + "Multiple signedBy detected."); + } + + String strCodeBase = findCodeBase(input, lcaseInput, index); + if (!codeBase && strCodeBase != null) { + commaFound = false; + codeBase = true; + index = input.indexOf(strCodeBase, index) + + strCodeBase.length(); + p.setCodeBase(unescape(unquote(strCodeBase))); + havePrev = true; + continue; + } else if (codeBase && strCodeBase != null) { + throw new IllegalArgumentException( + "Multiple codeBase detected."); + } + + /* + * If we get here, then we either have a principal or + * something illegal, so we will check for principal first + * then a catch all to throw error if we find something we + * are not expecting. + */ + if (!principalFound && checkIfPrincipal(lcaseInput, index)) { + commaFound = false; + index += strPrincipal.length(); + + principalFound = true; + continue; + } + + if (principalFound) { + String principalClass = getClassName(input, index); + if (principalClass == null) { + throw new IllegalArgumentException( + "Could not find principal class."); + } + index = input.indexOf(principalClass, index) + + principalClass.length(); + + String principalName = getPrincipalName(input, index); + if (principalName == null) { + throw new IllegalArgumentException( + "Principal name not found."); + } + index = input.indexOf(principalName, index) + + principalName.length(); + principalName = unescape(unquote(principalName)); + // We have a valid principal. + PolicyPrincipal principal = new PolicyPrincipal( + principalClass, principalName); + p.addPrincipal(principal); + principal.setCommentName(name); + havePrev = true; + principalFound = false; + name = null; + continue; + } + + // If we reach here then it means we found something that is + // invalid. Such as unknown keywords. + throw new IllegalArgumentException( + "Invalid character/keyword '" + c + + "' detected at index " + index + "."); + } + } else { // in the body "{ ... }" + // Only permissions are valid here. + if (!permissionFound && checkIfPermission(lcaseInput, index)) { + permissionFound = true; + index += strPermission.length(); + continue; + } + + if (permissionFound && !permissionClassFound) { + permissionClass = getClassName(input, index); + if (permissionClass == null) { + throw new IllegalArgumentException( + "Could not find principal class."); + } + index = input.indexOf(permissionClass, index) + + permissionClass.length(); + permissionClassFound = true; + continue; + } + + if (permissionClassFound && !permissionTargetFound) { + permissionTarget = getPermissionTarget(input, index); + if (permissionTarget == null) { + throw new IllegalArgumentException("Target required"); + } + + index = input.indexOf(permissionTarget, index) + + permissionTarget.length(); + permissionTarget = unescape(unquote(permissionTarget)); + permissionTargetFound = true; + continue; + } + + if (permissionTargetFound && commaFound) { + // Check for Action or signedBy whichever comes first. + commaFound = false; + + permissionSignedBy = findSignedBy(input, lcaseInput, index); + if (!signedBy && permissionSignedBy != null) { + signedBy = true; + index = input.indexOf(permissionSignedBy, index) + + permissionSignedBy.length(); + permissionSignedBy = unescape(unquote(permissionSignedBy)); + continue; // We can create an else block and nest the + // next condition in it, but it is messy. + } else if (signedBy && permissionSignedBy != null) { + throw new IllegalArgumentException( + "Multiple signedBy detected."); + } + + permissionAction = getQuotedText(input, index); + if (!signedBy && !action && permissionAction != null) { + action = true; + index += permissionAction.length(); + permissionAction = unescape(unquote(permissionAction)); + continue; + } else if ((signedBy || action) && permissionAction != null) { + throw new IllegalArgumentException( + "signedBy found before ACTION or multiple ACTION found."); + } + + throw new IllegalArgumentException( + "Unexpected input from policy file."); + } + + throw new IllegalArgumentException(""); + } + } + + if (foundGrant) { + throw new IllegalArgumentException("Missing body."); + } + + return policies.toArray(new Policy[policies.size()]); + } +} diff -r 86abbf8be0b1 netx/net/sourceforge/jnlp/policy/PolicyPermission.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/policy/PolicyPermission.java Fri Jun 24 10:21:30 2011 -0400 @@ -0,0 +1,836 @@ +/* PolicyPermission.java -- Store information about policy's permission. +Copyright (C) 2010 Red Hat + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package net.sourceforge.jnlp.policy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +/** + * This class represents a permission in a policy file. + * + * @author Andrew Su (asu@redhat.com, andrew.su@utoronto.ca) + * + */ +public final class PolicyPermission { + private static int nextIndex = 1; + + /* This is the class which all permission classes should extend. */ + private static final Class c; + private static final String canonClassName; + + static { + try { + c = Class.forName("java.security.Permission"); + } catch (ClassNotFoundException e) { + // This should never happen.. + throw new RuntimeException( + "We can't find the permission class. Die here."); + } + canonClassName = c.getCanonicalName(); + } + + public static enum TARGET { + /* Audio Permission */ + PLAY("play"), + RECORD("record"), + + /* Auth Permission (Does not use ACTION) */ + DO_AS("doAs"), + DO_AS_PRIVILEGED("doAsPrivileged"), + GET_SUBJECT("getSubject"), + GET_SUBJECT_FROM_DOMAIN_COMBINER("getSubjectFromDomainCombiner"), + SET_READ_ONLY("setReadOnly"), + MODIFY_PRINCIPALS("modifyPrincipals"), + MODIFY_PUBLIC_CREDENTIALS("modifyPublicCredentials"), + MODIFY_PRIVATE_CREDENTIALS("modifyPrivateCredentials"), + REFRESH_CREDENTIAL("refreshCredential"), + DESTROY_CREDENTIAL("destroyCredential"), + CREATE_LOGIN_CONTEXT("createLoginContext"), // This one has special + // syntax. + GET_LOGIN_CONFIGURATION("getLoginConfiguration"), + SET_LOGIN_CONFIGURATION("setLoginConfiguration"), + REFRESH_LOGIN_CONFIGURATION("refreshLoginConfiguration"), + + /* AWT Permission (Does not use ACTION) */ + ACCESS_CLIPBOARD("accessClipboard"), + ACCESS_EVENT_QUEUE("accessEventQueue"), + CREATE_ROBOT("createRobot"), + LISTEN_TO_ALL_AWT_EVENTS("listenToAllAWTEvents"), + READ_DISPLAY_PIXELS("readDisplayPixels"), + SHOW_WINDOW_WITHOUT_WARNING_BANNER("showWindowWithoutWarningBanner"), + + /* File Permission */ + ALL_FILES("<>"), + + /* Logging Permission (Does not use ACTION) */ + CONTROL("control"), + + /* Net Permission (Does not use ACTION) */ + SET_DEFAULT_AUTHENTICATOR("setDefaultAuthenticator"), + REQUEST_PASSWORD_AUTHENTICATION("requestPasswordAuthentication"), + SPECIFY_STREAM_HANDLER("specifyStreamHandler"), + + /* Reflect Permission */ + SUPRESS_ACCESS_CHECKS("supressAccessChecks"), + + /* Runtime Permission (Does not use ACTION) */ + CREATE_CLASS_LOADER("createClassLoader"), + GET_CLASS_LOADER("getClassLoader"), + SET_CONTEXT_CLASSLOADER("setContextClassLoader"), + SET_SECURITY_MANAGER("setSecurityManager"), + CREATE_SECURITY_MANAGER("createSecurityManager"), + EXIT_VM("exitVM"), + SHUTDOWN_HOOKS("shutdownHooks"), + SET_FACTORY("setFactory"), + SET_IO("setIO"), + MODIFY_THREAD("modifyThread"), + STOP_THREAD("stopThread"), + MODIFY_THREAD_GROUP("modifyThreadGroup"), + GET_PROTECTION_DOMAIN("getProtectionDomain"), + READ_FILE_DESCRIPTOR("readFileDescriptor"), + WRITE_FILE_DESCRIPTOR("writeFileDescriptor"), + LOAD_LIBRARY("loadLibrary"), // This one has special syntax. + // (loadLibrary.) + ACCESS_CLASS_IN_PACKAGE("accessClassInPackage"), // This one has special + // syntax. + DEFINE_CLASS_IN_PACKAGE("defineClassInPackage"), // This one has special + // syntax. + ACCESS_DECLARED_MEMBERS("accessDeclaredMembers"), + QUEUE_PRINT_JOB("queuePrintJob"), + // SELECT_PROVIDER("selectProvider"), + // CHARSET_PROVIDER("charsetProvider"), + + /* Security Permission (Does not use ACTION) */ + CREATE_ACCESS_CONTROL_CONTEXT("createAccessControlContext"), + GET_DOMAIN_COMBINER("getDomainCombiner"), + GET_POLICY("getPolicy"), + SET_POLICY("getPolicy"), + GET_PROPERTY("getProperty"), // This one has special syntax. + SET_PROPERTY("setProperty"), // This one has special syntax. + INSERT_PROVIDER("insertProvider"), // This one has special syntax. + REMOVE_PROVIDER("removeProvider"), // This one has special syntax. + SET_SYSTEM_SCOPE("setSystemScope"), + SET_IDENTITY_PUBLIC_KEY("setIdentityPublicKey"), + SET_IDENTITY_INFO("setIdentityInfo"), + ADD_IDENTITY_CERTIFICATE("addIdentityCertificate"), + REMOVE_IDENTITY_CERTIFICATE("removeIdentityCertificate"), + PRINT_IDENTITY("printIdentity"), + // The following three has special syntax. + CLEAR_PROVIDER_PROPERTIES("clearProviderProperties"), + PUT_PROVIDER_PROPERTY("putProviderProperty"), + REMOVE_PROVIDER_PROPERTY("removeProviderProperty"), + GET_SIGNER_PRIVATE_KEY("getSignerPrivateKey"), + SET_SIGNER_KEY_PAIR("setSignerKeyPair"), + + /* Serializable Permission (Does not use ACTION) */ + ENABLE_SUBCLASS_IMPLEMENTATION("enableSubclassImplementation"), + ENABLE_SUBSTITUTION("enableSubstitution"), + + /* SQL Permission */ + SET_LOG("setLog"), + + /* SSL Permission (Does not use ACTION) */ + SET_HOST_NAME_VERIFIER("setHostnameVerifier"), + GET_SSL_SESSION_CONTEXT("getSSLSessionContext"); + + private final String name; + + private TARGET(String name) { + this.name = name; + } + + public String toString() { + return name; + } + } + + public static enum ACTION { + READ("read"), + WRITE("write"), + EXECUTE("execute"), + DELETE("delete"), + INITIATE("initiate"), + ACCEPT("accept"), + CONNECT("connect"), + LISTEN("listen"), + RESOLVE("resolve"); + + private final String name; + + private ACTION(String name) { + this.name = name; + } + + public String toString() { + return name; + } + } + + public static enum PERMISSION { + ALL_PERMISSION("AllPermission", false, false, false, "java.security.AllPermission"), + AUDIO_PERMISSION("AudioPermission", true, false, false, "javax.sound.sampled.AudioPermission"), + AUTH_PERMISSION("AuthPermission", true, false, false, "javax.security.auth.AuthPermission"), + AWT_PERMISSION("AWTPermission", true, false, false, "java.awt.AWTPermission"), + DELEGATION_PERMISSION("DelegationPermission", false, false, true, "javax.security.auth.kerberos.DelegationPermission"), + FILE_PERMISSION("FilePermission", true, true, true, "java.io.FilePermission"), + LOGGING_PERMISSION("LoggingPermission", true, false, false, "java.util.logging.LoggingPermission"), + NET_PERMISSION("NetPermission", true, false, false, "java.net.NetPermission"), + PRIVATE_CREDENTIAL_PERMISSION("PrivateCredentialPermission", false, true, true, "javax.security.auth.PrivateCredentialPermission"), + PROPERTY_PERMISSION("PropertyPermission", false, true, true, "java.util.PropertyPermission"), + REFLECT_PERMISSION("ReflectPermission", true, false, false, "java.lang.reflect.ReflectPermission"), + RUNTIME_PERMISSION("RuntimePermission", true, false, false, "java.lang.RuntimePermission"), + SECURITY_PERMISSION("SecurityPermission", true, false, false, "java.security.SecurityPermission"), + SERIALIZABLE_PERMISSION("SerializablePermission", true, false, false, "java.io.SerializablePermission"), + SERVICE_PERMISSION("ServicePermission", false, true, true, "javax.security.auth.kerberos.ServicePermission"), + SOCKET_PERMISSION("SocketPermission", false, true, true, "java.net.SocketPermission"), + SQL_PERMISSION("SQLPermission", true, false, false, "java.sql.SQLPermission"), + SSL_PERMISSION("SSLPermission", true, false, false, "javax.net.ssl.SSLPermission"), + // UNRESOLVED_PERMISSION("UnresolvedPermission", true, false, false, + // "java.security.UnresolvedPermission") + ; + + private final String name; + private final String fullClassName; + private final boolean haveTargets; // True if and only if we have TARGET + private final boolean haveActions; // True if and only if we have ACTION + private final boolean manualTarget; // True if the user should be able + // to pop in a custom target + + /* FIXME: Use final? */ + private ArrayList targets; + private ArrayList actions; + + private PERMISSION(String name, boolean haveTargets, + boolean haveActions, boolean manualTarget, String fullClassName) { + this.name = name; + this.haveTargets = haveTargets; + this.haveActions = haveActions; + this.manualTarget = manualTarget; + this.fullClassName = fullClassName; + + if (haveTargets()) { + targets = new ArrayList(); + } + + if (haveActions()) { + actions = new ArrayList(); + } + } + + /** + * Set the default TARGETs this permission has. + * + * @param c + */ + private void setTargets(Collection c) { + if (!haveTargets()) { + throw new IllegalArgumentException( + "This permission should not contain targets."); + } + targets.addAll(c); + } + + private void setActions(Collection c) { + if (!haveActions()) { + throw new IllegalArgumentException( + "This permission should not contain targets."); + } + actions.addAll(c); + } + + /** + * Returns an array of TARGETs associated with this permission. + * + * @return + */ + public TARGET[] getTargets() { + TARGET[] targets = this.targets.toArray(new TARGET[this.targets + .size()]); + return targets; + } + + /** + * Returns an array of TARGETs associated with this permission. + * + * @return + */ + public ACTION[] getActions() { + if (!haveActions()) { + return new ACTION[0]; + } + ACTION[] actions = this.actions.toArray(new ACTION[this.actions + .size()]); + return actions; + } + + public String toString() { + return getClassName(); + } + + public boolean haveTargets() { + return this.haveTargets; + } + + public boolean haveActions() { + return this.haveActions; + } + + public boolean needInput() { + return this.manualTarget; + } + + public String getClassName() { + return this.fullClassName; + } + + public String getDisplayName() { + return this.name; + } + }; + + static { + ArrayList targetList = new ArrayList(); + ArrayList actionList = new ArrayList(); + + // PERMISSION.ALL_PERMISSION takes neither targets or actions. + + /* Set TARGETS here */ + // XXX: PERMISSION.DELEGATION_PERMISSION takes input but no default + // TARGET + + targetList.add(TARGET.PLAY); + targetList.add(TARGET.RECORD); + PERMISSION.AUDIO_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.DO_AS); + targetList.add(TARGET.DO_AS_PRIVILEGED); + targetList.add(TARGET.GET_SUBJECT); + targetList.add(TARGET.GET_SUBJECT_FROM_DOMAIN_COMBINER); + targetList.add(TARGET.SET_READ_ONLY); + targetList.add(TARGET.MODIFY_PRINCIPALS); + targetList.add(TARGET.MODIFY_PUBLIC_CREDENTIALS); + targetList.add(TARGET.MODIFY_PRIVATE_CREDENTIALS); + targetList.add(TARGET.REFRESH_CREDENTIAL); + targetList.add(TARGET.DESTROY_CREDENTIAL); + targetList.add(TARGET.CREATE_LOGIN_CONTEXT); + targetList.add(TARGET.GET_LOGIN_CONFIGURATION); + targetList.add(TARGET.SET_LOGIN_CONFIGURATION); + targetList.add(TARGET.REFRESH_LOGIN_CONFIGURATION); + PERMISSION.AUTH_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.ACCESS_CLIPBOARD); + targetList.add(TARGET.ACCESS_EVENT_QUEUE); + targetList.add(TARGET.CREATE_ROBOT); + targetList.add(TARGET.LISTEN_TO_ALL_AWT_EVENTS); + targetList.add(TARGET.READ_DISPLAY_PIXELS); + targetList.add(TARGET.SHOW_WINDOW_WITHOUT_WARNING_BANNER); + PERMISSION.AWT_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.ALL_FILES); + PERMISSION.FILE_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.CONTROL); + PERMISSION.LOGGING_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.SET_DEFAULT_AUTHENTICATOR); + targetList.add(TARGET.REQUEST_PASSWORD_AUTHENTICATION); + targetList.add(TARGET.SPECIFY_STREAM_HANDLER); + PERMISSION.NET_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.SUPRESS_ACCESS_CHECKS); + PERMISSION.REFLECT_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.CREATE_CLASS_LOADER); + targetList.add(TARGET.GET_CLASS_LOADER); + targetList.add(TARGET.SET_CONTEXT_CLASSLOADER); + targetList.add(TARGET.SET_SECURITY_MANAGER); + targetList.add(TARGET.CREATE_SECURITY_MANAGER); + targetList.add(TARGET.EXIT_VM); + targetList.add(TARGET.SHUTDOWN_HOOKS); + targetList.add(TARGET.SET_FACTORY); + targetList.add(TARGET.SET_IO); + targetList.add(TARGET.MODIFY_THREAD); + targetList.add(TARGET.MODIFY_THREAD_GROUP); + targetList.add(TARGET.GET_PROTECTION_DOMAIN); + targetList.add(TARGET.READ_FILE_DESCRIPTOR); + targetList.add(TARGET.WRITE_FILE_DESCRIPTOR); + targetList.add(TARGET.LOAD_LIBRARY); + targetList.add(TARGET.ACCESS_CLASS_IN_PACKAGE); + targetList.add(TARGET.DEFINE_CLASS_IN_PACKAGE); + targetList.add(TARGET.ACCESS_DECLARED_MEMBERS); + targetList.add(TARGET.QUEUE_PRINT_JOB); + PERMISSION.RUNTIME_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.CREATE_ACCESS_CONTROL_CONTEXT); + targetList.add(TARGET.GET_DOMAIN_COMBINER); + targetList.add(TARGET.GET_POLICY); + targetList.add(TARGET.SET_POLICY); + targetList.add(TARGET.GET_PROPERTY); + targetList.add(TARGET.SET_PROPERTY); + targetList.add(TARGET.INSERT_PROVIDER); + targetList.add(TARGET.REMOVE_PROVIDER); + targetList.add(TARGET.SET_SYSTEM_SCOPE); + targetList.add(TARGET.SET_IDENTITY_PUBLIC_KEY); + targetList.add(TARGET.SET_IDENTITY_INFO); + targetList.add(TARGET.REMOVE_IDENTITY_CERTIFICATE); + targetList.add(TARGET.PRINT_IDENTITY); + targetList.add(TARGET.CLEAR_PROVIDER_PROPERTIES); + targetList.add(TARGET.PUT_PROVIDER_PROPERTY); + targetList.add(TARGET.REMOVE_PROVIDER_PROPERTY); + targetList.add(TARGET.GET_SIGNER_PRIVATE_KEY); + targetList.add(TARGET.SET_SIGNER_KEY_PAIR); + PERMISSION.SECURITY_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.ENABLE_SUBCLASS_IMPLEMENTATION); + targetList.add(TARGET.ENABLE_SUBSTITUTION); + PERMISSION.SERIALIZABLE_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.SET_LOG); + PERMISSION.SQL_PERMISSION.setTargets(targetList); + + targetList.clear(); + targetList.add(TARGET.SET_HOST_NAME_VERIFIER); + targetList.add(TARGET.GET_SSL_SESSION_CONTEXT); + PERMISSION.SSL_PERMISSION.setTargets(targetList); + + actionList.add(ACTION.READ); + actionList.add(ACTION.WRITE); + actionList.add(ACTION.EXECUTE); + actionList.add(ACTION.DELETE); + PERMISSION.FILE_PERMISSION.setActions(actionList); + + actionList.clear(); + actionList.add(ACTION.READ); + actionList.add(ACTION.WRITE); + PERMISSION.PROPERTY_PERMISSION.setActions(actionList); + + actionList.clear(); + actionList.add(ACTION.READ); + PERMISSION.PRIVATE_CREDENTIAL_PERMISSION.setActions(actionList); + + actionList.clear(); + actionList.add(ACTION.INITIATE); + actionList.add(ACTION.ACCEPT); + PERMISSION.SERVICE_PERMISSION.setActions(actionList); + + actionList.clear(); + actionList.add(ACTION.ACCEPT); + actionList.add(ACTION.CONNECT); + actionList.add(ACTION.LISTEN); + actionList.add(ACTION.RESOLVE); + PERMISSION.SOCKET_PERMISSION.setActions(actionList); + } + + // Actual class implementation begins here. + + private String className; + private String target; + private String signedBy; + private boolean isDefault = false; + private PERMISSION permission = null; + private boolean classExist = false; + private boolean allowAction = true; + private String defaultName = getNextName(); + private boolean isDefaultName = true; + + private String name = null; + private HashSet actions = new HashSet(); + + public PolicyPermission() { + // Create Stub. + } + + /** + * Create a new policy permission. + * + * @param name String representing the full class name (Includes package) + * @param target String representing the target specific to this permission. + * @param action String representing which action we're allowed to perform + * on the target. Allowed to be null in some cased + */ + public PolicyPermission(String className, String target, String action, + String signedBy) { + + setClassName(className); + setTarget(target); + + if (action != null) + action = action.trim(); + + if (action != null && action.length() == 0) + action = null; + + /* + * Getting here means we have acceptable entries. By acceptable I mean, + * name is not null, and target is not null. action is null if there was + * a blank or no entry + */ + setAction(action); + setSignedBy(signedBy); + } + + /** + * Sets who signed this permission. + * + * @param signedBy name of signer. + */ + public void setSignedBy(String signedBy) { + // FIXME: Check if signer is valid (Check keystore?) + + if (signedBy != null) + signedBy = signedBy.trim(); + + if (signedBy != null && signedBy.length() == 0) + signedBy = null; + + this.signedBy = signedBy; + } + + /** + * Checks whether the class exists. + * + * @return + */ + public boolean classExist() { + return this.classExist; + } + + /** + * Set the class this permission is associated with. + * + * @param className Name of the class. + */ + public void setClassName(String className) { + if (className != null) + className = className.trim(); + + if (className.length() == 0) { + className = null; + } + this.classExist = false; // We don't know if it exist. So reset it. + this.isDefault = false; + this.className = className; + + validate(); + } + + /** + * Verify this class is valid. Sets whether it is part of default and if it + * exists or not. + */ + private void validate() { + if (getClassName() == null) + return; + + boolean localExist = false; + + // Do a lazy check here, since the defaults will exist. + for (PERMISSION permission : PERMISSION.values()) { + if (className.equals(permission.getClassName())) { + this.classExist = true; + this.permission = permission; + localExist = true; + this.allowAction = permission.haveActions(); + break; + } + } + + // Not a default permission class. Check if this class exists. + if (!this.classExist) { + this.permission = null; + try { + // Get the class if it exists. Otherwise an exception will be + // thrown. + Class clazz = Class.forName(className, false, null); + + boolean valid = false; + for (Class i : clazz.getClasses()) { + String canon = i.getCanonicalName(); + if (canon != null && canon.equals(canonClassName)) { + valid = true; + break; + } + } + + if (!valid) { // Not a valid permission class. Security manager + // can't handle it otherwise. + throw new IllegalArgumentException( + "This class is not a valid permissions class. Must extend " + + canonClassName); + } + + /* + * FIXME: Try to instantiate the class with given parameters to + * see if it's valid. This should throw an exception if invalid. + * That way we can catch it and remedy the problem. + */ + + this.classExist = true; + } catch (ClassNotFoundException e) { + // FIXME: Warn about invalid class. + } + } + this.isDefault = localExist; + } + + /** + * Returns the name of the class. + * + * @return + */ + public String getClassName() { + return this.className; + } + + /** + * Set the current target. + * + * @param target + */ + public void setTarget(String target) { + if (target != null) + target = target.trim(); + + if (target.length() == 0) { + target = null; + } + + this.target = target; + } + + /** + * Returns the target this permission is binded to. + * + * @return target this permission is associated with. + */ + public String getTarget() { + return this.target; + } + + /** + * Add action to set of actions. + * + * @param action + */ + private void addAction(String action) { + if (action != null) { + action = action.trim(); + actions.add(action); + } + } + + /** + * Sets the current Action. + * + * @param action + */ + public void setAction(String action) { + if (action != null) { + action = action.trim(); + String[] split = action.split("\\s*,\\s*"); + actions.clear(); + + if (isDefault() && permission.haveActions()) { + List l = Arrays.asList(permission.getActions()); + for (ACTION act : l) { + for (String s : split) { + if (act.toString().equals(s)) + addAction(s.toLowerCase()); + } + } + } else { + for (String s : split) { + if (s.length() > 0) + addAction(s.toLowerCase()); + } + } + } + } + + /** + * Returns the action this permission is binded to. + * + * @return action this permission is associated with, null otherwise. + */ + public String getAction() { + StringBuilder sb = new StringBuilder(); + for (String s : actions) { + sb.append(s); + sb.append(", "); + } + if (sb.length() == 0) + return null; + else + sb.setLength(sb.length() - 2); + + return sb.toString(); + } + + /** + * Returns the signer for this permission. + * + * @return + */ + public String getSignedBy() { + return this.signedBy; + } + + public String toString() { + return getName(); + } + + /** + * Set the display name. + * + * @param name + * @return + */ + public boolean setName(String name) { + int i = 0; + + if (name == null) { + this.isDefaultName = true; + this.name = name; + return true; + } + + boolean valid = false; + while (i < name.length() + && (valid = PolicyFormatter.isValidNameChar(name.charAt(i++)))) + ; + if (valid && (!name.equals(defaultName) || !this.isDefaultName)) { + this.isDefaultName = false; + this.name = name; + } + + return valid; + } + + /** + * Returns the name in comment of principal. + * + * @return + */ + public String getName() { + if (this.name == null) { + return this.defaultName; + } + return this.name; + } + + /** + * Check whether this permission is a valid permission. + * + * @return + */ + public boolean isValid() { + boolean valid = true; + if (isDefault()) { + /* + * If there are default targets and it doesn't allow manual + * inputting of targets then it's only valid if it's one of the + * default. + */ + boolean validTarget = !permission.haveTargets() + || permission.needInput(); + if (!validTarget) { + for (TARGET t : permission.getTargets()) { + if (t.toString().equals(getTarget())) { + validTarget = true; + break; + } + } + } + + /* + * If we have default actions, then the options selected must be all + * from this set of action. otherwise it is not valid. + */ + boolean validAction = !permission.haveActions(); + if (validTarget && !validAction) { + for (String myAction : this.actions) { + validAction = false; + for (ACTION action : permission.getActions()) { + if (action.toString().equals(myAction)) { + validAction = true; + break; + } + } + if (!validAction) { + break; + } + } + } + + valid = validTarget && validAction; + } + + return (permission == PERMISSION.ALL_PERMISSION && this.target == null && this.actions + .size() == 0) + || (permission != PERMISSION.ALL_PERMISSION + && this.target != null && this.className != null + && valid && (this.actions.size() == 0 + && !this.allowAction() || this.actions.size() > 0 + && this.allowAction())); + } + + /** + * Check if we allow setting Action. + * + * @return + */ + public boolean allowAction() { + return this.allowAction; + } + + /** + * Check if this was a preset permission. + * + * @return + */ + public boolean isDefault() { + return this.isDefault; + } + + /** + * Check whether the permission's name is the default one. + * + * @return + */ + public boolean isDefaultName() { + return isDefaultName; + } + + public static String getNextName() { + return "CustomPermission" + nextIndex++; + } + +} diff -r 86abbf8be0b1 netx/net/sourceforge/jnlp/policy/PolicyPrincipal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/policy/PolicyPrincipal.java Fri Jun 24 10:21:30 2011 -0400 @@ -0,0 +1,242 @@ +/* PolicyPrincipal.java -- Store information about policy's principal. +Copyright (C) 2010 Red Hat + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package net.sourceforge.jnlp.policy; + +/** + * This class represents a principal in a policy. + * + * @author Andrew Su (asu@redhat.com, andrew.su@utoronto.ca) + * + */ +public final class PolicyPrincipal { + private static int nextIndex = 1; + + public static enum PRINCIPAL { + X500_PRINCIPAL("X500Principal", "javax.security.auth.x500.X500Principal"), + KERBEROS_PRINCIPAL("KerberosPrincipal", "javax.security.auth.kerberos.KerberosPrincipal"); + + private final String displayName; + private final String fullClassName; + + private PRINCIPAL(String displayName, String fullClassName) { + this.displayName = displayName; + this.fullClassName = fullClassName; + } + + public String getClassName() { + return this.fullClassName; + } + + public String getDisplayName() { + return this.displayName; + } + + public String toString() { + return getClassName(); + } + } + + /* This is the interface which all principal classes should implement. */ + private static final Class c; + private static final String canonClassName; + + static { + try { + c = Class.forName("java.security.Principal", false, null); + } catch (ClassNotFoundException e) { + // This should never happen.. + throw new RuntimeException( + "We can't find the principal interface class. Die here."); + } + canonClassName = c.getCanonicalName(); + } + + private String className; + private String name = null; + private String commentName = null; + private String defaultName = getNextName(); + private boolean isDefaultName = true; + + /** + * Create a stub principal. + */ + public PolicyPrincipal() { + // Stub constructor. + } + + /** + * Creates a principal. + * + * @param className + * @param name + */ + public PolicyPrincipal(String className, String name) { + setClass(className); + setName(name); + } + + /** + * Returns the name of the principal. + * + * @return + */ + public String getName() { + return name; + } + + /** + * Returns the name of the class used by this principal. + * + * @return + */ + public String getClassName() { + return this.className; + } + + /** + * Returns whether this class exists. + * + * @return + */ + public boolean classExist() { + for (PRINCIPAL p : PRINCIPAL.values()) { + if (p.getClassName().equals(this.className)) { + return true; + } + } + + return false; + } + + public void setClass(String className) { + if (className != null) + className = className.trim(); + + if (className.length() == 0) + className = null; + + this.className = className; + validate(); + } + + public void setName(String name) { + if (name != null) + name = name.trim(); + + if (name == null || name.length() == 0) + name = null; + + this.name = name; + } + + private void validate() { + if (!classExist()) { // Not a default class. + try { + Class clazz = Class.forName(className, false, null); + + boolean valid = false; + for (Class i : clazz.getClasses()) { + String canon = i.getCanonicalName(); + if (canon != null && canon.equals(canonClassName)) { + valid = true; + break; + } + } + + if (!valid && !clazz.equals(c)) { + throw new IllegalArgumentException( + "This class is not a valid principal class. Must implement " + + canonClassName); + } + + /* + * FIXME: Try to instantiate the class with given parameters to + * see if it's valid. This should throw an exception if invalid. + * That way we can catch it and remedy the problem. + */ + } catch (ClassNotFoundException e) { + // FIXME: Warn about unfound class. + } + } + } + + /** + * Sets the display name. + * + * @param name + * @return + */ + public boolean setCommentName(String name) { + int i = 0; + + if (name == null) { + this.isDefaultName = true; + this.commentName = name; + return true; + } + + boolean valid = false; + while (i < name.length() + && (valid = PolicyFormatter.isValidNameChar(name.charAt(i++)))) + ; + if (valid && (!name.equals(defaultName) || !this.isDefaultName)) { + this.isDefaultName = false; + this.commentName = name; + } + + return valid; + } + + /** + * Returns the name in comment of principal. + * + * @return + */ + public String getCommentName() { + if (this.commentName == null) { + return this.defaultName; + } + return this.commentName; + } + + /** + * Get next available default name. + * + * @return + */ + public static String getNextName() { + return "CustomPrincipal" + nextIndex++; + } + + public String toString() { + return getCommentName(); + } + + /** + * Check whether this principal is valid. + * + * @return + */ + public boolean isValid() { + return getClassName() != null && getName() != null; + } + + public boolean isDefaultName() { + return isDefaultName; + } +}