db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d..@apache.org
Subject svn commit: r670284 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/sql/dictionary/ engine/org/apache/derby/impl/sql/catalog/ engine/org/apache/derby/impl/sql/execute/ engine/org/apache/derby/loc/ shared/org/apache/derby/shared/common/refe...
Date Sun, 22 Jun 2008 01:09:48 GMT
Author: dag
Date: Sat Jun 21 18:09:48 2008
New Revision: 670284

URL: http://svn.apache.org/viewvc?rev=670284&view=rev
Log:
DERBY-3722 Add circularity check for the GRANT role statement

Patch derby-3722-3, which implements this checking and adds test cases
for this to RolesTest.


Added:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleClosureIterator.java
  (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/RoleClosureIteratorImpl.java
  (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleGrantDescriptor.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSROLESRowFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateRoleConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DropRoleConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GrantRoleConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RevokeRoleConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/SetRoleConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
Sat Jun 21 18:09:48 2008
@@ -63,6 +63,7 @@
  */
 
 public interface DataDictionary
+
 {
 	String MODULE = "org.apache.derby.iapi.sql.dictionary.DataDictionary";
 
@@ -480,6 +481,30 @@
 
 
 	/**
+	 * This method creates a new iterator over the closure of role
+	 * grants starting or ending with a given role.
+	 *
+	 * This method will cause reading of dictionary, so should be
+	 * called inside a transaction, after a {@code dd.startReading()}
+	 * or {@code dd.startWriting()} call.
+	 *
+	 * @param tc transaction controller
+	 * @param role name of starting point for closure
+	 * @param inverse If {@code true}, compute closure on inverse of
+	 *        relation GRANT role-a TO role-b that is, we look at
+	 *        closure of all roles granted <bold>to</bold> {@code role}. If
+	 *        {@code false}, we look at closure of all roles that have
+	 *        been granted {@code role}.
+	 * @throws StandardException
+	 */
+	public RoleClosureIterator createRoleClosureIterator
+		(TransactionController tc,
+		 String role,
+		 boolean inverse
+		) throws StandardException;
+
+
+	/**
 	 * Drop all permission descriptors corresponding to a grant to
 	 * the named authentication identifier
 	 *
@@ -1977,4 +2002,4 @@
 	public boolean existsGrantToAuthid(String authId,
 									   TransactionController tc)
 				throws StandardException;
-}	
+}

Added: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleClosureIterator.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleClosureIterator.java?rev=670284&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleClosureIterator.java
(added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleClosureIterator.java
Sat Jun 21 18:09:48 2008
@@ -0,0 +1,97 @@
+/*
+
+   Derby - Class org.apache.derby.iapi.sql.dictionary.RoleClosureIterator
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.derby.iapi.sql.dictionary;
+
+import org.apache.derby.iapi.sql.Activation;
+import org.apache.derby.iapi.error.StandardException;
+import java.util.HashMap;
+
+/**
+ * Allows iterator over the role grant closure defined by the relation
+ * GRANT role-a TO role-b, or its inverse.
+ * @see DataDictionary#createRoleClosureIterator
+ * @see org.apache.derby.impl.sql.catalog.RoleClosureIteratorImpl
+ */
+public interface RoleClosureIterator
+{
+
+    /**
+     * Returns the next (as yet unseen) role in the closure of the
+     * grant or grant<sup>-1</sup> relation. 
+     *
+     * The grant relation forms a DAG (directed acyclic graph).
+     * <pre>
+     * Example:
+     *      Assume a set of created roles forming nodes:
+     *            {a1, a2, a3, b, c, d, e, f, h, j}
+     *
+     *      Assume a set of GRANT statements forming arcs:
+     *
+     *      GRANT a1 TO b;   GRANT b TO e;  GRANT e TO h;
+     *      GRANT a1 TO c;                  GRANT e TO f;
+     *      GRANT a2 TO c;   GRANT c TO f;  GRANT f TO h;
+     *      GRANT a3 TO d;   GRANT d TO f;  GRANT a1 to j;
+     *
+     *
+     *          a1            a2         a3
+     *         / | \           |          |
+     *        /  b  +--------> c          d
+     *       j   |              \        /
+     *           e---+           \      /
+     *            \   \           \    /
+     *             \   \---------+ \  /
+     *              \             \_ f
+     *               \             /
+     *                \           /
+     *                 \         /
+     *                  \       /
+     *                   \     /
+     *                    \   /
+     *                      h
+     * </pre>
+     * An iterator on the inverse relation starting at h for the above
+     * grant graph will return:
+     * <pre>
+     *       closure(h, grant-inv) = {e, b, a1, f, c, a2, d, a3}
+     * </pre>
+     * <p>
+     * An iterator on normal (not inverse) relation starting at a1 for
+     * the above grant graph will return:
+     * <pre>
+     *       closure(a1, grant)    = {b, j, e, h, f, c}
+     * </pre>
+     *
+     * @return a role name identifying a yet unseen node, or null if
+     *         the closure is exhausted.  The order in which the nodes
+     *         are returned is not defined.
+     */
+    public String next();
+
+
+    /**
+     * This method should be called after the iteration is completed.
+     *
+     * @throws StandardException
+     */
+    public void close() throws StandardException;
+
+
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleClosureIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleGrantDescriptor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleGrantDescriptor.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleGrantDescriptor.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/RoleGrantDescriptor.java
Sat Jun 21 18:09:48 2008
@@ -35,9 +35,9 @@
 /**
  * This class is used by rows in the SYS.SYSROLES system table.
  *
- * An instance contains information for exactly:
- * One <role definition>, cf. ISO/IEC 9075-2:2003 section 12.4 *or*
- * one <grant role statement>, section 12.5.
+ * An instance contains information for exactly: One &lt;role
+ * definition&gt;, cf. ISO/IEC 9075-2:2003 section 12.4
+ * <bold>or</bold> one &lt;grant role statement&gt;, section 12.5.
  *
  * A role definition is also modeled as a role grant (hence the class
  * name), but with the special grantor "_SYSTEM", and with a grantee

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
Sat Jun 21 18:09:48 2008
@@ -54,6 +54,7 @@
 import org.apache.derby.iapi.sql.dictionary.PermissionsDescriptor;
 import org.apache.derby.iapi.sql.dictionary.ReferencedKeyConstraintDescriptor;
 import org.apache.derby.iapi.sql.dictionary.RoleGrantDescriptor;
+import org.apache.derby.iapi.sql.dictionary.RoleClosureIterator;
 import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
 import org.apache.derby.iapi.sql.dictionary.CheckConstraintDescriptor;
@@ -2968,6 +2969,110 @@
 		return false;
 	}
 
+	/**
+	 * Return an in-memory representation of the role grant graph (sans
+	 * grant of roles to users, only role-role relation.
+	 *
+	 * @param tc        Transaction Controller
+	 * @param inverse   make graph on inverse grant relation
+	 * @return          hash map representing role grant graph.
+	 *                  <ul><li>Key: rolename,</li>
+	 *                      <li>Value: List<RoleGrantDescriptor> representing a
+	 *                      grant of that rolename to another role (not user).
+	 *                      </li>
+	 *                  </ul>
+	 *
+	 * FIXME: Need to cache graph and invalidate when role graph is modified.
+	 * Currently, we always read from SYSROLES.
+	 */
+	private HashMap getRoleGrantGraph(TransactionController tc, boolean inverse)
+			throws StandardException {
+
+		HashMap hm = new HashMap();
+
+		TabInfoImpl ti = getNonCoreTI(SYSROLES_CATALOG_NUM);
+		SYSROLESRowFactory rf = (SYSROLESRowFactory) ti.getCatalogRowFactory();
+
+		DataValueDescriptor isDefOrderable = new SQLVarchar("N");
+		ScanQualifier[][] scanQualifier = exFactory.getScanQualifier(1);
+
+		scanQualifier[0][0].setQualifier(
+			SYSROLESRowFactory.SYSROLES_ISDEF - 1, /* to zero-based */
+			isDefOrderable,
+			Orderable.ORDER_OP_EQUALS,
+			false,
+			false,
+			false);
+
+		ScanController sc = tc.openScan(
+			ti.getHeapConglomerate(),
+			false,   // don't hold open across commit
+			0,       // for update
+			TransactionController.MODE_RECORD,
+			TransactionController.ISOLATION_REPEATABLE_READ,
+			(FormatableBitSet) null,      // all fields as objects
+			(DataValueDescriptor[]) null, // start position -
+			0,                            // startSearchOperation - none
+			scanQualifier,                //
+			(DataValueDescriptor[]) null, // stop position -through last row
+			0);                           // stopSearchOperation - none
+
+		ExecRow outRow =  rf.makeEmptyRow();
+		RoleGrantDescriptor grantDescr;
+
+		while (sc.fetchNext(outRow.getRowArray())) {
+			grantDescr = (RoleGrantDescriptor)rf.buildDescriptor(
+				outRow,
+				(TupleDescriptor) null,
+				this);
+
+			// Next call is potentially inefficient.  We could read in
+			// definitions first in a separate hash table limiting
+			// this to a 2-pass scan.
+			RoleGrantDescriptor granteeDef = getRoleDefinitionDescriptor
+				(grantDescr.getGrantee());
+
+			if (granteeDef == null) {
+				// not a role, must be user authid, skip
+				continue;
+			}
+
+			String hashKey;
+			if (inverse) {
+				hashKey = granteeDef.getRoleName();
+			} else {
+				hashKey = grantDescr.getRoleName();
+			}
+
+			List arcs = (List)hm.get(hashKey);
+			if (arcs == null) {
+				arcs = new LinkedList();
+			}
+
+			arcs.add(grantDescr);
+			hm.put(hashKey, arcs);
+		}
+
+		sc.close();
+
+		return hm;
+
+	}
+
+	/**
+	 * @see DataDictionary#createRoleClosureIterator
+	 */
+	public RoleClosureIterator createRoleClosureIterator
+		(TransactionController tc,
+		 String role,
+		 boolean inverse
+		) throws StandardException {
+
+		HashMap graph = getRoleGrantGraph(tc, inverse);
+
+		return new RoleClosureIteratorImpl(role, inverse, graph);
+	}
+
 
 	/**
 	 * Drop all permission descriptors corresponding to a grant to

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/RoleClosureIteratorImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/RoleClosureIteratorImpl.java?rev=670284&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/RoleClosureIteratorImpl.java
(added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/RoleClosureIteratorImpl.java
Sat Jun 21 18:09:48 2008
@@ -0,0 +1,179 @@
+/*
+
+   Derby - Class org.apache.derby.impl.sql.catalog.RoleClosureIteratorImpl
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+
+package org.apache.derby.impl.sql.catalog;
+
+import org.apache.derby.iapi.sql.dictionary.RoleGrantDescriptor;
+import org.apache.derby.iapi.sql.dictionary.RoleClosureIterator;
+import org.apache.derby.iapi.error.StandardException;
+import java.util.List;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Allows iterator over the role grant closure defined by the relation
+ * <code>GRANT</code> role-a <code>TO</code> role-b, or its inverse.
+ * <p>
+ * The graph is represented as a <code>HashMap</code> where the key is
+ * the node and the value is a List grant descriptors representing
+ * outgoing arcs. The set constructed depends on whether <code>inverse</code>
+ * was specified in the constructor.
+ * @see org.apache.derby.iapi.sql.dictionary.RoleClosureIterator
+ */
+public class RoleClosureIteratorImpl implements RoleClosureIterator
+{
+    /**
+     * true if closure is inverse of GRANT role-a TO role-b.
+     */
+    private final boolean inverse;
+
+    /**
+     * Holds roles seen so far when computing the closure.
+     * <ul>
+     *   <li>Key: role name. Depending on value of {@code inverse}, the
+     *       key represents and is compared against {@code roleName()}
+     *       or {@code grantee()} of role descriptors visited.</li>
+     *   <li>Value: none</li>
+     * </ul>
+     */
+    private HashMap seenSoFar;
+
+    /**
+     * Holds the grant graph.
+     * <ul>
+     *   <li>key: role name</li>
+     *   <li>value: list of {@code RoleGrantDescriptor}, making up outgoing arcs
+     *        in graph</li>
+     * </ul>
+     */
+    private HashMap graph;
+
+    /**
+     * Holds discovered, but not yet handed out, roles in the closure.
+     */
+    private List lifo;
+
+    /**
+     * Last node returned by next; a logical pointer into the arcs
+     * list of a node we are currently processing.
+     */
+    private Iterator currNodeIter;
+
+    /**
+     * Constructor (package private).
+     * Use {@code createRoleClosureIterator} to obtain an instance.
+     * @see org.apache.derby.iapi.sql.dictionary.DataDictionary#createRoleClosureIterator
+     *
+     * @param root The role name for which to compute the closure
+     * @param inverse If {@code true}, {@code graph} represents the
+     *                grant<sup>-1</sup> relation.
+     * @param graph The grant graph for which to construct a closure
+     *              and iterator.
+     *
+     */
+    RoleClosureIteratorImpl(String root, boolean inverse,
+                            HashMap graph) {
+        this.inverse = inverse;
+        this.graph = graph;
+
+        // we omit root from closure, so don't add it here.
+        seenSoFar = new HashMap();
+        lifo      = new ArrayList(); // remaining work stack
+        // present iterator of outgoing arcs of the node we are
+        // currently looking at
+        List outgoingArcs = (List)graph.get(root);
+        if (outgoingArcs != null) {
+            this.currNodeIter = outgoingArcs.iterator();
+        } else {
+            // empty
+            this.currNodeIter = new ArrayList().iterator();
+        }
+
+
+    }
+
+
+    public String next() {
+        RoleGrantDescriptor result = null;
+
+        while (result == null) {
+            while (currNodeIter.hasNext()) {
+                RoleGrantDescriptor r =
+                    (RoleGrantDescriptor)currNodeIter.next();
+
+                if (seenSoFar.containsKey
+                        (inverse ? r.getRoleName() : r.getGrantee())) {
+                    continue;
+                } else {
+                    lifo.add(r);
+                    result = r;
+                    break;
+                }
+            }
+
+            if (result == null) {
+                // not more candidates located outgoing from the
+                // latest found node, pick another and continue
+                RoleGrantDescriptor newNode = null;
+
+                currNodeIter = null;
+
+                while (lifo.size() > 0 && currNodeIter == null) {
+
+                    newNode = (RoleGrantDescriptor)lifo.remove(lifo.size() - 1);
+
+                    // In the example (see interface doc), the
+                    // iterator of outgoing arcs for f (grant inverse)
+                    // would contain {e,c,d}.
+                    List outArcs = (List)graph.get(
+                        inverse? newNode.getRoleName(): newNode.getGrantee());
+
+                    if (outArcs != null) {
+                        currNodeIter = outArcs.iterator();
+                    } // else: leaf node, pop next candidate, if any
+                }
+
+                if (currNodeIter == null) {
+                    // candidate stack is empty, done
+                    currNodeIter = null;
+                    break;
+                }
+            }
+        }
+
+        if (result != null) {
+            seenSoFar.put(inverse ? result.getRoleName(): result.getGrantee(),
+                          null);
+            return inverse ? result.getRoleName() : result.getGrantee();
+        } else {
+            return null;
+        }
+    }
+
+
+    public void close() throws StandardException{
+        seenSoFar = null;
+        graph = null;
+        lifo = null;
+        currNodeIter = null;
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/RoleClosureIteratorImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSROLESRowFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSROLESRowFactory.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSROLESRowFactory.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSROLESRowFactory.java
Sat Jun 21 18:09:48 2008
@@ -53,7 +53,7 @@
     private static final int SYSROLES_GRANTEE = 3;
     private static final int SYSROLES_GRANTOR = 4;
     private static final int SYSROLES_WITHADMINOPTION = 5;
-    private static final int SYSROLES_ISDEF = 6;
+    static final int SYSROLES_ISDEF = 6;
 
     private static final int[][] indexColumnPositions =
     {
@@ -62,6 +62,9 @@
         {SYSROLES_ROLE_UUID}
     };
 
+    static final int SYSROLES_ROLEID_COLPOS_IN_INDEX_ID_EE_OR = 1;
+    static final int SYSROLES_GRANTEE_COLPOS_IN_INDEX_ID_EE_OR = 2;
+
     // (role)ID_(grant)EE_(grant)OR
     static final int SYSROLES_INDEX_ID_EE_OR_IDX = 0;
     // (role)ID_(is)DEF
@@ -69,10 +72,6 @@
     // UUID
     static final int SYSROLES_INDEX_UUID_IDX = 2;
 
-
-    static final int SYSROLES_ROLEID_COLPOS_IN_INDEX_ID_EE_OR = 1;
-    static final int SYSROLES_GRANTEE_COLPOS_IN_INDEX_ID_EE_OR = 2;
-
     private static  final   boolean[]   uniqueness = {
         true,
         false, // many rows have same roleid and is not a definition

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateRoleConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateRoleConstantAction.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateRoleConstantAction.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateRoleConstantAction.java
Sat Jun 21 18:09:48 2008
@@ -95,12 +95,12 @@
         //
         // Check if this role already exists. If it does, throw.
         //
-        RoleGrantDescriptor rd = dd.getRoleDefinitionDescriptor(roleName);
+        RoleGrantDescriptor rdDef = dd.getRoleDefinitionDescriptor(roleName);
 
-        if (rd != null) {
+        if (rdDef != null) {
             throw StandardException.
                 newException(SQLState.LANG_OBJECT_ALREADY_EXISTS,
-                             rd.getDescriptorType(), roleName);
+                             rdDef.getDescriptorType(), roleName);
         }
 
         // Check if the proposed role id exists as a user id in
@@ -113,7 +113,7 @@
                              "User", roleName);
         }
 
-        rd = ddg.newRoleGrantDescriptor(
+        rdDef = ddg.newRoleGrantDescriptor(
             dd.getUUIDFactory().createUUID(),
             roleName,
             currentAuthId,// grantee
@@ -121,7 +121,7 @@
             true,         // with admin option
             true);        // is definition
 
-        dd.addDescriptor(rd,
+        dd.addDescriptor(rdDef,
                          null,  // parent
                          DataDictionary.SYSROLES_CATALOG_NUM,
                          false, // duplicatesAllowed

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DropRoleConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DropRoleConstantAction.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DropRoleConstantAction.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DropRoleConstantAction.java
Sat Jun 21 18:09:48 2008
@@ -99,14 +99,14 @@
         */
         dd.startWriting(lcc);
 
-        RoleGrantDescriptor rd = dd.getRoleDefinitionDescriptor(roleName);
+        RoleGrantDescriptor rdDef = dd.getRoleDefinitionDescriptor(roleName);
 
-        if (rd == null) {
+        if (rdDef == null) {
             throw StandardException.newException(
                 SQLState.ROLE_INVALID_SPECIFICATION, roleName);
         }
 
-        rd.drop(lcc);
+        rdDef.drop(lcc);
 
         /*
          * We dropped a role, now drop all dependents:

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GrantRoleConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GrantRoleConstantAction.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GrantRoleConstantAction.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GrantRoleConstantAction.java
Sat Jun 21 18:09:48 2008
@@ -32,6 +32,7 @@
 import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
 import org.apache.derby.iapi.sql.dictionary.RoleGrantDescriptor;
 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
+import org.apache.derby.iapi.sql.dictionary.RoleClosureIterator;
 import org.apache.derby.iapi.store.access.TransactionController;
 import org.apache.derby.shared.common.reference.SQLState;
 import org.apache.derby.iapi.services.sanity.SanityManager;
@@ -98,9 +99,10 @@
                 String grantee = (String)gIter.next();
 
                 // check that role exists
-                RoleGrantDescriptor rd = dd.getRoleDefinitionDescriptor(role);
+                RoleGrantDescriptor rdDef =
+                    dd.getRoleDefinitionDescriptor(role);
 
-                if (rd == null) {
+                if (rdDef == null) {
                     throw StandardException.
                         newException(SQLState.ROLE_INVALID_SPECIFICATION, role);
                 }
@@ -114,18 +116,18 @@
                 // descriptor will not suffice in that case, so we
                 // need something like:
                 //
-                // rd = dd.findRoleGrantWithAdminToRoleOrPublic(grantor)
-                // if (rd != null) {
+                // rdDef = dd.findRoleGrantWithAdminToRoleOrPublic(grantor)
+                // if (rdDef != null) {
                 //   :
                 if (grantor.equals(lcc.getDataDictionary().
                                        getAuthorizationDatabaseOwner())) {
                     // All ok, we are database owner
                     if (SanityManager.DEBUG) {
                         SanityManager.ASSERT(
-                            rd.getGrantee().equals(grantor),
+                            rdDef.getGrantee().equals(grantor),
                             "expected database owner in role grant descriptor");
                         SanityManager.ASSERT(
-                            rd.isWithAdminOption(),
+                            rdDef.isWithAdminOption(),
                             "expected role definition to have ADMIN OPTION");
                     }
                 } else {
@@ -134,31 +136,34 @@
                 }
 
                 // Has it already been granted?
-                rd = dd.getRoleGrantDescriptor(role, grantee, grantor);
+                RoleGrantDescriptor rgd =
+                    dd.getRoleGrantDescriptor(role, grantee, grantor);
+
+                if (rgd != null &&
+                        withAdminOption && !rgd.isWithAdminOption()) {
 
-                if (rd != null && withAdminOption && !rd.isWithAdminOption())
{
                     // NOTE: Never called yet, withAdminOption not yet
                     // implemented.
 
                     // Remove old descriptor and add a new one with admin
                     // option: cf. SQL 2003, section 12.5, general rule 3
-                    rd.drop(lcc);
-                    rd.setWithAdminOption(true);
-                    dd.addDescriptor(rd,
+                    rgd.drop(lcc);
+                    rgd.setWithAdminOption(true);
+                    dd.addDescriptor(rgd,
                                      null,  // parent
                                      DataDictionary.SYSROLES_CATALOG_NUM,
                                      false, // no duplicatesAllowed
                                      tc);
-                } else if (rd == null) {
+                } else if (rgd == null) {
                     // Check if the grantee is a role (if not, it is a user)
-                    RoleGrantDescriptor gd =
+                    RoleGrantDescriptor granteeDef =
                         dd.getRoleDefinitionDescriptor(grantee);
 
-                    if (gd != null) {
-                        // FIXME: Grantee is role, need to check for circularity
+                    if (granteeDef != null) {
+                        checkCircularity(role, grantee, grantor, tc, dd);
                     }
 
-                    rd = ddg.newRoleGrantDescriptor(
+                    rgd = ddg.newRoleGrantDescriptor(
                         dd.getUUIDFactory().createUUID(),
                         role,
                         grantee,
@@ -166,7 +171,7 @@
                         withAdminOption,
                         false);  // not definition
                     dd.addDescriptor(
-                        rd,
+                        rgd,
                         null,  // parent
                         DataDictionary.SYSROLES_CATALOG_NUM,
                         false, // no duplicatesAllowed
@@ -176,6 +181,54 @@
         }
     }
 
+    /**
+     * Check that allowing this grant to go ahead does nto create a
+     * circularity in the GRANT role relation graph, cf. Section 12.5,
+     * Syntax rule 1 of ISO/IEC 9075-2 2003.
+     *
+     * @param role The role about to be granted
+     * @param grantee The role to which {@code role} is to be granted
+     * @param grantor Who does the granting
+     * @throws StandardException normal error policy. Throws
+     *                           AUTH_ROLE_GRANT_CIRCULARITY if a
+     *                           circularity is detected.
+     */
+    private void checkCircularity(String role,
+                                  String grantee,
+                                  String grantor,
+                                  TransactionController tc,
+                                  DataDictionary dd)
+            throws StandardException {
+
+        // The grantee is role, not a user id, so we need to check for
+        // circularity. If there exists a grant back to the role being
+        // granted now, from one of the roles in the grant closure of
+        // grantee, there is a circularity.
+
+        // Trivial circularity: a->a
+        if (role.equals(grantee)) {
+            throw StandardException.newException
+                (SQLState.AUTH_ROLE_GRANT_CIRCULARITY,
+                 role, grantee);
+        }
+
+
+        // Via grant closure of grantee
+        RoleClosureIterator rci =
+            dd.createRoleClosureIterator(tc, grantee, false);
+        try {
+            String r;
+            while ((r = rci.next()) != null) {
+                if (role.equals(r)) {
+                    throw StandardException.newException
+                        (SQLState.AUTH_ROLE_GRANT_CIRCULARITY,
+                         role, grantee);
+                }
+            }
+        } finally {
+            rci.close();
+        }
+    }
 
     // OBJECT SHADOWS
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RevokeRoleConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RevokeRoleConstantAction.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RevokeRoleConstantAction.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RevokeRoleConstantAction.java
Sat Jun 21 18:09:48 2008
@@ -93,9 +93,10 @@
                 String grantee = (String)gIter.next();
 
                 // check that role exists
-                RoleGrantDescriptor rd = dd.getRoleDefinitionDescriptor(role);
+                RoleGrantDescriptor rdDef =
+                    dd.getRoleDefinitionDescriptor(role);
 
-                if (rd == null) {
+                if (rdDef == null) {
                     throw StandardException.
                         newException(SQLState.ROLE_INVALID_SPECIFICATION, role);
                 }
@@ -117,10 +118,10 @@
                     // All ok, we are database owner
                     if (SanityManager.DEBUG) {
                         SanityManager.ASSERT(
-                            rd.getGrantee().equals(grantor),
+                            rdDef.getGrantee().equals(grantor),
                             "expected database owner in role grant descriptor");
                         SanityManager.ASSERT(
-                            rd.isWithAdminOption(),
+                            rdDef.isWithAdminOption(),
                             "expected role definition to have ADMIN OPTION");
                     }
                 } else {
@@ -128,7 +129,8 @@
                         (SQLState.AUTH_ROLE_DBO_ONLY, "REVOKE role");
                 }
 
-                rd = dd.getRoleGrantDescriptor(role, grantee, grantor);
+                RoleGrantDescriptor rd =
+                    dd.getRoleGrantDescriptor(role, grantee, grantor);
 
                 if (rd != null && withAdminOption) {
                     // NOTE: Never called yet, withAdminOption not yet
@@ -139,6 +141,13 @@
                     if (rd.isWithAdminOption()) {
                         // Remove old descriptor and add a new one
                         // without admin option.
+
+                        if (SanityManager.DEBUG) {
+                            SanityManager.NOTREACHED();
+                        }
+
+                        // do some invalidation
+
                         rd.drop(lcc);
                         rd.setWithAdminOption(false);
                         dd.addDescriptor(rd,

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/SetRoleConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/SetRoleConstantAction.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/SetRoleConstantAction.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/SetRoleConstantAction.java
Sat Jun 21 18:09:48 2008
@@ -118,14 +118,14 @@
             thisRoleName = dvs.getString();
         }
 
-        RoleGrantDescriptor rd = null;
+        RoleGrantDescriptor rdDef = null;
 
         if (thisRoleName != null) {
             try {
-                rd = dd.getRoleDefinitionDescriptor(thisRoleName);
+                rdDef = dd.getRoleDefinitionDescriptor(thisRoleName);
 
                 // SQL 2003, section 18.3, General rule 4:
-                if (rd == null) {
+                if (rdDef == null) {
                     throw StandardException.newException
                         (SQLState.ROLE_INVALID_SPECIFICATION, thisRoleName);
                 }
@@ -141,6 +141,6 @@
             }
         }
 
-        lcc.setCurrentRole(activation, rd != null ? thisRoleName : null);
+        lcc.setCurrentRole(activation, rdDef != null ? thisRoleName : null);
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Sat Jun 21 18:09:48
2008
@@ -1149,6 +1149,13 @@
             </msg>
 
             <msg>
+                <name>4251C</name>
+                <text>Role {0} cannot be granted to {1} because this would create a
circularity.</text>
+                <arg>authorizationID</arg>
+                <arg>authorizationID</arg>
+            </msg>
+
+            <msg>
                 <name>42601</name>
                 <text>In an ALTER TABLE statement, the column '{0}' has been specified
as NOT NULL and either the DEFAULT clause was not specified or was specified as DEFAULT NULL.</text>
                 <arg>columnName</arg>

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
(original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
Sat Jun 21 18:09:48 2008
@@ -761,6 +761,7 @@
 	String AUTH_INTERNAL_BAD_UUID                                      = "4250E";
     String AUTH_ROLE_DBO_ONLY                                          = "4251A";
 	String AUTH_PUBLIC_ILLEGAL_AUTHORIZATION_ID                        = "4251B";
+	String AUTH_ROLE_GRANT_CIRCULARITY                                 = "4251C";
 
 	String LANG_DB2_NOT_NULL_COLUMN_INVALID_DEFAULT                    = "42601";
 	String LANG_DB2_INVALID_HEXADECIMAL_CONSTANT                    = "42606";

Modified: db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
(original)
+++ db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
Sat Jun 21 18:09:48 2008
@@ -49,6 +49,7 @@
 import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
 import org.apache.derby.iapi.sql.dictionary.RoleGrantDescriptor;
+import org.apache.derby.iapi.sql.dictionary.RoleClosureIterator;
 import org.apache.derby.iapi.sql.dictionary.SubKeyConstraintDescriptor;
 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
 import org.apache.derby.iapi.sql.dictionary.TablePermsDescriptor;
@@ -193,6 +194,15 @@
 		// TODO Auto-generated method stub
 	}
 
+	public RoleClosureIterator createRoleClosureIterator
+		(TransactionController tc,
+		 String role,
+		 boolean inverse
+		) throws StandardException {
+		// TODO Auto-generated method stub
+		return (RoleClosureIterator)null;
+	}
+
 	public void	dropAllPermsByGrantee(String authid,
 									  TransactionController tc)
 			throws StandardException {

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java?rev=670284&r1=670283&r2=670284&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java
Sat Jun 21 18:09:48 2008
@@ -75,6 +75,7 @@
     private final static String userAlreadyExists        = "X0Y68";
     private final static String invalidPUBLIC            = "4251B";
     private final static String loginFailed              = "08004";
+    private final static String roleGrantCircularity     = "4251C";
 
     private int MAX_IDENTIFIER_LENGTH = 128;
     /**
@@ -414,7 +415,7 @@
         doStmt("create role " + users[dboIndex], sqlAuthorizationRequired,
                userAlreadyExists, roleDboOnly);
 
-        // specified with mixed case : DonalDuck
+        // specified with mixed case : DonaldDuck
         doStmt("create role " + users[nonDboIndex],
                 sqlAuthorizationRequired, userAlreadyExists, roleDboOnly);
 
@@ -470,6 +471,7 @@
                                // bar granted to nonDbo, so 4!
                                4);
 
+        checkGrantCircularity();
 
         /*
          * SET ROLE
@@ -685,6 +687,69 @@
         }
     }
 
+
+    private void checkGrantCircularity() {
+        if (isDbo()) {
+            // Test circularity in role grant relation given this a
+            // priori graph:
+            //
+            //          s8
+            //           |   s1<---s3
+            //           | / |
+            //           V/  |
+            //          s4   |
+            //         / |\  |
+            //        /  V \ V
+            //       s5  s6  s2
+            //           |
+            //           s7
+
+            String NA = null; // we only run this as dbo
+
+            for(int i=1; i <= 8; i++) {
+                doStmt("create role s" + i, NA, null, NA);
+            }
+
+            // This establishes the role grant graph shown above. None
+            // of these grants should fail.
+            doStmt("grant s1 to s2", NA, null, NA);
+            doStmt("grant s3 to s1", NA, null, NA);
+            doStmt("grant s1 to s4", NA, null, NA);
+            doStmt("grant s4 to s2", NA, null, NA);
+            doStmt("grant s4 to s6", NA, null, NA);
+            doStmt("grant s4 to s5", NA, null, NA);
+            doStmt("grant s6 to s7", NA, null, NA);
+            doStmt("grant s8 to s4", NA, null, NA);
+
+            // These statements all represent illegal grants in that
+            // they would cause a circularity, so we expect all to
+            // throw.
+            doStmt("grant s1 to s1", NA, roleGrantCircularity, NA);
+            doStmt("grant s2 to s3", NA, roleGrantCircularity, NA);
+            doStmt("grant s2 to s8", NA, roleGrantCircularity, NA);
+            doStmt("grant s7 to s1", NA, roleGrantCircularity, NA);
+            doStmt("grant s7 to s4", NA, roleGrantCircularity, NA);
+            doStmt("grant s7 to s6", NA, roleGrantCircularity, NA);
+            doStmt("grant s7 to s3", NA, roleGrantCircularity, NA);
+            doStmt("grant s2 to s1", NA, roleGrantCircularity, NA);
+            doStmt("grant s2 to s8", NA, roleGrantCircularity, NA);
+            doStmt("grant s2 to s4", NA, roleGrantCircularity, NA);
+            doStmt("grant s6 to s1", NA, roleGrantCircularity, NA);
+            doStmt("grant s6 to s8", NA, roleGrantCircularity, NA);
+            doStmt("grant s6 to s3", NA, roleGrantCircularity, NA);
+            doStmt("grant s6 to s4", NA, roleGrantCircularity, NA);
+            doStmt("grant s5 to s1", NA, roleGrantCircularity, NA);
+            doStmt("grant s5 to s3", NA, roleGrantCircularity, NA);
+            doStmt("grant s5 to s4", NA, roleGrantCircularity, NA);
+            doStmt("grant s5 to s8", NA, roleGrantCircularity, NA);
+
+            for(int i=1; i <= 8; i++) {
+                doStmt("drop role s" + i, NA, null, NA);
+            }
+        }
+    }
+
+
     protected void setUp() throws Exception
     {
         super.setUp();
@@ -844,7 +909,7 @@
             if (_authLevel == NO_SQLAUTHORIZATION) {
                 if (noAuthState[0] == null) {
                     fail("stmt " + stmt + " failed with exception " +
-                         e.getSQLState());
+                         e.getSQLState(), e);
                 } else {
                     assertSQLState("Stmt " + stmt, noAuthState[0], e);
                 }
@@ -853,14 +918,14 @@
                 if (isDbo()) {
                     if (authDboState[0] == null) {
                         fail("stmt " + stmt + " failed with exception " +
-                             e.getSQLState());
+                             e.getSQLState(), e);
                     } else {
                         assertSQLState("Stmt " + stmt, authDboState[0], e);
                     }
                 } else {
                     if (authNotDboState[0] == null) {
                         fail("stmt " + stmt + " failed with exception " +
-                             e.getSQLState());
+                             e.getSQLState(), e);
                     } else {
                         assertSQLState("Stmt " + stmt, authNotDboState[0], e);
                     }
@@ -887,7 +952,7 @@
                  assertSQLState(sqlAuthorizationRequired, e);
                  return;
              } else {
-                 fail("prepare of set role ? failed:" + e);
+                 fail("prepare of set role ? failed:" + e, e);
              }
         }
 
@@ -896,7 +961,7 @@
             int rowcnt = pstmt.executeUpdate();
             assertEquals("rowcount from set role ? not 0", rowcnt, 0);
         } catch (SQLException e) {
-            fail("execute of set role ? failed: [foo]" + e);
+            fail("execute of set role ? failed: [foo]" + e, e);
         }
 
 
@@ -905,7 +970,7 @@
             int rowcnt = pstmt.executeUpdate();
             assertEquals("rowcount from set role ? not 0", rowcnt, 0);
         } catch (SQLException e) {
-            fail("execute of set role ? failed: [NONE] " + e);
+            fail("execute of set role ? failed: [NONE] " + e, e);
         }
 
         if (isDbo()) {
@@ -920,7 +985,7 @@
                 assertRoleInRs(rs, "NONE", n_a);
                 rs.close();
             } catch (SQLException e) {
-                fail("execute of set role ? failed: [NONE] " + e);
+                fail("execute of set role ? failed: [NONE] " + e, e);
             }
         }
 



Mime
View raw message