#pragma once #include "rublon/utils.hpp" #include #include #include #include #include #include #include namespace rublon { template < class MethodSelect_t = MethodSelect > class Init : public AuthenticationStep { using base_t = AuthenticationStep; const char * apiPath = "/api/transaction/init"; tl::expected< MethodSelect_t, Error > createMethod(const Document & coreResponse) const { memory::MonotonicStackResource< 512 > stackResource; RapidJSONPMRAlloc alloc{&stackResource}; const auto * rublonMethods = JSONPointer{"/result/methods", &alloc}.Get(coreResponse); const auto * rublonTid = JSONPointer{"/result/tid", &alloc}.Get(coreResponse); if(not rublonMethods) log(LogLevel::Error, "core response has no methods"); if(not rublonTid) log(LogLevel::Error, "core response has no transaction ID"); _session.updateTransactionId(rublonTid); return MethodSelect_t{_session, *rublonMethods, _session.config().prompt, _session.config().autopushPrompt}; } void addPamInfo(Document & coreRequest, const Pam_t & pam) const { auto & alloc = coreRequest.GetAllocator(); coreRequest.AddMember("username", Value{pam.username().get(), alloc}, alloc); } void addParams(Document & coreRequest, const Pam_t & pam) const { using namespace memory::literals; using namespace std::string_view_literals; memory::MonotonicStackResource< 2_kB > memoryResource; auto & alloc = coreRequest.GetAllocator(); const auto os = details::osName(&memoryResource); const auto host = details::hostname(&memoryResource); if(os == "unknown") { log(LogLevel::Warning, "No OS information available"); } Value params{rapidjson::kObjectType}; params.AddMember("appVer", RUBLON_VERSION_STRING, alloc); if(not host.empty()) params.AddMember("hostName", Value{os.c_str(), static_cast< unsigned >(host.size()), alloc}, alloc); if(_session.inNonInteractiveMode()) params.AddMember("mode", "noninteractive", alloc); params.AddMember("os", Value{os.c_str(), static_cast< unsigned >(os.size()), alloc}, alloc); auto ip = pam.ip(); // ip has always a value, can be empty but the value should be set if(ip.get() != ""sv){ params.AddMember("userIP", Value{pam.ip().get(), alloc}, alloc); } else { getDefaultRouteIp(&memoryResource) // .and_then([&](const auto & ip) { params.AddMember("userIP", Value{ip.c_str(), alloc}, alloc); return tl::expected< void, Error >{}; }) .or_else([](auto) { log(LogLevel::Warning, "There is no UserIP that can be used"); }); } coreRequest.AddMember("params", std::move(params), alloc); } tl::expected< std::reference_wrapper< const Document >, Error > checkEnrolement(const Document & coreResponse, const Pam_t pam) const { using namespace std::string_view_literals; using namespace memory::literals; memory::MonotonicStackResource< 1_kB > stackResource; RapidJSONPMRAlloc alloc{&stackResource}; const auto * rublonStatus = JSONPointer{"/result/status", &alloc}.Get(coreResponse); const auto * rublonWebURI = JSONPointer{"/result/webURI", &alloc}.Get(coreResponse); if(rublonStatus) { const auto & status = rublonStatus->GetString(); if(status == "pending"sv) { if(rublonWebURI) { pam.print("Visit %s", rublonWebURI->GetString()); } } else if(status == "waiting"sv) { log(LogLevel::Warning, "Got enrolement message with stats %s", status); return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserWaiting}}}; } else if(status == "denied"sv) { log(LogLevel::Warning, "Got enrolement message with stats %s", status); return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserDenied}}}; } } return coreResponse; } public: const char * _name = "Initialization"; Init(Session & session) : base_t(session) { log(LogLevel::Debug, "Starting inicialization"); } template < typename Hander_t > tl::expected< MethodSelect_t, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler, const Pam_t & pam) const { const auto createMethod = [&](const auto & coreResponse) { return this->createMethod(coreResponse); }; const auto checkEnrolement = [&](const auto & coreResponse) { return this->checkEnrolement(coreResponse, pam); }; RapidJSONPMRStackAlloc< 2048 > alloc{}; Document body{rapidjson::kObjectType, &alloc}; this->addSystemToken(body); this->addPamInfo(body, pam); this->addParams(body, pam); return coreHandler .request(alloc, apiPath, body) // .and_then(checkEnrolement) .and_then(createMethod); } }; } // namespace rublon