Home Help Center

Different kinds of proxies

mwilsonmwilson Member Mark WilsonOrganization: Integrated Computer Solutions, Inc.Project: TBD ✭✭✭
I am developing several services for a large control system. I want to make sure that only one client has full privileges at a time, based on a client ID. I also want to allow non-privileged clients to view details about specific services without being able to effect any changes.

My first thought was to have a service return different types of proxies, depending on user privilege, with an interface something like this:

interface ServiceX
{
ServiceXProxy* getProxy(string clientID, string password);
}

Depending on client ID, the client would get back a read/write proxy or a read-only proxy. If another client already had the read/write proxy, the requesting client would get a null proxy, a read-only proxy, or an exception, depending on implementation. However, as far as I can tell Ice would not allow this, as it generates the proxies for you.

Does anyone else have an idea how to do this?

Thanks,
Mark

Comments

  • mesmes CaliforniaAdministrators, ZeroC Staff Mark SpruiellOrganization: ZeroC, Inc.Project: Ice Developer ZeroC Staff
    Hi,

    There are lots of ways to approach this. I'll toss out a few:
    • Define an interface containing the read-only operations, and provide the read-write operations in a derived interface. The getProxy operation returns a proxy to the read-only interface, and the client can attempt to downcast that proxy to the read-write interface. Naturally, this solution only works if the downcast to the read-write interface fails to succeed if the client doesn't have permission to use it.
    • Define all of the operations in a single interface, and raise an exception if a client invokes a read-write operation without permission.
    • Define unrelated interfaces containing read-only and read-write operations. There are a few issues with this solution. First of all, the getProxy operation gets a little more complicated. It must either return a generic Object* that must be downcast for either type (not very user-friendly), or you need two getProxy operations, or perhaps a single getProxy operation attempts to return the read-write proxy, but if that's in use then a read-only proxy is returned instead (via an exception or out parameter).

      This solution also might complicate the server implementation, since a single servant cannot implement multiple interfaces. You would either need to implement the operations in different classes, or define a private Slice interface that inherits from both public ones.

      All in all, I don't think this would be my first choice.
    I would think there are other considerations as well, such as security (do you have to worry about malicious clients attempting to gain read-write access?) and locking (does the client with the read-write proxy have to release it?).

    Hope that helps,
    - Mark
  • OrNotOrNot Member Bin.LiOrganization: GE HealthcareProject: Enterprise solution
    hi,mwilson,
    As far as I know, there have been some threads in this forums once discussing the topic similar to yours. You can check them and may get some clues.

    http://www.zeroc.com/vbulletin/showthread.php?t=1436&highlight=facade

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

    http://www.zeroc.com/vbulletin/showthread.php?t=521&highlight=object+access

    Hope this helps.

    Best Regards
    OrNot
  • mwilsonmwilson Member Mark WilsonOrganization: Integrated Computer Solutions, Inc.Project: TBD ✭✭✭
    Hello,

    Just back from vacation. Thanks for the replies.

    I've thought of another way to accomplish this (keeping in mind that this system works in a completely closed environment - no need for encryption, and simple client ID/password system is sufficient).

    Define an interface:

    interface AccessControl {
    bool requestRWAccess(string clientID, string password);
    };

    Implement that interface as part of your service:

    interface ServiceX implements AccessControl {
    void mutator1(int param);
    ....
    };

    On the client side, we have a wrapper/facade that takes the client ID/password in it's constructor, has the same interface as the ServiceX proxy, and forwards calls to the real ServiceX proxy, which it contains as a private member variable.

    On construction, the wrapper calls requestRWAccess. When the client calls mutator1 on the facade, the facade throws an exception if the client did not receive full permission.

    When the wrapper is destroyed, it releases the service for someone else to use.

    I think the basic idea is solid - I can think of several additions to this, such as pre-emption by a higher priority client, notifying a client when the service is available, etc.
  • matthewmatthew NL, CanadaMember Matthew NewhookOrganization: ZeroC, Inc.Project: Internet Communications Engine ✭✭✭
    It may well be simpler if you actually have two different implementations of your servant -- you can then avoid alot of permissions checks in each of the implementation since the client cannot get a proxy to the object unless it has passed the security test.

    interface AccessControl {
    ServiceX* login((string clientID, string password);
    };

    Implement that interface as part of your service:

    interface ServiceX
    {
    void mutator1(int param);
    ....
    };

    Regards, Matthew
  • mwilsonmwilson Member Mark WilsonOrganization: Integrated Computer Solutions, Inc.Project: TBD ✭✭✭
    It's been a few weeks since we've worked on this, now we are revisiting it.

    I guess making two different servants (we have "expert" and "operator" user permission types) and returning a proxy to the correct one would work. There would be some extra work keeping them in sync if interfaces change, but probably not too bad if we come up with the right abstractions.

    One issue is that many (most) of our services interact with hardware devices such as actuators, cameras, and measurement instructments such as oscilloscopes. In this case I think the "expert" and "operator" servants would have to serve as handles to the "real" servant that manages the hardware interactions. The handles would forward the calls to the "real" servant (PIMPL idiom?). If the handle methods are inlined, there should be very little overhead (I guess the overhead of an extra function call is pretty small compared to the network latency and object adapter overhead anyway, right?).

    Now, would we have only two handle servants, or would we create a new servant of the appropriate type for each user? We need to be able to lock services and identify who has a lock. We don't want an "expert" changing things while an "operator" is using the system, for instance, though we would want the expert to be able to observe everything that the operator and system are doing. So we're thinking about:

    interface Lockable
    {
    bool lock();
    void unlock();
    string lockedBy(); // returns a user id
    bool isLocked();
    }

    on the servants. The handle servants would implement Lockable. The real servant would own the lock, and locking requests would be forwarded from the handle servants to the real servant. It seems like this would be easier if there were a handle servant per client.
Sign In or Register to comment.