#pragma once #include #include #include #include #include #include #include namespace rublon { template < typename Pam_t = LinuxPam > class Method : AuthenticationStep< Method< Pam_t > > { std::string _systemToken; std::string _tid; public: template < typename Method_t > Method(Method_t && metho, std::string systemToken, std::string tid) : _impl{metho}, _systemToken{systemToken}, _tid{tid} {} template < typename Handler_t > tl::expected< Confirmation, PamAction > fire(const CoreHandlerInterface< Handler_t > & coreHandler) const { log(Debug, "method select"); char _buffer[1024]; std::pmr::monotonic_buffer_resource mr{_buffer, 1024}; RapidJSONPMRAlloc alloc{&mr}; Document body{rapidjson::kObjectType, &alloc}; body.AddMember("systemToken", Value{_systemToken.c_str(), alloc}, alloc); body.AddMember("tid", Value{_tid.c_str(), alloc}, alloc); body.AddMember("method", Value{"totp", alloc}, alloc); body.AddMember("GDPRAccepted", Value{"true", alloc}, alloc); body.AddMember("tosAccepted", Value{"true", alloc}, alloc); auto coreResponse = coreHandler.request("/api/transaction/methodSSH", body); if(coreResponse.has_value()) { log(LogLevel::Info, "[TMP] has response, processing", __PRETTY_FUNCTION__); const auto & rublonResponse = coreResponse.value()["result"]; std::string tid = rublonResponse["tid"].GetString(); return tl::unexpected{PamAction::accept}; } else { // mostly connectio errors switch(coreResponse.error().errorClass) { case CoreHandlerError::ErrorClass::BadSigature: log(LogLevel::Error, "ErrorClass::BadSigature"); return tl::unexpected{PamAction::decline}; case CoreHandlerError::ErrorClass::CoreException: /// TODO exception handling log(LogLevel::Error, "ErrorClass::CoreException"); return tl::unexpected{PamAction::decline}; /// TODO accept? case CoreHandlerError::ErrorClass::ConnectionError: log(LogLevel::Error, "ErrorClass::ConnectionError"); return tl::unexpected{PamAction::decline}; /// TODO decline? case CoreHandlerError::ErrorClass::BrokenData: log(LogLevel::Error, "ErrorClass::BrokenData"); return tl::unexpected{PamAction::decline}; } } return std::visit( [&](const auto & method) { rublon::log(LogLevel::Info, "Using '%s' method", method.name); return method.fire(coreHandler); }, _impl); } private: std::variant< method::OTP< Pam_t > > _impl; }; template < typename Pam_t = LinuxPam > class PostMethod : public rublon::AuthenticationStep< PostMethod< Pam_t > > { using base_t = rublon::AuthenticationStep< PostMethod< Pam_t > >; const char * uri = "/api/transaction/methodSSH"; std::string _method; public: const char * name = "Confirm Method"; PostMethod(std::string systemToken, std::string tid, std::string method) : base_t(std::move(systemToken), std::move(tid)), _method{method} {} template < typename Hander_t > tl::expected< Method<>, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const { char _buffer[1024]; std::pmr::monotonic_buffer_resource mr{_buffer, 1024}; RapidJSONPMRAlloc alloc{&mr}; Document body{rapidjson::kObjectType, &alloc}; body.AddMember("systemToken", Value{this->_systemToken.c_str(), alloc}, alloc); body.AddMember("tid", Value{this->_tid.c_str(), alloc}, alloc); body.AddMember("method", Value{_method.c_str(), alloc}, alloc); body.AddMember("GDPRAccepted", Value{"true", alloc}, alloc); body.AddMember("tosAccepted", Value{"true", alloc}, alloc); auto coreResponse = coreHandler.request(uri, body); if(coreResponse.has_value()) { log(LogLevel::Info, "[TMP] has response, processing", __PRETTY_FUNCTION__); const auto & rublonResponse = coreResponse.value()["result"]; std::string tid = rublonResponse["tid"].GetString(); return tl::unexpected{PamAction::accept}; } else { return tl::unexpected{this->coreErrorHandler(coreResponse)}; } return tl::unexpected{PamAction::accept}; } }; class MethodSelect { std::string systemToken; std::string _tid; std::vector< std::string > _methods; public: template < typename Array_t > MethodSelect(std::string systemToken, std::string tid, const Array_t & methods) : systemToken{systemToken}, _tid{std::string{tid}} { for(auto it = methods.Begin(); it < methods.End(); it++) { _methods.emplace_back(it->GetString()); } } template < typename Pam_t > tl::expected< PostMethod< Pam_t >, PamAction > create(const Pam_t & pam, const std::string & tid) const { std::pmr::map< int, std::string > methods_id; pam.print("%s", ""); int i{}; for(const auto & method : _methods) { rublon::log(LogLevel::Debug, "method %s found at pos %d", method.c_str(), i); if(method == "email") { pam.print("%d: Email Link", i + 1); methods_id[++i] = "email"; } if(method == "qrcode") { pam.print("%d: QR Code", i + 1); methods_id[++i] = "qrcode"; } if(method == "totp") { pam.print("%d: Mobile TOTP", i + 1); methods_id[++i] = "totp"; } if(method == "push") { pam.print("%d: Mobile Push", i + 1); methods_id[++i] = "push"; } if(method == "sms") { pam.print("%d: SMS code", i + 1); methods_id[++i] = "sms"; } } auto methodid = pam.scan( [](char * userinput) { rublon::log(LogLevel::Debug, "User input: %s", userinput); return std::stoi(userinput); }, "\nSelect method [1-%d]: ", _methods.size()); pam.print( "you selected: %s", methods_id.count(methodid.value_or(0)) ? methods_id.at(methodid.value_or(0)).c_str() : "unknown option"); if(methods_id.at(methodid.value_or(0)) == "totp") { return PostMethod< Pam_t >{systemToken, tid, methods_id.at(methodid.value_or(0))}; } return tl::unexpected{PamAction::accept}; } }; } // namespace rublon