--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.security.DatabasePermission\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.security;\r
+\r
+import java.security.Permission;\r
+\r
+import java.util.Set;\r
+import java.util.HashSet;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.ObjectInputStream;\r
+import java.io.ObjectOutputStream;\r
+\r
+\r
+/**\r
+ * This class represents access to database-scoped privileges.\r
+ *\r
+ * An example of database-scoped privileges is the permission to create\r
+ * a database under a specified directory path.\r
+ * <p>\r
+ * A DatabasePermission is defined by two string attributes, similar to\r
+ * a java.io.FilePermission:\r
+ * <ul>\r
+ * <li> <i>URL</i> - a location description of or for a Derby database\r
+ * <li> <i>Actions</i> - a list of granted administrative actions\r
+ * </ul>\r
+ * The database location URL may contain certain wildcard characters.\r
+ * The currently only supported database action is <i>create</i>.\r
+ *\r
+ * @see DatabasePermission#DatabasePermission(String,String)\r
+ * @see SystemPermission\r
+ * @see java.io.FilePermission\r
+ */\r
+public class DatabasePermission extends Permission {\r
+\r
+ /**\r
+ * The URL protocol scheme specifying a directory location.\r
+ */\r
+ static public final String URL_PROTOCOL_DIRECTORY = "directory:";\r
+\r
+ /**\r
+ * The URL file path separator character.\r
+ */\r
+ static public final char URL_PATH_SEPARATOR_CHAR = '/';\r
+\r
+ /**\r
+ * The relative path character.\r
+ */\r
+ static public final char URL_PATH_RELATIVE_CHAR = '.';\r
+\r
+ /**\r
+ * The wildcard character specifying arbitrarily named databases\r
+ * under a directory path.\r
+ */\r
+ static public final char URL_PATH_WILDCARD_CHAR = '*';\r
+\r
+ /**\r
+ * The wildcard character specifying arbitrarily named databases\r
+ * anywhere under a path and its subdirectories.\r
+ */\r
+ static public final char URL_PATH_RECURSIVE_CHAR = '-';\r
+\r
+ // derived path type constants\r
+ static public final String URL_PATH_SEPARATOR_STRING\r
+ = String.valueOf(URL_PATH_SEPARATOR_CHAR);\r
+ static public final String URL_PATH_RELATIVE_STRING\r
+ = String.valueOf(URL_PATH_RELATIVE_CHAR);\r
+ static public final String URL_PATH_RELATIVE_PREFIX\r
+ = (URL_PATH_RELATIVE_STRING + URL_PATH_SEPARATOR_CHAR);\r
+ static public final String URL_PATH_WILDCARD_STRING\r
+ = String.valueOf(URL_PATH_WILDCARD_CHAR);\r
+ static public final String URL_PATH_WILDCARD_SUFFIX\r
+ = (URL_PATH_SEPARATOR_STRING + URL_PATH_WILDCARD_CHAR);\r
+ static public final String URL_PATH_RECURSIVE_STRING\r
+ = String.valueOf(URL_PATH_RECURSIVE_CHAR);\r
+ static public final String URL_PATH_RECURSIVE_SUFFIX\r
+ = (URL_PATH_SEPARATOR_STRING + URL_PATH_RECURSIVE_CHAR);\r
+\r
+ /**\r
+ * The create database permission.\r
+ */\r
+ static public final String CREATE = "create";\r
+\r
+ /**\r
+ * The legal database permission action names.\r
+ */\r
+ static protected final Set LEGAL_ACTIONS = new HashSet();\r
+ static {\r
+ // when adding new actions, check method: implies(Permission)\r
+ LEGAL_ACTIONS.add(CREATE);\r
+ };\r
+\r
+ /**\r
+ * The original location URL passed to constructor.\r
+ */\r
+ private final String url;\r
+\r
+ /**\r
+ * This permission's canonical directory path.\r
+ *\r
+ * The path consists of a canonicalized form of the user-specified URL,\r
+ * stripped off the protocol specification and any recursive/wildcard\r
+ * characters. The canonical path is used when testing permissions\r
+ * with implies(), where real directory locations, not just notational\r
+ * differences, ought to be compared. Analog to java.io.FilePermission,\r
+ * the canonical path is also used by equals() and hashCode() to support\r
+ * hashing and mapping of permissions by their real directory locations.\r
+ *\r
+ * Because canonical file paths are platform dependent, this field\r
+ * must not be serialized (hence transient) but be recomputed from\r
+ * the original URL upon deserialization.\r
+ */\r
+ private transient String path;\r
+\r
+ /**\r
+ * The parent directory of this permission's canonical directory path,\r
+ * or null if this permission's path does not name a parent directory.\r
+ *\r
+ * Because canonical file paths are platform dependent, this field\r
+ * must not be serialized (hence transient) but be recomputed from\r
+ * the original URL upon deserialization.\r
+ */\r
+ private transient String parentPath;\r
+\r
+ /**\r
+ * Indicates whether the path denotes a recursive, wildcard, or single\r
+ * location.\r
+ *\r
+ * If the path denotes a recursive or wildcard location, this field's\r
+ * value is URL_PATH_RECURSIVE_CHAR or URL_PATH_WILDCARD_CHAR,\r
+ * respectively; otherwise, it's URL_PATH_SEPARATOR_CHAR denoting a\r
+ * single location.\r
+ */\r
+ private char pathType;\r
+\r
+ /**\r
+ * Creates a new DatabasePermission with the specified URL and actions.\r
+ * <P>\r
+ * <i>actions</i> contains a comma-separated list of the desired actions\r
+ * granted on a database. Currently, the only supported action is\r
+ * <code>create</code>.\r
+ * <P>\r
+ * <i>URL</i> denotes a database location URL, which, at this time, must\r
+ * start with <code>directory:</code> followed by a directory pathname.\r
+ * Note that in a URL, the separator character is always "/" rather than\r
+ * the file separator of the operating-system. The directory path may\r
+ * be absolute or relative, in which case it is prefixed with the current\r
+ * user directory. In addition, similar to java.io.FilePermission, the\r
+ * directory pathname may end with a wildcard character to allow for\r
+ * arbitrarily named databases under a path:\r
+ * <ul>\r
+ * <li> "directory:location" - refers to a database called\r
+ * <i>location</i>,\r
+ * <li> "directory:location/*" - refers to any database in the\r
+ * directory <i>location</i>,\r
+ * <li> "directory:location/-" - refers to any database anywhere under\r
+ * <i>location</i> or its subdirectories.\r
+ * <li> "directory:*" - refers to any database in the user's current\r
+ * working directory.\r
+ * <li> "directory:-" - refers to any database anywhere under the\r
+ * user's current working directory or its subdirectories.\r
+ * </ul>\r
+ * Note that in contrast to FilePermission, there is no reasonable use\r
+ * for a special pathname "<<ALL FILES>>" matching all locations.\r
+ *\r
+ * @param url the database URL\r
+ * @param actions the action string\r
+ * @throws NullPointerException if an argument is null\r
+ * @throws IllegalArgumentException if an argument is not legal\r
+ * @throws IOException if the location URL cannot be canonicalized\r
+ * @see Permission#Permission(String)\r
+ * @see java.io.FilePermission#FilePermission(String,String)\r
+ */\r
+ public DatabasePermission(String url, String actions)\r
+ throws IOException {\r
+ super(url);\r
+ initActions(actions);\r
+ initLocation(url);\r
+\r
+ // store original URL for reconstructing path at deserialization\r
+ this.url = url;\r
+ }\r
+\r
+ /**\r
+ * Parses the list of database actions.\r
+ *\r
+ * @param actions the comma-separated action list\r
+ * @throws NullPointerException if actions is null\r
+ * @throws IllegalArgumentException if not a list of legal actions\r
+ */\r
+ protected void initActions(String actions) {\r
+ // note that exception messages on the action list aren't localized,\r
+ // as is the general rule with runtime exceptions indicating\r
+ // internal coding errors\r
+\r
+ // analog to java.security.BasicPermission, we check that actions\r
+ // is not null nor empty\r
+ if (actions == null) {\r
+ throw new NullPointerException("actions can't be null");\r
+ }\r
+ if (actions.length() == 0) {\r
+ throw new IllegalArgumentException("actions can't be empty");\r
+ }\r
+\r
+ // splitting the comma-separated list into the individual actions\r
+ // may throw a java.util.regex.PatternSyntaxException, which is a\r
+ // java.lang.IllegalArgumentException, hence directly applicable\r
+ final String[] s = actions.split(",");\r
+\r
+ // check for any illegal actions\r
+ for (int i = 0; i < s.length; i++) {\r
+ final String action = s[i].trim();\r
+ if (!LEGAL_ACTIONS.contains(action)) {\r
+ // report illegal action\r
+ final String msg = "Illegal action '" + action + "'";\r
+ //System.out.println("DatabasePermission: " + msg);\r
+ throw new IllegalArgumentException(msg);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Parses the database location URL.\r
+ *\r
+ * @param url the database URL\r
+ * @throws NullPointerException if the URL is null\r
+ * @throws IllegalArgumentException if the URL is not well-formed\r
+ * @throws IOException if the location URL cannot be canonicalized\r
+ */\r
+ protected void initLocation(String url)\r
+ throws IOException {\r
+ // note that exception messages on the URL aren't localized,\r
+ // as is the general rule with runtime exceptions indicating\r
+ // internal coding errors\r
+\r
+ // analog to java.security.BasicPermission, we check that URL\r
+ // is not null nor empty\r
+ if (url == null) {\r
+ throw new NullPointerException("URL can't be null");\r
+ }\r
+ if (url.length() == 0) {\r
+ throw new IllegalArgumentException("URL can't be empty");\r
+ }\r
+\r
+ // check URL's protocol scheme and initialize path\r
+ if (!url.startsWith(URL_PROTOCOL_DIRECTORY)) {\r
+ final String msg = "Unsupported protocol in URL '" + url + "'";\r
+ //System.out.println("DatabasePermission: " + msg);\r
+ throw new IllegalArgumentException(msg);\r
+ }\r
+ String p = url.substring(URL_PROTOCOL_DIRECTORY.length());\r
+\r
+ // check path for relative/recursive/wildcard specifications,\r
+ // split path into real pathname and the path type\r
+ if (p.equals(URL_PATH_RECURSIVE_STRING)) {\r
+ // relative & recursive: "-" --> '-', "./"\r
+ pathType = URL_PATH_RECURSIVE_CHAR;\r
+ p = URL_PATH_RELATIVE_PREFIX;\r
+ } else if (p.equals(URL_PATH_WILDCARD_STRING)) {\r
+ // relative & wildcard: "*" --> '*', "./"\r
+ pathType = URL_PATH_WILDCARD_CHAR;\r
+ p = URL_PATH_RELATIVE_PREFIX;\r
+ } else if (p.endsWith(URL_PATH_RECURSIVE_SUFFIX)) {\r
+ // absolute & recursive: "<path>/-" --> '-', "<path>/"\r
+ pathType = URL_PATH_RECURSIVE_CHAR;\r
+ p = p.substring(0, p.length() - 1);\r
+ } else if (p.endsWith(URL_PATH_WILDCARD_SUFFIX)) {\r
+ // absolute & wildcard: "<path>/*" --> '*', "<path>/"\r
+ pathType = URL_PATH_WILDCARD_CHAR;\r
+ p = p.substring(0, p.length() - 1);\r
+ } else {\r
+ // absolute | relative: "<path>" --> '/', "<path>"\r
+ pathType = URL_PATH_SEPARATOR_CHAR;\r
+ // p = p;\r
+ }\r
+\r
+ // resolve against user's working directory if relative pathname\r
+ if (p.startsWith(URL_PATH_RELATIVE_PREFIX)) {\r
+ final String cwd = System.getProperty("user.dir");\r
+ // concatenated path "<cwd>/./<path>" will be canonicalized\r
+ p = cwd + URL_PATH_SEPARATOR_STRING + p;\r
+ }\r
+\r
+ // store canonicalized path as required for implies(Permission);\r
+ // may throw IOException \r
+ final File f = (new File(p)).getCanonicalFile();\r
+ this.path = f.getPath();\r
+\r
+ // store canonicalized path of parent file as required for\r
+ // implies(Permission); may throw IOException; note that\r
+ // the path already denotes parent directory if of wildcard type:\r
+ // for example, the parent of "/a/-" or "/a/*" is "/a"\r
+ this.parentPath = ((pathType != URL_PATH_SEPARATOR_CHAR)\r
+ ? path : f.getParent());\r
+\r
+ //assert (pathType == URL_PATH_SEPARATOR_CHAR\r
+ // || pathType == URL_PATH_WILDCARD_CHAR\r
+ // || pathType == URL_PATH_RECURSIVE_CHAR);\r
+ //assert (path != null);\r
+ }\r
+\r
+ /**\r
+ * Checks if this DatabasePermission implies a specified permission.\r
+ * <P>\r
+ * This method returns true if:<p>\r
+ * <ul>\r
+ * <li> <i>p</i> is an instanceof DatabasePermission and<p>\r
+ * <li> <i>p</i>'s directory pathname is implied by this object's\r
+ * pathname. For example, "/tmp/*" implies "/tmp/foo", since\r
+ * "/tmp/*" encompasses the "/tmp" directory and all files in that\r
+ * directory, including the one named "foo".\r
+ * </ul>\r
+ * @param p the permission to check against\r
+ * @return true if the specified permission is implied by this object,\r
+ * false if not\r
+ * @see Permission#implies(Permission)\r
+ */\r
+ public boolean implies(Permission p) {\r
+ //System.out.println("this = " + this);\r
+ //System.out.println("that = " + p);\r
+\r
+ // can only imply other DatabasePermissions\r
+ if (!(p instanceof DatabasePermission)) {\r
+ return false;\r
+ }\r
+ final DatabasePermission that = (DatabasePermission)p;\r
+\r
+ // a recursive permission implies any other if a path prefix\r
+ if (this.pathType == URL_PATH_RECURSIVE_CHAR) {\r
+ return (that.parentPath != null\r
+ && that.parentPath.startsWith(this.path));\r
+ }\r
+ //assert (this.pathType != URL_PATH_RECURSIVE_CHAR);\r
+\r
+ // a non-recursive permission cannot imply a recursive one\r
+ if (that.pathType == URL_PATH_RECURSIVE_CHAR) {\r
+ return false;\r
+ }\r
+ //assert (that.pathType != URL_PATH_RECURSIVE_CHAR);\r
+\r
+ //System.out.println("");\r
+ \r
+ // a wildcard permission implies another if a parent directory\r
+ if (this.pathType == URL_PATH_WILDCARD_CHAR) {\r
+ return this.path.equals(that.parentPath);\r
+ }\r
+ //assert (this.pathType != URL_PATH_WILDCARD_CHAR);\r
+\r
+ // a non-wildcard permission cannot imply a wildcard one\r
+ if (that.pathType == URL_PATH_WILDCARD_CHAR) {\r
+ return false;\r
+ }\r
+ //assert (that.pathType != URL_PATH_WILDCARD_CHAR);\r
+\r
+ // non-recursive, non-wildcard permissions imply when paths are equal\r
+ //assert (this.pathType == URL_PATH_SEPARATOR_CHAR);\r
+ //assert (that.pathType == URL_PATH_SEPARATOR_CHAR);\r
+ return this.path.equals(that.path);\r
+ }\r
+\r
+ /**\r
+ * Checks two DatabasePermission objects for equality.\r
+ * <P>\r
+ * Checks that <i>obj</i> is a DatabasePermission and has the same\r
+ * canonizalized URL and actions as this object.\r
+ * <P>\r
+ * @param obj the object we are testing for equality with this object\r
+ * @return true if obj is a DatabasePermission, and has the same URL and\r
+ * actions as this DatabasePermission object, false if not\r
+ *\r
+ * @see Permission#equals(Object)\r
+ */\r
+ public boolean equals(Object obj) {\r
+ if (obj == this) {\r
+ return true;\r
+ }\r
+\r
+ if (!(obj instanceof DatabasePermission)) {\r
+ return false;\r
+ }\r
+ final DatabasePermission that = (DatabasePermission)obj;\r
+\r
+ // compare canonicalized URLs\r
+ return (path.equals(that.path) && pathType == that.pathType);\r
+ }\r
+\r
+ /**\r
+ * Returns the hash code value for this object.\r
+ *\r
+ * @return a hash code value for this object\r
+ * @see Permission#hashCode()\r
+ */\r
+ public int hashCode() {\r
+ // hash canonicalized URL\r
+ return (path.hashCode() ^ pathType);\r
+ }\r
+\r
+ /**\r
+ * Returns the "canonical string representation" of the actions.\r
+ *\r
+ * @return the canonical string representation of the actions\r
+ * @see Permission#getActions()\r
+ */\r
+ public String getActions() {\r
+ // currently, the only supported action\r
+ return CREATE;\r
+ }\r
+\r
+\r
+ /**\r
+ * Called upon Serialization for saving the state of this\r
+ * DatabasePermission to a stream.\r
+ */\r
+ private void writeObject(ObjectOutputStream s)\r
+ throws IOException {\r
+ // write the non-static and non-transient fields to the stream\r
+ s.defaultWriteObject();\r
+ }\r
+\r
+ /**\r
+ * Called upon Deserialization for restoring the state of this\r
+ * DatabasePermission from a stream.\r
+ */\r
+ private void readObject(ObjectInputStream s)\r
+ throws IOException, ClassNotFoundException\r
+ {\r
+ // read the non-static and non-transient fields from the stream\r
+ s.defaultReadObject();\r
+ // restore the platform-dependent path from the original URL\r
+ initLocation(url);\r
+ }\r
+}\r