static CapabilityMap prepareAuthCaps(const CapabilityMap& data)
{
    CapabilityMap result;

    for (CapabilityMap::const_iterator it = data.begin(), end = data.end(); it != end; ++it)
    {
        if (boost::algorithm::starts_with(it->first, QiAuthPrefix))
            result[it->first] = it->second;
        else
            result[AuthProvider::UserAuthPrefix + it->first] = it->second;
    }
    return result;
}
static CapabilityMap extractAuthData(const CapabilityMap& cmap)
{
    CapabilityMap authData;
    // Extract all capabilities related to authentication
    for (CapabilityMap::const_iterator it = cmap.begin(), end = cmap.end(); it != end; ++it)
    {
        const std::string& key = it->first;
        if (boost::algorithm::starts_with(key, QiAuthPrefix))
            authData[key] = it->second;
        else if (boost::algorithm::starts_with(key, AuthProvider::UserAuthPrefix))
            authData[key.substr(AuthProvider::UserAuthPrefix.length(), std::string::npos)] = it->second;
    }
    return authData;
}
  void Session_Service::onTransportSocketResult(qi::Future<TransportSocketPtr> value, long requestId) {
    qiLogDebug() << "Got transport socket for service";
    {
      boost::recursive_mutex::scoped_lock sl(_requestsMutex);
      ServiceRequest *sr = serviceRequest(requestId);
      if (!sr)
        return;

      if (value.hasError()) {
        sr->promise.setError(value.error());
        removeRequest(requestId);
        return;
      }
    }
    TransportSocketPtr socket = value.value();

    // If true, this socket came from the socket cache and has already been identified.
    // This typically happens when two services are behind the same endpoint.
    // We forge a message that just shows we've authenticated successfully.
    if (socket->hasReceivedRemoteCapabilities())
    {
      Message dummy;
      CapabilityMap cm;
      cm[AuthProvider::State_Key] = AnyValue::from(AuthProvider::State_Done);
      dummy.setType(Message::Type_Reply);
      dummy.setFunction(qi::Message::ServerFunction_Authenticate);
      dummy.setValue(AnyValue::from(cm), typeOf<CapabilityMap>()->signature());
      onAuthentication(TransportSocket::SocketEventData(dummy), requestId, socket, ClientAuthenticatorPtr(new NullClientAuthenticator), SignalSubscriberPtr());
      return;
    }
    ClientAuthenticatorPtr authenticator = _authFactory->newAuthenticator();
    CapabilityMap authCaps;
    {
      CapabilityMap tmp = authenticator->initialAuthData();
      for (CapabilityMap::iterator it = tmp.begin(), end = tmp.end(); it != end; ++it)
        authCaps[AuthProvider::UserAuthPrefix + it->first] = it->second;
    }
    SignalSubscriberPtr protSubscriber(new SignalSubscriber);
    *protSubscriber = socket->socketEvent.connect(&Session_Service::onAuthentication, this, _1, requestId, socket, authenticator, protSubscriber);


    Message msgCapabilities;
    msgCapabilities.setFunction(Message::ServerFunction_Authenticate);
    msgCapabilities.setService(Message::Service_Server);
    msgCapabilities.setType(Message::Type_Call);

    TransportSocketPtr sdSocket = _sdClient->socket();
    CapabilityMap socketCaps;
    if (sdSocket)
    {
      socketCaps = sdSocket->localCapabilities();
      socket->advertiseCapabilities(socketCaps);
    }
    socketCaps.insert(authCaps.begin(), authCaps.end());
    msgCapabilities.setValue(socketCaps, typeOf<CapabilityMap>()->signature());
    socket->send(msgCapabilities);
  }
  void Session_Service::onAuthentication(const TransportSocket::SocketEventData& data, long requestId, TransportSocketPtr socket, ClientAuthenticatorPtr auth, SignalSubscriberPtr old)
  {
    static const std::string cmsig = typeOf<CapabilityMap>()->signature().toString();
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    ServiceRequest *sr = serviceRequest(requestId);
    if (!sr)
      return;
    if (data.which() == TransportSocket::Event_Error)
    {
      if (old)
        socket->socketEvent.disconnect(*old);
      sr->promise.setError(boost::get<std::string>(data));
      removeRequest(requestId);
      return;
    }

#if BOOST_VERSION < 105800
    const Message& msg = boost::get<const Message&>(data);
#else
    const Message& msg = boost::relaxed_get<const Message&>(data);
#endif
    int function = msg.function();
    bool failure = msg.type() == Message::Type_Error
        || msg.service() != Message::Service_Server
        || function != Message::ServerFunction_Authenticate;

    if (failure)
    {
      if (old)
        socket->socketEvent.disconnect(*old);
      if (_enforceAuth)
      {
        std::stringstream error;
        if (msg.type() == Message::Type_Error)
          error << "Error while authenticating: " << msg.value("s", socket).to<std::string>();
        else
          error << "Expected a message for function #" << Message::ServerFunction_Authenticate << " (authentication), received a message for function " << function;
        sr->promise.setError(error.str());
        removeRequest(requestId);
      }
      else
      {
        session_service_private::sendCapabilities(socket);
        qi::Future<void> metaObjFut;
        sr->remoteObject = new qi::RemoteObject(sr->serviceId, socket);
        metaObjFut = sr->remoteObject->fetchMetaObject();
        metaObjFut.connect(&Session_Service::onRemoteObjectComplete, this, _1, requestId);
      }
      return;
    }

    AnyReference cmref = msg.value(typeOf<CapabilityMap>()->signature(), socket);
    CapabilityMap authData = cmref.to<CapabilityMap>();
    CapabilityMap::iterator authStateIt = authData.find(AuthProvider::State_Key);
    cmref.destroy();

    if (authStateIt == authData.end() || authStateIt->second.to<unsigned int>() < AuthProvider::State_Error
        || authStateIt->second.to<unsigned int>() > AuthProvider::State_Done)
    {
      if (old)
        socket->socketEvent.disconnect(*old);
      std::string error = "Invalid authentication state token.";
      sr->promise.setError(error);
      removeRequest(requestId);
      qiLogInfo() << error;
      return;
    }
    if (authData[AuthProvider::State_Key].to<unsigned int>() == AuthProvider::State_Done)
    {
      qi::Future<void> metaObjFut;
      if (old)
        socket->socketEvent.disconnect(*old);
      sr->remoteObject = new qi::RemoteObject(sr->serviceId, socket);
      //ask the remoteObject to fetch the metaObject
      metaObjFut = sr->remoteObject->fetchMetaObject();
      metaObjFut.connect(&Session_Service::onRemoteObjectComplete, this, _1, requestId);
      return;
    }

    CapabilityMap nextData = auth->processAuth(authData);
    Message authMsg;
    authMsg.setService(Message::Service_Server);
    authMsg.setType(Message::Type_Call);
    authMsg.setValue(nextData, cmsig);
    authMsg.setFunction(Message::ServerFunction_Authenticate);
    socket->send(authMsg);
  }
Example #5
0
  void Server::onMessageReadyNotAuthenticated(const Message &msg, TransportSocketPtr socket, AuthProviderPtr auth,
                                              boost::shared_ptr<bool> first, SignalSubscriberPtr oldSignal)
  {
    qiLogVerbose() << "Starting auth message" << msg.address();
    int id = msg.id();
    int service = msg.service();
    int function = msg.action();
    int type = msg.type();
    Message reply;

    reply.setId(id);
    reply.setService(service);
    if (service != Message::Service_Server
        || type != Message::Type_Call
        || function != Message::ServerFunction_Authenticate)
    {
      socket->messageReady.disconnect(*oldSignal);
      if (_enforceAuth)
      {
        std::stringstream err;

        err << "Expected authentication (service #" << Message::Service_Server <<
               ", type #" << Message::Type_Call <<
               ", action #" << Message::ServerFunction_Authenticate <<
               "), received service #" << service << ", type #" << type << ", action #" << function;
        reply.setType(Message::Type_Error);
        reply.setError(err.str());
        socket->send(reply);
        socket->disconnect();
        qiLogVerbose() << err.str();
      }
      else
      {
        server_private::sendCapabilities(socket);
        qiLogVerbose() << "Authentication is not enforced. Skipping...";

        connectMessageReady(socket);
        onMessageReady(msg, socket);
      }
      return;
    }
    // the socket now contains the remote capabilities in socket->remoteCapabilities()
    qiLogVerbose() << "Authenticating client " << socket->remoteEndpoint().str() << "...";

    AnyReference cmref = msg.value(typeOf<CapabilityMap>()->signature(), socket);
    CapabilityMap authData = cmref.to<CapabilityMap>();
    cmref.destroy();
    CapabilityMap authResult = auth->processAuth(authData);
    unsigned int state = authResult[AuthProvider::State_Key].to<unsigned int>();
    std::string cmsig = typeOf<CapabilityMap>()->signature().toString();
    reply.setFunction(function);
    switch (state)
    {
    case AuthProvider::State_Done:
      qiLogVerbose() << "Client " << socket->remoteEndpoint().str() << " successfully authenticated.";
      socket->messageReady.disconnect(*oldSignal);
      connectMessageReady(socket);
      // no break, we know that authentication is done, send the response to the remote end
    case AuthProvider::State_Cont:
      if (*first)
      {
        authResult.insert(socket->localCapabilities().begin(), socket->localCapabilities().end());
        *first = false;
      }
      reply.setValue(authResult, cmsig);
      reply.setType(Message::Type_Reply);
      socket->send(reply);
      break;
    case AuthProvider::State_Error:
    default:{
      std::stringstream builder;
      builder << "Authentication failed";
      if (authResult.find(AuthProvider::Error_Reason_Key) != authResult.end())
      {
        builder << ": " << authResult[AuthProvider::Error_Reason_Key].to<std::string>();
        builder << " [" << _authProviderFactory->authVersionMajor() << "." << _authProviderFactory->authVersionMinor() << "]";
      }
      reply.setType(Message::Type_Error);
      reply.setError(builder.str());
      qiLogVerbose() << builder.str();
      socket->send(reply);
      socket->disconnect();
      }
    }
    qiLogVerbose() << "Auth ends";
  }