Home Help Center

Glacier2/ identity

stephanstephan Member Stephan StapelOrganization: Stephan StapelProject: BristolNG
Hi there,

I am currently taking a deeper look into Glacier2, especially into the SessionManager functionality.

The implementation itself is quite straightforward, you've done that very well! What I'm missing is how I can find out the current user identity within arbitrary server calls. Also, it'd be interesting to find out the currently used identity on the client.

Is this possible?

cheers,

Stephan

Comments

  • marcmarc FloridaAdministrators, ZeroC Staff Marc LaukienOrganization: ZeroC, Inc.Project: The Internet Communications Engine ZeroC Staff
    This is typically done with a facade as discussed in this thread:

    http://www.zeroc.com/vbulletin/showthread.php?t=1370

    By making the facade accessible only to the authorized user (if you have one facade object per logged-in user, which also serves as session object), you can securely pass the user id to the back-end services as parameter.

    As an alternative method, we could add an option to Glacier2, so that it adds the user-id to the context. While this would work, I'm not sure if I would recommend to use this method. If a user-id is required by a server, then this fact should be explicitly stated in the contract (i.e., in the parameter list of an operation expressed in Slice), instead of hiding this in the context.
  • stephanstephan Member Stephan StapelOrganization: Stephan StapelProject: BristolNG
    Hi Marc!

    Thanks for the answer and the pointer to the second Glacier2 thread.

    However, I still do not get it :(

    My session manager looks like this:
    class SessionManagerI : Glacier2._SessionManagerDisp
    {
      public override Glacier2.SessionPrx create(string userId, Ice.Current current)
      {
        Glacier2.Session session = new MySession(userId);
        Ice.Identity id          = new Ice.Identity();
        id.category              = "_" + userId;
        id.name                  = Ice.Util.generateUUID();
        return Glacier2.SessionPrxHelper.uncheckedCast(current.adapter.add(session, id));
      }
    }
    

    which means that I follow the principle you mentioned: I use the session object both as session object and "identity facade object" and set the category to meet the user name, following section 39.5.2 in the manual (Glacier2.AddUserToAllowCategories=2).

    What I now don't yet get is how I can retrieve this identity on the server side.

    Should it appear in the standard current parameter as current.id.category ?

    regs,

    Stephan
  • marcmarc FloridaAdministrators, ZeroC Staff Marc LaukienOrganization: ZeroC, Inc.Project: The Internet Communications Engine ZeroC Staff
    You must define methods on your facade that the client calls, otherwise it's not a facade. These methods then in turn call on the back-end. If the user-id is required, then it can be passed as a regular parameter to the back-end. Alternatively, you can also pass the session proxy, or whatever information the back-end requires.
  • stephanstephan Member Stephan StapelOrganization: Stephan StapelProject: BristolNG
    I understand. This means that I have to make the client pass the session context (through the aforementioned proxy or an identifier) explicitly to the server.

    Wouldn't it be a nice addition to Glacier2 and Ice in general to show the caller's identity on the server without passing the session context explicitly?

    This would significantly ease the development with Glacier2 as one wouldn't have to store the session proxy on the client as a global object and pass it with every server call. Furthermore, a pure-server side function for identity retrieval could be extended in the future for further functionality, e.g. for implementing a groups and roles model on top of it.

    regs,

    Stephan
  • matthewmatthew NL, CanadaMember Matthew NewhookOrganization: ZeroC, Inc.Project: Internet Communications Engine ✭✭✭
    Hi Stephan,

    Sorry, that still isn't correct. The client calls on the facade, the facade calls on the server backend. The facade stores the client-side session information, and passes it where necessary to the server backend or alternatively provides a method to get the data for the server back-end on demand.

    The client does not need to pass its identity in every invocation.

    Regards, Matthew
  • marcmarc FloridaAdministrators, ZeroC Staff Marc LaukienOrganization: ZeroC, Inc.Project: The Internet Communications Engine ZeroC Staff
    As Matthew writes, the client would never pass its own identity after log-in. It would call on the per-client facade object, which knows the identity implicitly. The implementation of the facade's method would then pass on the client's identity to the server where required, and in whatever form requires (as string, as proxy to the session object, as proxy to a callback object in the client, etc.).
  • stephanstephan Member Stephan StapelOrganization: Stephan StapelProject: BristolNG
    Can you give a small example on this? I think that would solve my question quickly.

    If I have a server object publishing the sayHello(name) function, i.e. implementing in turn:

    string HelloWorldI::sayHello(string name, Ice.Current current) {}

    How would the facade and the client call look here?
  • matthewmatthew NL, CanadaMember Matthew NewhookOrganization: ZeroC, Inc.Project: Internet Communications Engine ✭✭✭
    Typically the facade approach is used to simplify access to the server backend. For example, consider an online game. In this case the facade presents a significantly simplified interface to all of the server internals. The facade takes each of the parameters to a method invocation, does some validation on them and makes invocations are necessary on the backend objects. As an example, take an invocation to tell the character to move to point B. The facade interface may look as:
    struct PickedPointInfo
    {
        // ....
    };
    interface CharacterFacade
    {
        //...
        ["amd"] bool move(PickedPointInfo info)
            throws PointNotReachableException;
    }
    

    The implementation for move might be something like:
    synchronized public void
    move_async(final AMD_CharacterFacade_move cb, final PickedPointInfo info, Ice.Current current)
    {
            if(_destroy) 
            { 
                Ice.ObjectNotExistException e = new Ice.ObjectNotExistException(); 
                e.id = current.id; 
                throw e; 
            }
    
            if(_characterDead)
            {
                cb.ice_exception(new PointNotReachableException());
                return;
            }
    
            AMI_Being_moveToPointForClientClick amiCB =
    	new AMI_Being_moveToPointForClientClick() {
    
    	    public void 
    	    ice_response(boolean truncate)
    	    {
    		cb.ice_response(truncate);
    	    }
    
    	    public void 
    	    ice_exception(Ice.LocalException ex)
    	    {
    		cb.ice_exception(ex);
    	    }
    
    	    public void 
    	    ice_exception(Ice.UserException ex)
    	    {
    		try
    		{
    		    throw ex;
    		}
                    // Some server backend exceptions.
    		catch(CharacterBusyException e)
    		{
    		    //
    		    // Tell him why he's busy and return.
    		    //
    		    _characterOneway.brainBoxMessage(e.description);
    		    cb.ice_response(false);
    		}
    	        catch(PointOutOfRangeException ee)
    		{
    		   PointNotReachableException pnre = new PointNotReachableException();
    		   pnre.reason = "Please choose a closer destination.";
    		   ice_exception(pnre);
    		}
    	    }
    	};
    
            _character.moveToPointForClientClick_async(amiCB, info);
    }
    

    The facade itself has state and behaviour (in this case it knows whether the character is dead, and does some exception translation). The facade in this case has a member _character, which is assigned when logging in. A character knows its own identity, and thus its not necessary to use a parameter on invocations.

    The facade also uses AMD/AMI chaining to ensure that threads are not held for an extended period of time by the client (see the article I wrote in connections for more information on this).

    Hope this helps.
  • stephanstephan Member Stephan StapelOrganization: Stephan StapelProject: BristolNG
    Thanks a lot for the example. I was aware of what the facade pattern is about and asked myself how you would want to use it here.

    Looking at your example, I understand that all server functions are implemented twice, one time in the facade, adding session information and timeout behaviour (move_async() in your example) and another time in the backend, implementing the functionality itself (called through _character.moveToPointForClientClick_async(amiCB, info); in the example).

    While this is ok for smaller server objects, I ask myself about the complexity that arises when having more interfaces on the server, each a number of functions.

    Is my guess correct that you suggest to implement a facade function for all server-side functions in the mentioned interfaces?

    Stephan
  • matthewmatthew NL, CanadaMember Matthew NewhookOrganization: ZeroC, Inc.Project: Internet Communications Engine ✭✭✭
    First some background. Consider again the game example. For the game the server backend is very complex, and the interfaces contain much much more stuff than a client need concern itself with. Restricting what the client itself can do is a very good thing:

    - It means that any mischief that a client can do (maliciously or due to a bug) is restricted to what the facade permits it to do. This also adds a layer of security, since the client is incapable of calling on non-facaded objects (due to Glacier2 access control).
    - It presents a simple and direct interface to the server backend, that is free of any abstractions that the backend may contain.
    - The backend server interfaces and implementation can change without affecting the client.

    If you only have a small project, most of this stuff isn't necessary. But for a project of any complexity all of these advantages are significant -- especially if you have different teams for the server & client. The facade presents a contract between the server & client development teams. The client team doesn't need to understand all of the complexity offered by the server backend.

    So to be specific:
    Looking at your example, I understand that all server functions are implemented twice, one time in the facade, adding session information and timeout behaviour (move_async() in your example) and another time in the backend, implementing the functionality itself (called through _character.moveToPointForClientClick_async(amiCB, info); in the example).

    It is not correct at all to say "all server functions". As I say above the facade probably presents only a subset of the full server functionality. As the complexity of the server backend rises the more value the facade itself can present both in protecting the server backend, and providing a more suitable abstraction for the "client". For the game in fact there were actually many different types of clients (and each had their own corresponding facade). There was the client that players used (ie: graphical front end), then there were administrative clients (user management), and GM clients (extra game administration).
  • marcmarc FloridaAdministrators, ZeroC Staff Marc LaukienOrganization: ZeroC, Inc.Project: The Internet Communications Engine ZeroC Staff
    Getting back to my first response in this thread: For toy examples, the facade approach is an overkill, and some option to have Glacier add the client's identity to the context would be more appropriate.

    While this would be easy to add, I'm reluctant to do so. Real-world Glacier applications are usually much more complex, and require a facade approach for the reasons Matthew explained. By adding such a feature, we would encourage to skip the facade approach, which will lead to many problems as the project grows.
Sign In or Register to comment.