Return-Path: Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: (qmail 16848 invoked from network); 25 Aug 2008 22:11:58 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 25 Aug 2008 22:11:58 -0000 Received: (qmail 11236 invoked by uid 500); 25 Aug 2008 22:11:56 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 11209 invoked by uid 500); 25 Aug 2008 22:11:56 -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 11200 invoked by uid 99); 25 Aug 2008 22:11:56 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 25 Aug 2008 15:11:56 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED 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; Mon, 25 Aug 2008 22:11:06 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 37994238896B; Mon, 25 Aug 2008 15:11:36 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r688900 [1/2] - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/sql/dictionary/ engine/org/apache/derby/impl/sql/execute/ testing/org/apache/derbyTesting/functionTests/tests/lang/ Date: Mon, 25 Aug 2008 22:11:35 -0000 To: derby-commits@db.apache.org From: dag@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080825221136.37994238896B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: dag Date: Mon Aug 25 15:11:35 2008 New Revision: 688900 URL: http://svn.apache.org/viewvc?rev=688900&view=rev Log: DERBY-3223 SQL roles: make use of privileges granted to roles in actual privilege checking Patch derby-3223-revocation-logic-5, which adds revocation logic for roles. Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConstraintDescriptor.java db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementColumnPermission.java db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementRoutinePermission.java db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementTablePermission.java db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TriggerDescriptor.java db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ViewDescriptor.java db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DDLConstantAction.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/RevokeRoleConstantAction.java db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/SetRoleConstantAction.java db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesConferredPrivilegesTest.java db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConstraintDescriptor.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConstraintDescriptor.java?rev=688900&r1=688899&r2=688900&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConstraintDescriptor.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConstraintDescriptor.java Mon Aug 25 15:11:35 2008 @@ -556,7 +556,9 @@ //types SELECT, UPDATE, DELETE, INSERT, REFERENCES, TRIGGER), we //don't do anything here. Later in makeInvalid method, we make //the ConstraintDescriptor drop itself. + //Ditto for role grant conferring a privilege. case DependencyManager.REVOKE_PRIVILEGE: + case DependencyManager.REVOKE_ROLE: case DependencyManager.INTERNAL_RECOMPILE_REQUEST: break; @@ -600,14 +602,22 @@ * REVOKE_PRIVILEGE are the only valid actions */ - //Let's handle REVOKE_PRIVILEGE first - if (action == DependencyManager.REVOKE_PRIVILEGE) + //Let's handle REVOKE_PRIVILEGE and REVOKE_ROLE first + if (action == DependencyManager.REVOKE_PRIVILEGE || + action == DependencyManager.REVOKE_ROLE) { //At this point (Derby 10.2), only a FOREIGN KEY key constraint can //depend on a privilege. None of the other constraint types //can be dependent on a privilege becuse those constraint types //can not reference a table/routine. ConglomerateDescriptor newBackingConglomCD = drop(lcc, true); + + lcc.getLastActivation().addWarning( + StandardException.newWarning( + SQLState.LANG_CONSTRAINT_DROPPED, + getConstraintName(), + getTableDescriptor().getName())); + if (newBackingConglomCD != null) { /* Since foreign keys can never be unique, and since Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementColumnPermission.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementColumnPermission.java?rev=688900&r1=688899&r2=688900&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementColumnPermission.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementColumnPermission.java Mon Aug 25 15:11:35 2008 @@ -28,6 +28,7 @@ import org.apache.derby.iapi.services.io.FormatableBitSet; import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; import org.apache.derby.iapi.sql.Activation; +import org.apache.derby.iapi.sql.execute.ExecPreparedStatement; /** * This class describes a column permission used (required) by a statement. @@ -92,9 +93,10 @@ throws StandardException { DataDictionary dd = lcc.getDataDictionary(); + ExecPreparedStatement ps = activation.getPreparedStatement(); if (hasPermissionOnTable(lcc, activation, - authorizationId, forGrant)) { + authorizationId, forGrant, ps)) { return; } @@ -121,11 +123,11 @@ FormatableBitSet unresolvedColumns = (FormatableBitSet)columns.clone(); - for( int i = unresolvedColumns.anySetBit(); + for (int i = unresolvedColumns.anySetBit(); i >= 0; i = unresolvedColumns.anySetBit(i)) { - if( permittedColumns != null && permittedColumns.get(i)) { + if (permittedColumns != null && permittedColumns.get(i)) { // column i (zero-based here) accounted for: unresolvedColumns.clear(i); } @@ -205,7 +207,24 @@ td.getSchemaName(), td.getName()); } + } else { + // We found and successfully applied a role to resolve the + // remaining required permissions. + // + // So add a dependency on the role (qua provider), so that + // if role is no longer available to the current user + // (e.g. grant to user is revoked, role is dropped, + // another role has been set), or it is impacted by + // revoked permissions or other roles granted to it, we + // are able to invalidate the the ps. + // + // FIXME: Rather invalidate Activation so other + // sessions sharing the same ps are not impacted!! + dd.getDependencyManager(). + addDependency(ps, dd.getRoleDefinitionDescriptor(role), + lcc.getContextManager()); } + } // end of check /** @@ -297,6 +316,65 @@ } /** + * Returns false if the current role is necessary to cover + * the necessary permission(s). + * @param authid authentication id of the current user + * @param dd data dictionary + * + * @return false if the current role is required + */ + public boolean allColumnsCoveredByUserOrPUBLIC(String authid, + DataDictionary dd) + throws StandardException { + + ColPermsDescriptor colsPermsDesc = + dd.getColumnPermissions(tableUUID, privType, false, authid); + FormatableBitSet permittedColumns = colsPermsDesc.getColumns(); + FormatableBitSet unresolvedColumns = (FormatableBitSet)columns.clone(); + boolean result = true; + + if (permittedColumns != null) { // else none at user level + for(int i = unresolvedColumns.anySetBit(); + i >= 0; + i = unresolvedColumns.anySetBit(i)) { + + if(permittedColumns.get(i)) { + unresolvedColumns.clear(i); + } + } + } + + + if (unresolvedColumns.anySetBit() >= 0) { + colsPermsDesc = + dd.getColumnPermissions( + tableUUID, privType, false, + Authorizer.PUBLIC_AUTHORIZATION_ID); + permittedColumns = colsPermsDesc.getColumns(); + + if (permittedColumns != null) { // else none at public level + for(int i = unresolvedColumns.anySetBit(); + i >= 0; + i = unresolvedColumns.anySetBit(i)) { + + if(permittedColumns.get(i)) { + unresolvedColumns.clear(i); + } + } + } + + if (unresolvedColumns.anySetBit() >= 0) { + // even after trying all grants to user and public there + // are unresolved columns so role must have been used. + result = false; + } + } + + return result; + } + + + /** * Given the set of yet unresolved column permissions, try to use * the supplied role r to resolve them. After this is done, return * the set of columns still unresolved. If the role is used for Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementRoutinePermission.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementRoutinePermission.java?rev=688900&r1=688899&r2=688900&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementRoutinePermission.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementRoutinePermission.java Mon Aug 25 15:11:35 2008 @@ -29,6 +29,7 @@ import org.apache.derby.iapi.sql.dictionary.RoutinePermsDescriptor; import org.apache.derby.iapi.store.access.TransactionController; import org.apache.derby.iapi.sql.Activation; +import org.apache.derby.iapi.sql.execute.ExecPreparedStatement; /** * This class describes a routine execute permission @@ -64,6 +65,7 @@ { DataDictionary dd = lcc.getDataDictionary(); TransactionController tc = lcc.getTransactionExecute(); + ExecPreparedStatement ps = activation.getPreparedStatement(); RoutinePermsDescriptor perms = dd.getRoutinePermissions( routineUUID, authorizationId); if( perms == null || ! perms.getHasExecutePermission()) @@ -76,7 +78,7 @@ boolean resolved = false; - // Since permission does not exists for the current user or PUBLIC, + // Since no permission exists for the current user or PUBLIC, // check if a permission exists for the current role (if set). String role = lcc.getCurrentRoleId(activation); @@ -84,7 +86,7 @@ // Check that role is still granted to current user or // to PUBLIC: A revoked role which is current for this - // session, is lazily set to none when it is attempted + // session, is lazily set to none when it is attemped // used. String dbo = dd.getAuthorizationDatabaseOwner(); RoleGrantDescriptor rd = dd.getRoleGrantDescriptor @@ -127,6 +129,20 @@ } } } + + if (resolved /* using a role*/) { + // Also add a dependency on the role (qua provider), + // so that if role is no longer available to the + // current user (e.g. grant is revoked, role is + // dropped, another role has been set), we are able to + // invalidate the the ps. + // + // FIXME: Rather invalidate Activation so other + // sessions sharing the same ps are not impacted!! + dd.getDependencyManager(). + addDependency(ps, dd.getRoleDefinitionDescriptor(role), + lcc.getContextManager()); + } } if (!resolved) { Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementTablePermission.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementTablePermission.java?rev=688900&r1=688899&r2=688900&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementTablePermission.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementTablePermission.java Mon Aug 25 15:11:35 2008 @@ -27,6 +27,7 @@ import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; import org.apache.derby.iapi.reference.SQLState; import org.apache.derby.iapi.sql.Activation; +import org.apache.derby.iapi.sql.execute.ExecPreparedStatement; /** * This class describes a table permission required by a statement. @@ -113,9 +114,10 @@ throws StandardException { DataDictionary dd = lcc.getDataDictionary(); + ExecPreparedStatement ps = activation.getPreparedStatement(); if (!hasPermissionOnTable(lcc, activation, - authorizationId, forGrant)) { + authorizationId, forGrant, ps)) { TableDescriptor td = getTableDescriptor( dd); throw StandardException.newException( forGrant ? SQLState.AUTH_NO_TABLE_PERMISSION_FOR_GRANT : SQLState.AUTH_NO_TABLE_PERMISSION, @@ -134,14 +136,23 @@ return td; } // end of getTableDescriptor - /* + /** * Check if current session has permission on the table (current user, - * PUBLIC or role). + * PUBLIC or role) and, if applicable, register a dependency of ps on the + * current role. + * + * @param lcc the current language connection context + * @param activation the activation of ps + * @param authorizationId the id of the current user + * @param forGrant true if FOR GRANT is required + * @param ps the prepared statement for which we are checking necessary + * privileges */ protected boolean hasPermissionOnTable(LanguageConnectionContext lcc, Activation activation, String authorizationId, - boolean forGrant) + boolean forGrant, + ExecPreparedStatement ps) throws StandardException { DataDictionary dd = lcc.getDataDictionary(); @@ -200,6 +211,21 @@ result = oneAuthHasPermissionOnTable (dd, r, forGrant); } + + if (result) { + // Also add a dependency on the role (qua provider), + // so that if role is no longer available to the + // current user (e.g. grant is revoked, role is + // dropped, another role has been set), we are able to + // invalidate the the ps. + // + // FIXME: Rather invalidate Activation so other + // sessions sharing the same ps are not impacted!! + dd.getDependencyManager(). + addDependency(ps, + dd.getRoleDefinitionDescriptor(role), + lcc.getContextManager()); + } } } } Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TriggerDescriptor.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TriggerDescriptor.java?rev=688900&r1=688899&r2=688900&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TriggerDescriptor.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TriggerDescriptor.java Mon Aug 25 15:11:35 2008 @@ -702,8 +702,15 @@ // When REVOKE_PRIVILEGE gets sent (this happens for privilege // types SELECT, UPDATE, DELETE, INSERT, REFERENCES, TRIGGER), we // make the TriggerDescriptor drop itself. + // Ditto for revoking a role conferring a privilege. case DependencyManager.REVOKE_PRIVILEGE: + case DependencyManager.REVOKE_ROLE: drop(lcc); + + lcc.getLastActivation().addWarning( + StandardException.newWarning( + SQLState.LANG_TRIGGER_DROPPED, + this.getObjectName() )); break; default: @@ -717,7 +724,6 @@ DataDictionary dd = getDataDictionary(); DependencyManager dm = getDataDictionary().getDependencyManager(); TransactionController tc = lcc.getTransactionExecute(); - dm.invalidateFor(this, DependencyManager.DROP_TRIGGER, lcc); // Drop the trigger Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ViewDescriptor.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ViewDescriptor.java?rev=688900&r1=688899&r2=688900&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ViewDescriptor.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ViewDescriptor.java Mon Aug 25 15:11:35 2008 @@ -274,6 +274,12 @@ //the ViewDescriptor drop itself. case DependencyManager.REVOKE_PRIVILEGE: + // When a role grant is revoked, any a view dependent on priviliges + // obtained through that role grant will dropped. We don't do + // anything here. Later in makeInvalid method, we make the + // ViewDescriptor drop itself. + case DependencyManager.REVOKE_ROLE: + // When REVOKE_PRIVILEGE gets sent to a // TablePermsDescriptor we must also send // INTERNAL_RECOMPILE_REQUEST to its Dependents which @@ -352,6 +358,7 @@ //make the ViewDescriptor drop itself. case DependencyManager.REVOKE_PRIVILEGE: case DependencyManager.DROP_COLUMN: + case DependencyManager.REVOKE_ROLE: drop(lcc, getDataDictionary().getTableDescriptor(uuid).getSchemaDescriptor(), getDataDictionary().getTableDescriptor(uuid)); Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DDLConstantAction.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DDLConstantAction.java?rev=688900&r1=688899&r2=688900&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DDLConstantAction.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/DDLConstantAction.java Mon Aug 25 15:11:35 2008 @@ -27,8 +27,6 @@ import org.apache.derby.catalog.UUID; import org.apache.derby.iapi.error.StandardException; import org.apache.derby.iapi.reference.SQLState; -import org.apache.derby.iapi.reference.Property; -import org.apache.derby.iapi.services.property.PropertyUtil; import org.apache.derby.iapi.services.sanity.SanityManager; import org.apache.derby.iapi.sql.Activation; import org.apache.derby.iapi.sql.conn.Authorizer; @@ -39,15 +37,19 @@ import org.apache.derby.iapi.sql.dictionary.ColPermsDescriptor; import org.apache.derby.iapi.sql.dictionary.DataDictionary; import org.apache.derby.iapi.sql.dictionary.PermissionsDescriptor; +import org.apache.derby.iapi.sql.dictionary.RoleGrantDescriptor; import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor; import org.apache.derby.iapi.sql.dictionary.StatementColumnPermission; import org.apache.derby.iapi.sql.dictionary.StatementPermission; -import org.apache.derby.iapi.sql.dictionary.StatementRoutinePermission; import org.apache.derby.iapi.sql.dictionary.StatementSchemaPermission; +import org.apache.derby.iapi.sql.dictionary.StatementRolePermission; +import org.apache.derby.iapi.sql.dictionary.StatementRoutinePermission; import org.apache.derby.iapi.sql.dictionary.StatementTablePermission; +import org.apache.derby.iapi.sql.dictionary.RoleClosureIterator; import org.apache.derby.iapi.sql.execute.ConstantAction; import org.apache.derby.iapi.store.access.ConglomerateController; import org.apache.derby.iapi.store.access.TransactionController; +import org.apache.derby.iapi.services.sanity.SanityManager; /** * Abstract class that has actions that are across @@ -282,6 +284,9 @@ * statement. Because of these differences between constraints and views * (and triggers), there are 2 different methods in this class to save * their privileges in the dependency system. + * + * For each required privilege, we now register a dependency on a role if + * that role was required to find an applicable privilege. * * @param activation The execution environment for this constant action. * @param dependent Make this object depend on required privileges @@ -300,17 +305,21 @@ LanguageConnectionContext lcc = activation.getLanguageConnectionContext(); DataDictionary dd = lcc.getDataDictionary(); DependencyManager dm = dd.getDependencyManager(); - + String dbo = dd.getAuthorizationDatabaseOwner(); + String authId = lcc.getAuthorizationId(); + SettableBoolean roleDepAdded = new SettableBoolean(); + //If the Database Owner is creating this constraint, then no need to //collect any privilege dependencies because the Database Owner can //access any objects without any restrictions if (!(lcc.getAuthorizationId().equals(dd.getAuthorizationDatabaseOwner()))) { PermissionsDescriptor permDesc; - // Now, it is time to add into dependency system, constraint's - // dependency on REFERENCES or, if it is a CHECK constraint, any - // EXECUTE privileges. If the REFERENCES is revoked from the - // constraint owner, the constraint will get dropped automatically. + // Now, it is time to add into dependency system the FOREIGN + // constraint's dependency on REFERENCES privilege, or, if it is a + // CHECK constraint, any EXECUTE privileges. If the REFERENCES is + // revoked from the constraint owner, the constraint will get + // dropped automatically. List requiredPermissionsList = activation.getPreparedStatement().getRequiredPermissionsList(); if (requiredPermissionsList != null && ! requiredPermissionsList.isEmpty()) @@ -336,7 +345,8 @@ //privilege in the required privileges list if (!statementTablePermission.getTableUUID().equals(refTableUUID)) continue; - } else if (statPerm instanceof StatementSchemaPermission) { + } else if (statPerm instanceof StatementSchemaPermission + || statPerm instanceof StatementRolePermission) { continue; } else { if (SanityManager.DEBUG) { @@ -363,18 +373,45 @@ // individual column levels. In addition, individual column // REFERENCES privilege could be available at the user // level, PUBLIC or role level. EXECUTE privilege could be - // available at the user or PUBLIC level. + // available at the user level, PUBLIC or role level. permDesc = statPerm.getPermissionDescriptor(lcc.getAuthorizationId(), dd); if (permDesc == null) { - //No REFERENCES privilege exists for given - //authorizer at table or column level. - //REFERENCES privilege has to exist at at PUBLIC level + // No privilege exists for given user. The privilege + // has to exist at at PUBLIC level.... + permDesc = statPerm.getPermissionDescriptor(Authorizer.PUBLIC_AUTHORIZATION_ID, dd); - if (permDesc != null && - !(permDesc.checkOwner(lcc.getAuthorizationId()))) - dm.addDependency(dependent, permDesc, lcc.getContextManager()); - } else + // .... or at the role level. Additionally, for column + // level privileges, even if *some* were available at + // the PUBLIC level others may be still be missing, + // hence the call in the test below to + // allColumnsCoveredByUserOrPUBLIC. + boolean roleUsed = false; + + if (permDesc == null || + ((permDesc instanceof ColPermsDescriptor) && + !(((StatementColumnPermission)statPerm). + allColumnsCoveredByUserOrPUBLIC + (lcc.getAuthorizationId(), dd)))) { + roleUsed = true; + permDesc = findRoleUsage(activation, statPerm); + } + + // If the user accessing the object is the owner of + // that object, then no privilege tracking is needed + // for the owner. + if (!(permDesc.checkOwner(lcc.getAuthorizationId()))) { + dm.addDependency(dependent, permDesc, + lcc.getContextManager()); + + if (roleUsed) { + // We had to rely on role, so track that + // dependency, too. + trackRoleDependency + (activation, dependent, roleDepAdded); + } + } + } else //if the object on which permission is required is owned by the //same user as the current user, then no need to keep that //object's privilege dependency in the dependency system @@ -383,37 +420,166 @@ dm.addDependency(dependent, permDesc, lcc.getContextManager()); if (permDesc instanceof ColPermsDescriptor) { - //The if statement above means we found a - //REFERENCES privilege at column level for - //the given authorizer. If this privilege - //doesn't cover all the column , then there - //has to exisit REFERENCES for the remaining - //columns at PUBLIC level. Get that permission - //descriptor and save it in dependency system - StatementColumnPermission statementColumnPermission = (StatementColumnPermission) statPerm; - permDesc = statementColumnPermission.getPUBLIClevelColPermsDescriptor(lcc.getAuthorizationId(), dd); + // The if statement above means we found a + // REFERENCES privilege at column level for the + // given authorizer. If this privilege doesn't + // cover all the column , then there has to exisit + // REFERENCES for the remaining columns at PUBLIC + // level or at role level. Get that permission + // descriptor and save it in dependency system + StatementColumnPermission + statementColumnPermission = ( + StatementColumnPermission)statPerm; + permDesc = statementColumnPermission. + getPUBLIClevelColPermsDescriptor + (lcc.getAuthorizationId(), dd); //Following if checks if some column level privileges //exist only at public level. If so, then the public //level column privilege dependency is added //into the dependency system - if (permDesc != null) - dm.addDependency(dependent, permDesc, lcc.getContextManager()); + if (permDesc != null && + permDesc.getObjectID() != null) { + // User did not have all required column + // permissions and at least one column is + // covered by PUBLIC. + dm.addDependency(dependent, permDesc, + lcc.getContextManager()); + } + // Possibly, the current role has also been relied + // upon. + if (!statementColumnPermission. + allColumnsCoveredByUserOrPUBLIC + (lcc.getAuthorizationId(), dd)) { + // Role has been relied upon, so register a + // dependency. + trackRoleDependency + (activation, dependent, roleDepAdded); + } } } - if (statPerm instanceof StatementTablePermission) { + if (!(statPerm instanceof StatementRoutinePermission)) { //We have found the REFERENCES privilege for all the //columns in foreign key constraint and we don't //need to go through the rest of the privileges //for this sql statement. break; + } else { + // For EXECUTE privilege there may be several functions + // referenced in the constraint, so continue looking. } } } } } - + + + /** + * We have determined that the statement permission described by statPerm + * is not granted to the current user nor to PUBLIC, so it must be granted + * to the current role or one of the roles inherited by the current + * role. Find the relevant permission descriptor and return it. + * + * @return the permission descriptor that yielded the privilege + */ + private static PermissionsDescriptor findRoleUsage + (Activation activation, + StatementPermission statPerm) throws StandardException { + + LanguageConnectionContext lcc = + activation.getLanguageConnectionContext(); + DataDictionary dd = lcc.getDataDictionary(); + RoleGrantDescriptor rootGrant = null; + String role = lcc.getCurrentRoleId(activation); + String dbo = dd.getAuthorizationDatabaseOwner(); + String authId = lcc.getAuthorizationId(); + PermissionsDescriptor permDesc = null; + + if (SanityManager.DEBUG) { + SanityManager.ASSERT( + role != null, + "Unexpected: current role is not set"); + } + + // determine how we got to be able use this role + rootGrant = + dd.getRoleGrantDescriptor(role, authId, dbo); + + if (rootGrant == null) { + rootGrant = dd.getRoleGrantDescriptor( + role, + Authorizer.PUBLIC_AUTHORIZATION_ID, + dbo); + } + + // If not found in current role, get transitive + // closure of roles granted to current role and + // iterate over it to see if permission has + // been granted to any of the roles the current + // role inherits. + RoleClosureIterator rci = + dd.createRoleClosureIterator + (activation.getTransactionController(), + role, true /* inverse relation*/); + + String graphGrant; + while (permDesc == null && + (graphGrant = rci.next()) != null) { + permDesc = + statPerm.getPermissionDescriptor + (graphGrant, dd); + } + + if (SanityManager.DEBUG) { + SanityManager.ASSERT( + permDesc != null, + "Unexpected: Permission needs to be found via role"); + } + + return permDesc; + } + + + /** + * The statement permission needed for dependent has been found to rely on + * the current role. If not already done, register the dependency so that + * if the current role (or any of the roles it inherits) is revoked (or + * dropped), we can invalidate dependent. + * + * @param activation the current activation + * @param dependent the view, constraint or trigger that is dependent on the + * current role for some privilege. + * @param roleDepAdded keeps track of whether a dependeny on the + * current role has aleady been registered. + */ + private static void trackRoleDependency(Activation activation, + Dependent dependent, + SettableBoolean roleDepAdded) + throws StandardException { + + // We only register the dependency once, lest + // we get duplicates in SYSDEPENDS (duplicates + // are not healthy..invalidating more than once + // fails for triggers at least). + if (!roleDepAdded.get()) { + LanguageConnectionContext lcc = + activation.getLanguageConnectionContext(); + DataDictionary dd = lcc.getDataDictionary(); + DependencyManager dm = dd.getDependencyManager(); + + String role = + lcc.getCurrentRoleId(activation); + RoleGrantDescriptor rgd = + dd.getRoleDefinitionDescriptor(role); + + dm.addDependency + (dependent, rgd, + lcc.getContextManager()); + roleDepAdded.set(true); + } + } + /** * This method saves dependencies of views and triggers on privileges in * the dependency system. It gets called by CreateViewConstantAction @@ -437,7 +603,10 @@ * to identify right privileges for right constraints for a given sql * statement. Because of these differences between constraints and views * (and triggers), there are 2 different methods in this class to save - * their privileges in the dependency system. + * their privileges in the dependency system. + * + * For each required privilege, we now register of a dependency on a role + * if that role was required to find an applicable privilege. * * @param activation The execution environment for this constant action. * @param dependent Make this object depend on required privileges @@ -451,11 +620,14 @@ LanguageConnectionContext lcc = activation.getLanguageConnectionContext(); DataDictionary dd = lcc.getDataDictionary(); DependencyManager dm = dd.getDependencyManager(); - - //If the Database Owner is creating this view/triiger, then no need to - //collect any privilege dependencies because the Database Owner can - //access any objects without any restrictions - if (!(lcc.getAuthorizationId().equals(dd.getAuthorizationDatabaseOwner()))) + String dbo = dd.getAuthorizationDatabaseOwner(); + String authId = lcc.getAuthorizationId(); + SettableBoolean roleDepAdded = new SettableBoolean(); + + // If the Database Owner is creating this view/trigger, then no need to + // collect any privilege dependencies because the Database Owner can + // access any objects without any restrictions. + if (!authId.equals(dbo)) { PermissionsDescriptor permDesc; List requiredPermissionsList = activation.getPreparedStatement().getRequiredPermissionsList(); @@ -470,22 +642,55 @@ //But we don't need to add schema permission to list of //permissions that the object is dependent on once it is //created. - if (statPerm instanceof StatementSchemaPermission) + //Also, StatementRolePermission should not occur here. + if (statPerm instanceof StatementSchemaPermission || + statPerm instanceof StatementRolePermission) { + + if (SanityManager.DEBUG) { + if (statPerm instanceof StatementRolePermission) { + SanityManager.THROWASSERT( + "Unexpected StatementRolePermission"); + } + } + continue; + } + //See if we can find the required privilege for given authorizer? permDesc = statPerm.getPermissionDescriptor(lcc.getAuthorizationId(), dd); if (permDesc == null)//privilege not found for given authorizer { //The if condition above means that required privilege does //not exist at the user level. The privilege has to exist at - //PUBLIC level. - permDesc = statPerm.getPermissionDescriptor(Authorizer.PUBLIC_AUTHORIZATION_ID, dd); + //PUBLIC level... , + permDesc = statPerm.getPermissionDescriptor( + Authorizer.PUBLIC_AUTHORIZATION_ID, dd); + + boolean roleUsed = false; + + // .. or at role level + if (permDesc == null || + ((permDesc instanceof ColPermsDescriptor) && + !(((StatementColumnPermission)statPerm). + allColumnsCoveredByUserOrPUBLIC + (lcc.getAuthorizationId(), dd)))) { + roleUsed = true; + permDesc = findRoleUsage(activation, statPerm); + } + //If the user accessing the object is the owner of that //object, then no privilege tracking is needed for the //owner. - if (permDesc != null && - !(permDesc.checkOwner(lcc.getAuthorizationId()))) + if (!(permDesc.checkOwner(lcc.getAuthorizationId()))) { dm.addDependency(dependent, permDesc, lcc.getContextManager()); + + // We had to rely on role, so track that + // dependency, too. + if (roleUsed) { + trackRoleDependency + (activation, dependent, roleDepAdded); + } + } continue; } //if the object on which permission is required is owned by the @@ -519,15 +724,30 @@ permDesc = statementColumnPermission.getPUBLIClevelColPermsDescriptor(lcc.getAuthorizationId(), dd); //Following if checks if some column level privileges //exist only at public level. If so, then the public - //level column privilege dependency of view is added - //into dependency system. - if (permDesc != null) - dm.addDependency(dependent, permDesc, lcc.getContextManager()); + //level column privilege, if any, dependency of + //view is added into dependency system. + + if (permDesc != null && + permDesc.getObjectID() != null) { + // User did not have all required column + // permissions and at least one column is + // covered by PUBLIC. + dm.addDependency(dependent, permDesc, + lcc.getContextManager()); + } // else nothing found for PUBLIC.. + + // Has the the current role has also been relied + // upon? + if (!statementColumnPermission. + allColumnsCoveredByUserOrPUBLIC + (lcc.getAuthorizationId(), dd)) { + trackRoleDependency + (activation, dependent, roleDepAdded); + } } } } } - } } @@ -543,5 +763,24 @@ } return false; } + + /** + * Mutable Boolean wrapper, initially false + */ + private class SettableBoolean { + boolean value; + + SettableBoolean() { + value = false; + } + + void set(boolean b) { + value = b; + } + + boolean get() { + return value; + } + } } 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=688900&r1=688899&r2=688900&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 Mon Aug 25 15:11:35 2008 @@ -21,13 +21,13 @@ package org.apache.derby.impl.sql.execute; -import org.apache.derby.iapi.sql.execute.ConstantAction; - import org.apache.derby.iapi.error.StandardException; import org.apache.derby.iapi.sql.Activation; import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; +import org.apache.derby.iapi.sql.depend.DependencyManager; import org.apache.derby.iapi.sql.dictionary.DataDictionary; import org.apache.derby.iapi.sql.dictionary.RoleGrantDescriptor; +import org.apache.derby.iapi.sql.dictionary.RoleClosureIterator; import org.apache.derby.shared.common.reference.SQLState; import org.apache.derby.iapi.store.access.TransactionController; @@ -74,11 +74,9 @@ /** - * This is the guts of the Execution-time logic for DROP ROLE. - * - * @see ConstantAction#executeConstantAction + * This is the guts of the Execution-time logic for DROP ROLE. * - * @exception StandardException Thrown on failure + * @see org.apache.derby.iapi.sql.execute.ConstantAction#executeConstantAction */ public void executeConstantAction( Activation activation ) throws StandardException @@ -106,6 +104,30 @@ SQLState.ROLE_INVALID_SPECIFICATION, roleName); } + // When a role is dropped, for every role in its grantee closure, we + // call two invalidate actions. REVOKE_ROLE and + // INTERNAL_RECOMPILE_REQUEST. The latter is used to force + // recompilation of dependent prepared statements, the former to drop + // dependent objects (constraints, triggers and views). Note that + // until DERBY-1632 is fixed, we risk dropping objects not really + // dependent on this role, but one some other role just because it + // inherits from this one. See also RevokeRoleConstantAction. + RoleClosureIterator rci = + dd.createRoleClosureIterator + (activation.getTransactionController(), + roleName, false); + + String role; + while ((role = rci.next()) != null) { + RoleGrantDescriptor r = dd.getRoleDefinitionDescriptor(role); + + dd.getDependencyManager().invalidateFor + (r, DependencyManager.REVOKE_ROLE, lcc); + + dd.getDependencyManager().invalidateFor + (r, DependencyManager.INTERNAL_RECOMPILE_REQUEST, lcc); + } + rdDef.drop(lcc); /* 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=688900&r1=688899&r2=688900&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 Mon Aug 25 15:11:35 2008 @@ -21,16 +21,16 @@ package org.apache.derby.impl.sql.execute; -import org.apache.derby.iapi.sql.execute.ConstantAction; - import java.util.Iterator; import java.util.List; import org.apache.derby.iapi.error.StandardException; import org.apache.derby.iapi.sql.Activation; import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; +import org.apache.derby.iapi.sql.depend.DependencyManager; import org.apache.derby.iapi.sql.conn.Authorizer; 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; @@ -63,11 +63,9 @@ // INTERFACE METHODS /** - * This is the guts of the Execution-time logic for REVOKE role. - * - * @see ConstantAction#executeConstantAction + * This is the guts of the Execution-time logic for REVOKE role. * - * @exception StandardException Thrown on failure + * @see org.apache.derby.iapi.sql.execute.ConstantAction#executeConstantAction */ public void executeConstantAction(Activation activation) throws StandardException { @@ -146,15 +144,33 @@ SanityManager.NOTREACHED(); } - // do some invalidation - - rd.drop(lcc); - rd.setWithAdminOption(false); - dd.addDescriptor(rd, - null, // parent - DataDictionary.SYSROLES_CATALOG_NUM, - false, // no duplicatesAllowed - tc); + // Do invalidation. + // + // RoleClosureIterator rci = + // dd.createRoleClosureIterator + // (activation.getTransactionController(), + // role, false); + // + // String r; + // while ((r = rci.next()) != null) { + // rdDef = dd.getRoleDefinitionDescriptor(r); + // + // dd.getDependencyManager().invalidateFor + // (rdDef, DependencyManager.REVOKE_ROLE, lcc); + // + // dd.getDependencyManager().invalidateFor + // (rdDef, + // DependencyManager.INTERNAL_RECOMPILE_REQUEST, + // lcc); + // } + // + // rd.drop(lcc); + // rd.setWithAdminOption(false); + // dd.addDescriptor(rd, + // null, // parent + // DataDictionary.SYSROLES_CATALOG_NUM, + // false, // no duplicatesAllowed + // tc); } else { activation.addWarning (StandardException.newWarning @@ -162,9 +178,37 @@ role, grantee)); } } else if (rd != null) { - // normal revoke of role from grantee + // Normal revoke of role from grantee. // + // When a role is revoked, for every role in its grantee + // closure, we call two invalidate actions. REVOKE_ROLE + // and INTERNAL_RECOMPILE_REQUEST. The latter is used to + // force recompilation of dependent prepared statements, + // the former to drop dependent objects (constraints, + // triggers and views). Note that until DERBY-1632 is + // fixed, we risk dropping objects not really dependent on + // this role, but one some other role just because it + // inherits from this one. + RoleClosureIterator rci = + dd.createRoleClosureIterator + (activation.getTransactionController(), + role, false); + + String r; + while ((r = rci.next()) != null) { + rdDef = dd.getRoleDefinitionDescriptor(r); + + dd.getDependencyManager().invalidateFor + (rdDef, DependencyManager.REVOKE_ROLE, lcc); + + dd.getDependencyManager().invalidateFor + (rdDef, + DependencyManager.INTERNAL_RECOMPILE_REQUEST, + lcc); + } + rd.drop(lcc); + } else { activation.addWarning (StandardException.newWarning 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=688900&r1=688899&r2=688900&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 Mon Aug 25 15:11:35 2008 @@ -28,9 +28,8 @@ import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; import org.apache.derby.iapi.sql.ParameterValueSet; import org.apache.derby.iapi.sql.StatementType; -import org.apache.derby.iapi.sql.conn.Authorizer; +import org.apache.derby.iapi.sql.depend.DependencyManager; import org.apache.derby.iapi.types.DataValueDescriptor; -import org.apache.derby.iapi.reference.Limits; import org.apache.derby.iapi.error.StandardException; import org.apache.derby.iapi.sql.Activation; import org.apache.derby.iapi.reference.SQLState; @@ -120,8 +119,21 @@ RoleGrantDescriptor rdDef = null; - if (thisRoleName != null) { - try { + try { + String oldRole = lcc.getCurrentRoleId(activation); + + if (oldRole != null && !oldRole.equals(thisRoleName)) { + rdDef = dd.getRoleDefinitionDescriptor(oldRole); + + if (rdDef != null) { + dd.getDependencyManager().invalidateFor( + rdDef, + DependencyManager.INTERNAL_RECOMPILE_REQUEST, + lcc); + } // else: old role else no longer exists, so ignore. + } + + if (thisRoleName != null) { rdDef = dd.getRoleDefinitionDescriptor(thisRoleName); // SQL 2003, section 18.3, General rule 4: @@ -135,10 +147,10 @@ (SQLState. ROLE_INVALID_SPECIFICATION_NOT_GRANTED, thisRoleName); } - } finally { - // reading above changes idle state, so reestablish it - lcc.userCommit(); } + } finally { + // reading above changes idle state, so reestablish it + lcc.userCommit(); } lcc.setCurrentRole(activation, rdDef != null ? thisRoleName : null);