#include "rublon/json.hpp" #include "rublon/memory.hpp" #include #include #include #include #include #include #include #include #include #include #include std::string g_tid; int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { using namespace rublon; details::initLog(); PamStub pam{}; auto printAuthMessageAndExit = [&](const AuthenticationStatus status) { switch(status.action()) { case AuthenticationStatus::Action::Bypass: pam.print("RUBLON authentication BYPASSED"); return PAM_SUCCESS; case AuthenticationStatus::Action::Denied: pam.print("RUBLON authentication FAILED"); return PAM_MAXTRIES; case AuthenticationStatus::Action::Confirmed: pam.print("RUBLON authentication SUCCEEDED"); return PAM_SUCCESS; } pam.print("RUBLON connector has exited with unknown code, access DENY!\n"); return PAM_MAXTRIES; }; auto session = rublon::RublonFactory{}.startSession(pam); if(not session.has_value()) { return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); } if(!session->config().logging){ g_level = LogLevel::Warning; } auto & CH = session.value().coreHandler(); auto selectMethod = [&](const MethodSelect & selector) { return selector.create(pam); }; auto confirmMethod = [&](const PostMethod & postMethod) { return postMethod.handle(CH); }; auto confirmCode = [&](const MethodProxy & method) mutable { return method.fire(CH, pam); }; auto finalizeTransaction = [&](const AuthenticationStatus & status) -> tl::expected< AuthenticationStatus, Error > { if(status.userAuthorized()) { auto tok = std::string{status.accessToken().substr(10, 60).data(), 60}; Finish finish{session.value().config(), std::move(tok)}; finish.handle(CH); } return status; }; auto allowLogin = [&](const AuthenticationStatus & status) -> tl::expected< int, Error > { return printAuthMessageAndExit(status); }; auto mapError = [&](const Error & error) -> tl::expected< int, Error > { log(LogLevel::Error, "Process interrupted by {%s::%s}", error.errorClassName(), error.categoryName()); if(error.is< RublonAuthenticationInterrupt >()) { switch(error.get< RublonAuthenticationInterrupt >().errorClass) { case RublonAuthenticationInterrupt::ErrorClass::UserBaypass: return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); case RublonAuthenticationInterrupt::ErrorClass::UserDenied: pam.print("Access denied! Contact your administrator for more information"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); case RublonAuthenticationInterrupt::ErrorClass::UserWaiting: case RublonAuthenticationInterrupt::ErrorClass::UserPending: pam.print( "Your account is awaiting administrator's approval.\n" "Contact your administrator and ask them to approve your account"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); case RublonAuthenticationInterrupt::ErrorClass::UserNotFound: return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); } } if(error.is< MethodError >()) { switch(error.get< MethodError >().errorClass) { case MethodError::ErrorClass::BadMethod: return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); case MethodError::ErrorClass::BadUserInput: return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); case MethodError::ErrorClass::NoMethodAvailable: return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); } } if(error.is< ConnectionError >()) { if(session.value().config().failMode == FailMode::deny) { pam.print("Incorrect response from the Rublon API, user bypassed"); return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); } else { pam.print("Incorrect response from the Rublon API, user access denied"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); } } if(error.is< CoreHandlerError >()) { const auto & reson = error.get< CoreHandlerError >().reson; pam.print("Something went wrong and authentication could not be completed, contact your administrator"); if(reson == "UserBypassedException" or reson == "UserNotFoundException") return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); } if(error.is< WerificationError >()) { switch(error.get< WerificationError >().errorClass) { case WerificationError::ErrorClass::PasscodeException: pam.print(R"(Incorrect passcode)"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); case WerificationError::ErrorClass::BadInput: pam.print(R"(Ensure that the Secret Key is correct.)"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); } } if(error.is< RublonCheckApplicationException >()) { switch(error.get< RublonCheckApplicationException >().errorClass) { case RublonCheckApplicationException::ErrorClass::ApplicationNotFoundException: log(LogLevel::Error, R"(Could not find the application in the Rublon Admin Console.)"); log(LogLevel::Error, R"(Ensure that the application exists and the SystemToken is correct.)"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); case RublonCheckApplicationException::ErrorClass::InvalidSignatureException: log(LogLevel::Error, R"(Could not verify the signature.)"); log(LogLevel::Error, R"(Ensure that the Secret Key is correct.)"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); case RublonCheckApplicationException::ErrorClass::UnsupportedVersionException: log(LogLevel::Error, R"(The provided version of the app is unsupported.)"); log(LogLevel::Error, R"(Try changing the app version.)"); return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); } } return printAuthMessageAndExit(AuthenticationStatus::Action::Denied); }; { CheckApplication ca; auto ret = ca.call(CH, {session.value().config().systemToken.data(), session.value().config().systemToken.size()}).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); return PAM_MAXTRIES; } } auto ret = Init{session.value()} .handle(CH, pam) // .and_then(selectMethod) .and_then(confirmMethod) .and_then(confirmCode) .and_then(finalizeTransaction) .and_then(allowLogin) .or_else(mapError); return ret.value_or(PAM_MAXTRIES); }