#pragma once #include #include #include #include namespace rublon::method { class PasscodeBasedAuth : public AuthenticationStep< PasscodeBasedAuth > { protected: using base_t = AuthenticationStep< PasscodeBasedAuth >; const char * uri = "/api/transaction/confirmCode"; const char * userMessage{nullptr}; const uint_fast8_t length; const bool onlyDigits; constexpr static bool isdigit(char ch) { return std::isdigit(static_cast< unsigned char >(ch)); } bool digitsOnly(std::string_view userinput) const { return std::all_of(userinput.cbegin(), userinput.cend(), isdigit); } bool hasValidLength(std::string_view userInput) const { if(userInput.size() == length) { log(LogLevel::Debug, "User input size %d is correct", userInput.size()); return true; } else { log(LogLevel::Warning, "User input size %d is different then %d", userInput.size(), length); return false; } } bool hasValidCharacters(std::string_view userInput) const { if(onlyDigits ? digitsOnly(userInput) : true) { log(LogLevel::Debug, "User input contains valid characters"); return true; } else { log(LogLevel::Warning, "User input contains characters different then digits"); return false; } } template < typename PamInfo_t = LinuxPam > tl::expected< std::reference_wrapper< Document >, Error > readPasscode(Document & body, const PamInfo_t & pam) const { auto & alloc = body.GetAllocator(); auto vericode = pam.scan([](const char * userInput) { return std::string{userInput}; }, userMessage); if(hasValidLength(vericode) and hasValidCharacters(vericode)) { body.AddMember("vericode", Value{vericode.c_str(), alloc}, alloc); return body; } return tl::unexpected{Error{WerificationError{WerificationError::WrongCode}}}; } template < typename PamInfo_t = LinuxPam > tl::expected< std::reference_wrapper< Document >, Error > askForPasscodeAgain(Document & body, const PamInfo_t & pam) const { pam.print("passcode has wrong number of digits or contains illegal characters, please correct"); return readPasscode(body, pam); } template < typename PamInfo_t = LinuxPam > tl::expected< AuthenticationStatus, Error > checkAuthenticationStatus(const Document & coreResponse, const PamInfo_t & pam) const { RapidJSONPMRStackAlloc< 1024 > alloc; auto error = JSONPointer{"/result/error", &alloc}.Get(coreResponse); if(error) { pam.print("Wrong code"); return tl::unexpected{Error{WerificationError{WerificationError::WrongCode}}}; } pam.print("Verification code validated"); return AuthenticationStatus{AuthenticationStatus::Action::Confirmed}; } public: const char * name; PasscodeBasedAuth(std::string systemToken, std::string tid, const char * name, const char * userMessage, uint_fast8_t length, bool numbersOnly) : base_t(std::move(systemToken), std::move(tid)), userMessage{userMessage}, length{length}, onlyDigits{numbersOnly}, name{name} {} template < typename Hander_t, typename PamInfo_t = LinuxPam > tl::expected< AuthenticationStatus, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler, const PamInfo_t & pam) const { RapidJSONPMRStackAlloc< 2048 > alloc{}; Document body{rapidjson::kObjectType, &alloc}; const auto checkCodeValidity = [&](const auto & coreResponse) { return this->checkAuthenticationStatus(coreResponse, pam); }; const auto requestAuthorization = [&](const auto & body) { return coreHandler.request(alloc, uri, body); }; const auto askForPasscodeAgain = [&](const auto & /*error*/) { return this->askForPasscodeAgain(body, pam); }; this->addSystemToken(body); this->addTid(body); return readPasscode(body, pam) // .or_else(askForPasscodeAgain) .and_then(requestAuthorization) .and_then(checkCodeValidity); } }; } // namespace rublon::method