db-derby-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Mamta Satoor" <msat...@gmail.com>
Subject [Grant/Revoke]Proposal for invoker/definer model
Date Mon, 13 Mar 2006 18:49:34 GMT
While going through some of the Derbylist mails on Grant/Revoke, I realized
that various database objects in a sqlStandard mode may require switching
authorizers from the invoker to different definers and vice versa. This
piece of task sounds interesting to me and hence I have following proposal
for implementing it.

Let's start with specific case of views, which in sqlStandard mode will
always run with the definer authorization.
say view v1 is defined by user "u1", and it does a select from a view v2
defined by user "u2".  And view v2 does a select from table t2, which is
also owned by user "u2".
Now, when user "u1", after connecting to the database, runs select * from
v1; the authorizers involved at different times during the execution of this
sql will be different. First authorizer involved will always be for "u1",
since "u1" is the user who has made connection to the database. This
first authorizer is also the invoker authorizer. At the start of execution
of select * from v1 by user "u1", since v1 is owned by "u1", the authorizer
for "u1" should be used at that point. Next during the
execution of select * from v1, Derby is going to access v2 which is defined
by user "u2" and hence, while accessing v2, the authorizer used should be
corresponding to definer"u2". When the accessing of v2 is over, Derby should
discard the authorizer for view definer "u2" and go back to authorizer for
invoker "u1" to finish exeucting the rest of select * from v1. And at last,
when execution of select * from v1 is over, the only user authorizer active
should be the one for user "u1", who had made connection to the database for
this session.

I am considering implementing above scenario of switching from one
authorizer to another by keeping a stack of authorizers in
GenericLanguageConnectionContext. Prior to intoduction of sqlStandard mode
for Grant/Revoke, Derby was written to run everything with invoker as the
authorizer. This single invoker authorizer information is currently kept in
GenericLanguageConnectionContext's authorizer(GenericAuthorizer) field. This
field gets set to invoker's authorizer when GenericLanguageConnectionContext
gets initialized at the database connect time. This happens in
GenericLanguageConnectionContext.initilize method. In addition to
initilizing authorizer, this method also sets the default schema to a schema
with the same name as the invoker. So, as we can see,
GenericLanguageConnectionContext currently just deals with one authorizer
and that authorizer is always the authorizer corresponding to the user who
has made connection to the database.

But with the addition of Grant/Revoke to Derby, we can have different
authorizers active during the execution of a single sql statement. To
support multiple authorizers, GenericLanguageConnectionContext will need to
keep a stack of these authorizers and a corresponding stack of default
schemas descriptors for those authorizers. To achieve this, I am thinking of
replacing "authorizer" field in GenericLanguageConnectionContext with Vector
stackOfAuthorizers and Vector stackOfDefaultSchemasForAuthorizers. The
initialize methd of GenericLanguageConnectionContext at the database
connection time will create an authorizer for the user who made the
connection to the database and this authorizer will be added as the first
element to stackOfAuthorizers. This authorizer will stay on
GenericLanguageConnectionContext's stack of authorizers until the user
disconnects from the database. In addition, the default schema of this
authorizer will be pushed on to the stack

In addition, two methods, addAuthorizerToTopOfStack and
removeAuthorizerFromTopOfStack will be added to
GenericLanguageConnectionContext and calls to these methods will be
generated during the generation phase of a sql depending on what that sql's
needs are for different authorizers. It will be the responsibility of the
sql generation phase to generate calls to removeAuthorizerFromTopOfStack
when it is finished with the authorizer that it added earlier using
addAuthorizerToTopOfStack. The code changes for this new definer/invoker
mode will also involve changing the signature of
GenericLanguageConnectionContext's initialize method, such that the required
authorizer name will be passed to this method and initialize method will
create a GenericAuthorizer object using that name and will put that
Authorizer on top of the authorizer stack. In addition, initialize method
will also create default schema descriptor for that authorizer name and will
put that on schema descriptor on top of the default schema descriptor stack.
The GenericLanguageConnectionContext's changed initialize method will look
something like this.
 public void initialize(String authorizerName, boolean sqlConnection) throws
  //Creating the authorizer authorizes the connection.
  Authorizer authorizer = new
GenericAuthorizer(getAuthorizationId(authorizerName),this, sqlConnection);

  //we can ignore the following if this is a database connection
  //associated with internal thread such as logSniffer and StageTrunc

  ** Set the authorization id.  User shouldn't
  ** be null or else we are going to blow up trying
  ** to create a schema for this user.
  if (SanityManager.DEBUG)
   if (getAuthorizationId() == null)
    SanityManager.THROWASSERT("User name is null," +
     " check the connection manager to make sure it is set" +
     " reasonably");

  SchemaDescriptor sd = initDefaultSchemaDescriptor();

A call to this initialize method should be generated during the generation
phase if an object requires a need to switch to the object definer's
authorization. The code generation will involve passing the name of the
object definer to GenericLanguageConnectionContext's initialize method and
authorizer for the definer will be put
on GenericLanguageConnectionContext's stack of authorizers. If an object
needs to run with the invoker's authorization, code should be generated to
peek at the first authorizer on the stack of authorizers (the first
authorizer will always correspond to invoker's authorizer) and that
authorizer will need to be added to the top of authorizer stack by
generating code addAuthorizerToTopOfStack. To peek at the first element of
stack to get invoker's authorizer, I am planning on adding method
getAuthorizerForConnectedUser to GenericLanguageConnectionContext.

Irrespective of which authorizer was added (definer/invoker), the sql code
generation phase should make sure that it generates the code to pop that
authorizer from the stack when it is finished with that authorizer.
Example of code generation for adding an invoker authorizer
 //We want to put the INVOKER as the authorizer on
LanguageConnectionContext's authorizer stack.
 //We can find the INVOKER authorizer by looking at first element of
 //authorizer stack which will always be the INVOKER of the sql statement.
Hence, we simply
 //generate the code to copy the first element of the
LanguageConnectionContext's authorizer stack
 //to the top of the stack.
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
  "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
  "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
  "getAuthorizerForConnectedUser", "
org.apache.derby.iapi.sql.conn.Authorizer", 0);
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
  "addAuthorizerToTopOfStack", "void", 1);

Example of code generation for adding a definer authorizer
 //We want to put the DEFINER authorizer by finding the owner name of the
object and generating
 //code to call GenericLanguageConnectionContext.initialize method on that
owner name.
 //GenericLanguageConnectionContext.initialize method will generate the
authorizer for the passed
 //name and will add that authorizer to top of stack of
GenericLanguageConnectionContext's authorizer stack.
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
  "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
 String objectOwnerName = getDataDictionary().getSchemaDescriptor(
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null, "initialize", "void", 2);

Example of code generation for popping the authorizer that was added earlier
and should not be used anymore
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
  "getLanguageConnectionContext", ClassName.LanguageConnectionContext, 0);
 mb.callMethod(VMOpcode.INVOKEINTERFACE, null,
  "removeAuthorizerFromTopOfStack", "void", 0);
In case the sql being executed runs into an exception, as part of the
cleanup work, we need to remove all the authorizers (except the first one on
the stack since it belongs to the "invoker" ie the user who has made this
connection to the database) and their corresponding default schema
descriptors from GenericLanugaugeConnectionContext's stack. The additional
code in GenericLanugaugeConnectionContext.cleanupOnError will be as follows
  ** In case of error, need to cleanup the intermediate authorizers
  ** and their corresponding default schema descriptors.
  ** Remove all the authorizers and default schema descriptors except
  ** for the first one on the stack because first authorizer corresponds
  ** to the user who has made this database connection
  for (int i = stackOfAuthorizers.size() - 1; i >= 1; i--) {

This logic is pretty simple and straight forward but can be confusing at the
same time. So, if anyonehas any questions, pass them on my way.


View raw message