## Server-side Access Controls

### Servant binding access controls

RCF allows you to apply access controls to individual servant bindings on your server. The access control is implemented as a user-defined callback function, in which you can apply application-specific logic to determine whether a client connection should be allowed to access a particular servant binding.

For example:

    bool onHelloWorldAccess(int dispatchId)
    {
        // Return true to allow access, and false to deny access.
        // ...

        return true;
    }

    HelloWorldImpl helloWorldImpl;
    RCF::RcfServer server( RCF::TcpEndpoint(50001) );

    RCF::ServerBindingPtr bindingPtr = server.bind<I_HelloWorld>(helloWorldImpl);
    bindingPtr->setAccessControl( boost::bind(&onHelloWorldAccess, _1) );

    server.start();

The access control callback will be invoked by the `RcfServer` each time a client tries to call a method on that servant. From the access control callback you can inspect the current session and determine whether it should be granted access to the servant. Once authentication is granted, you will probably want to store the authentication state in a session object, so it can be easily reused on subsequent calls:

    // App-specific authentication state.
    class HelloWorldAuthenticationState
    {
    public:
        HelloWorldAuthenticationState() : mAuthenticated(false)
        {
        }

        bool            mAuthenticated;
        std::string     mClientUsername;
    };

    // Servant binding access control.
    bool onHelloWorldAccess(int dispatchId)
    {
        RCF::RcfSession & session = RCF::getCurrentRcfSession();
        HelloWorldAuthenticationState & authState = session.getSessionObject<HelloWorldAuthenticationState>(true);
        if (!authState.mAuthenticated)
        {
            // Here we are checking that the client is using either NTLM or Kerberos.
            RCF::TransportProtocol tp =  session.getTransportProtocol();
            if (tp == RCF::Tp_Ntlm || tp == RCF::Tp_Kerberos)
            {
                authState.mAuthenticated = true;
                authState.mClientUsername = session.getClientUsername();
            }
        }
        return authState.mAuthenticated;
    }

As the previous example indicates, typically you would use the access control callback to inspect the transport protocol the client is using, and use that to determine the identity of the client.

In some situations you may want the client to provide extra authentication information, beyond what is available though the transport protocol. This typically means having the equivalent of a `Login()` method on the interface, that needs to be called before any other method on the interface. The access control callback can be used to verify that `Login()` is called before any other method:

    // App-specific login info.
    class LoginInfo
    {
    public:
        // ...

        void serialize(SF::Archive & ar)
        {}
    };

    RCF_BEGIN(I_HelloWorld, "I_HelloWorld")
        RCF_METHOD_V1(void, Login, const LoginInfo &)
        RCF_METHOD_V1(void, Print, const std::string &)
    RCF_END(I_HelloWorld)

    // App-specific authentication state.
    class HelloWorldAuthenticationState
    {
    public:
        HelloWorldAuthenticationState() : mAuthenticated(false)
        {
        }
        bool            mAuthenticated;
        std::string     mClientUsername;
        LoginInfo       mLoginInfo;
    };

    // Servant object.
    class HelloWorldImpl
    {
    public:

        void Login(const LoginInfo & loginInfo)
        {
            RCF::RcfSession & session = RCF::getCurrentRcfSession();
            HelloWorldAuthenticationState & authState = session.getSessionObject<HelloWorldAuthenticationState>(true);
            if (!authState.mAuthenticated)
            {
                RCF::TransportProtocol tp =  session.getTransportProtocol();
                if (tp == RCF::Tp_Ntlm || tp == RCF::Tp_Kerberos)
                {
                    authState.mAuthenticated = true;
                    authState.mClientUsername = session.getClientUsername();
                    authState.mLoginInfo = loginInfo;
                }
            }
        }

        void Print(const std::string & s)
        {
            std::cout << "I_HelloWorld service: " << s << std::endl;
        }
    };

    // Servant binding access control.
    bool onHelloWorldAccess(int dispatchId)
    {
        if (dispatchId == 0)
        {
            // Calls to Login() are always allowed through.
            return true;
        }
        else
        {
            RCF::RcfSession & session = RCF::getCurrentRcfSession();
            HelloWorldAuthenticationState & authState = session.getSessionObject<HelloWorldAuthenticationState>(true);
            return authState.mAuthenticated;
        }
    }

In this case, the access control callback allows calls to `Login()` to go straight through, while for any other method on the `I_HelloWorld` interface, it checks for the existence of the authentication state that the `Login()` call creates.

The `Login()` method is identified in the access control callback by its dispatch ID - in this case 0, as it is the first method on the interface. Dispatch ID's are assigned in incremental order, from 0, so if for example `Login()` had been the third method on the interface, it would have had a dispatch ID of 2.