Object capabilities and disposal when trying to log out users from a server

104 views
Skip to first unread message

German Diago

unread,
Mar 22, 2020, 8:02:23 AM3/22/20
to Cap'n Proto
Hello everyone,

I am user germandiagogomez from Github. I opened before a couple of isues, so that you can identify who I am :)


It is my first post in the mailing lists. After some research, I could not find definitive answers to my questios anywhere.
I saw some related post, but that was at the time of Capnproto 0.5 version: https://groups.google.com/forum/#!msg/capnproto/-VtGIsRP-ho/zVtiz0yHLQAJ

Some context

I am using capnproto for a service where people connect and create rooms and join them to play matches in a small game. So far so good :) Now, I am in the need to implement a command that logs outs all users
from the admin interface for the server.

My implementation is something like this (simplified). I have:

a. game cilent
b. an admin commands console to talk to the server for admin purposes, independent of the game client

These are the capnproto interfaces I have implemented in C++ code:

1. class OnlineRoomsAccess with a method authenticate and another method authenticateAsAdmin that are used to log in into the server
   to interact with it. These 2 calls will return another object of their respective type to use some capabilities.

2. class OnlineRoomsService that is returned after calling onlineRoomsAccessObj.authenticate(params) in my game client. It can be used to create/join rooms, etc.

3. class OnlineRoomsAdmin that is returned after calling onlineRoomsAccessObj.authenticateAsAdmin(params). I can issue a call  to logoutAllUsers
    with this interface. I created a cli to send this command.
   

My game client C++ code, looks like this at some point, when I issue an authenticate request:

    // Authenticate + use flow

    auto authRequest = accessObj.authenticateRequest();
    authRequest.setUserName("myUser");
    ...
    auto authResponse = authRequest.send().wait(...);
    OnlineRoomsService::Client onlineRoomsService = authResponse.getService();
    //Now create/join rooms, etc.
   onlineRoomsService.createRoom(....);


My server has something like this in OnlineRoomsAccesss::authenticate to give the object capability to the user that requested it:

  kj::Promise<void> authenticate(AuthenticateContext context) override {
       ...
       context.getResults().setService(kj::heap<OnlineRoomsService>());
       ...
    }

As you can see, I use setService in the implementation of OnlineRoomsAccess::authenticate and I do not hold myself a reference to OnlineRoomsService object myself anymore after
creating it via kj::heap. I just send it through the response to the request. As far as I understand, kj::heap<T> is the equivalent of std::unique_ptr<T>, correct me if I am wrong.


Question


1. when my admin tool issues a logoutAllUsers() after I authenticate as admin, and a client is connected, how can I revoke the capability to its user?
   Because I do not see an obvious way to do it on the server side. I do not have an object or anything to destroy/dispose after context.getResults().setService(kj::heap<OnlineRoomsService>())
   has been called... and I do not want to stop the server either.
   I saw the proxy trick (which I did not understand fully yet) with dispatchCall in the server for  0.5. Is this still the better way to achieve this functionality?

  
Thanks for your time   

Kenton Varda

unread,
Mar 23, 2020, 10:59:40 AM3/23/20
to German Diago, Cap'n Proto
Hi German,

This sounds like a job for membranes!


A "membrane" is a wrapper which also automatically wraps any further objects that pass through it. So if you have a membraned object, and you call a method on it, and it returns a new capability, that capability will be wrapped as well.

One thing in particular that membranes are useful for is revocation. Create a MembranePolicy whose onRevoked() method returns a promise that resolves when logoutAllUsers() in invoked.

Details: Use kj::newPromiseAndFulfiller<void>() to create a promise/fulfiller pair. Call .fork() on the promise so that you can return a new copy every time onRevoked() is called. When logoutAllUsers() is called, call fulfiller->reject(KJ_EXCEPTION(DISCONNECTED, "logoutAllUsers() was called")). Then create a new pair for the next time it's called. Whenever someone authenticates, before returning OnlineRoomsService to them, wrap it in the membrane using capnp::membrane(capability, policy->addRef()).

-Kenton

--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/capnproto/312b2324-7a4c-47eb-99cd-a4fad12adb03%40googlegroups.com.

german.g...@gmail.com

unread,
Apr 16, 2020, 10:32:16 AM4/16/20
to Cap'n Proto

On Monday, March 23, 2020 at 3:59:40 PM UTC+1, Kenton Varda wrote:
Hi German,

This sounds like a job for membranes!


A "membrane" is a wrapper which also automatically wraps any further objects that pass through it. So if you have a membraned object, and you call a method on it, and it returns a new capability, that capability will be wrapped as well.

One thing in particular that membranes are useful for is revocation. Create a MembranePolicy whose onRevoked() method returns a promise that resolves when logoutAllUsers() in invoked.

Hello Kenton,

Sorry for the late reply. Not much time available lately.
 I think I understand the basics but the untyped interface seems a bit difficult to manage for me and I am not clear when I need to do/undo the membrane on top of the object. Any real test code or similar written around to handle it? In particular what seems confusing to me is the wrapping/unwrapping of a membrane as something is forwarded around.


Details: Use kj::newPromiseAndFulfiller<void>() to create a promise/fulfiller pair. Call .fork() on the promise so that you can return a new copy every time onRevoked() is called. When logoutAllUsers() is called, call fulfiller->reject(KJ_EXCEPTION(DISCONNECTED, "logoutAllUsers() was called")). Then create a new pair for the next time it's called. Whenever someone authenticates, before returning OnlineRoomsService to them, wrap it in the membrane using capnp::membrane(capability, policy->addRef()).

 Thanks, I will take a look.


-Kenton

To unsubscribe from this group and stop receiving emails from it, send an email to capn...@googlegroups.com.

Kenton Varda

unread,
Apr 20, 2020, 12:52:42 PM4/20/20
to german.g...@gmail.com, Cap'n Proto
Hi German,

You would only need to add the membrane once, when the user authenticates -- add the membrane to the object returned by the authentication method. For your use case, you don't ever have to manually remove the membrane.

-Kenton

To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/capnproto/8423e683-df61-4a08-a520-08132d3ec27d%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages