Return-Path: Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: (qmail 86832 invoked from network); 22 Jun 2008 01:10:11 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 22 Jun 2008 01:10:11 -0000 Received: (qmail 25588 invoked by uid 500); 22 Jun 2008 01:10:13 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 25560 invoked by uid 500); 22 Jun 2008 01:10:12 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 25551 invoked by uid 99); 22 Jun 2008 01:10:12 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 21 Jun 2008 18:10:12 -0700 X-ASF-Spam-Status: No, hits=-1997.6 required=10.0 tests=ALL_TRUSTED,FR_ALMOST_VIAG2 X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 22 Jun 2008 01:09:30 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id ADFBB23889C1; Sat, 21 Jun 2008 18:09:49 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit 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 -0000 To: derby-commits@db.apache.org From: dag@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080622010949.ADFBB23889C1@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 to {@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-1 relation. + * + * The grant relation forms a DAG (directed acyclic graph). + *
+     * 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
+     * 
+ * An iterator on the inverse relation starting at h for the above + * grant graph will return: + *
+     *       closure(h, grant-inv) = {e, b, a1, f, c, a2, d, a3}
+     * 
+ *

+ * An iterator on normal (not inverse) relation starting at a1 for + * the above grant graph will return: + *

+     *       closure(a1, grant)    = {b, j, e, h, f, c}
+     * 
+ * + * @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 , cf. ISO/IEC 9075-2:2003 section 12.4 *or* - * one , section 12.5. + * 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. * * 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. + *
  • Key: rolename,
  • + *
  • Value: List representing a + * grant of that rolename to another role (not user). + *
  • + *
+ * + * 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 + * GRANT role-a TO role-b, or its inverse. + *

+ * The graph is represented as a HashMap where the key is + * the node and the value is a List grant descriptors representing + * outgoing arcs. The set constructed depends on whether inverse + * 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. + *

    + *
  • 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.
  • + *
  • Value: none
  • + *
+ */ + private HashMap seenSoFar; + + /** + * Holds the grant graph. + *
    + *
  • key: role name
  • + *
  • value: list of {@code RoleGrantDescriptor}, making up outgoing arcs + * in graph
  • + *
+ */ + 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-1 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 @@ + 4251C + Role {0} cannot be granted to {1} because this would create a circularity. + authorizationID + authorizationID + + + 42601 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. columnName 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); } }