#pragma clang diagnostic push #pragma ide diagnostic ignored "performance-unnecessary-value-param" #pragma ide diagnostic ignored "readability-make-member-function-const" #pragma ide diagnostic ignored "readability-convert-member-functions-to-static" #include #include "controllers/controllers.h" #include "dto/dto.h" const std::string GITLAB_ID = "98bcbad78cb1f880d1d1de62291d70a791251a7bea077bfe7df111ef3c115760"; const std::string GITLAB_SECRET = "7ee01d2b204aff3a05f9d028f004d169b6d381ec873e195f314b3935fa150959"; const std::string GITLAB_URL = "https://gitlab.mattv.de"; const std::string GITLAB_API_URL = "https://ssh.gitlab.mattv.de"; std::string get_redirect_uri(req_type req) { auto host_header = req->headers().find("host"); std::stringstream ss; ss << (req->isOnSecureConnection() ? "https" : "http") << "://" << (host_header != req->headers().end() ? host_header->second : "127.0.0.1:1234") << "/api/auth/gitlab_callback"; return drogon::utils::urlEncode(ss.str()); } const drogon::HttpClientPtr& get_gitlab_client() { static drogon::HttpClientPtr client = drogon::HttpClient::newHttpClient(GITLAB_API_URL, drogon::app().getLoop(), false, false); return client; } namespace api { std::optional auth::get_gitlab_tokens(req_type req, const std::string& code_or_token, bool token) { std::stringstream ss; ss << "/oauth/token" << "?redirect_uri=" << get_redirect_uri(req) << "&client_id=" << GITLAB_ID << "&client_secret=" << GITLAB_SECRET << (token ? "&refresh_token=" : "&code=") << code_or_token << "&grant_type=" << (token ? "refresh_token" : "authorization_code"); auto gitlab_req = drogon::HttpRequest::newHttpRequest(); gitlab_req->setPathEncode(false); gitlab_req->setPath(ss.str()); gitlab_req->setMethod(drogon::HttpMethod::Post); auto res_tuple = get_gitlab_client()->sendRequest(gitlab_req); auto res = res_tuple.second; if ((res->statusCode() != drogon::HttpStatusCode::k200OK) && (res->statusCode() != drogon::HttpStatusCode::k201Created)) return std::nullopt; auto json = *res->jsonObject(); return std::make_optional( json["access_token"].as(), json["refresh_token"].as() ); } std::optional auth::get_gitlab_user(const std::string& at) { auto gitlab_req = drogon::HttpRequest::newHttpRequest(); gitlab_req->setPath("/api/v4/user"); gitlab_req->addHeader("Authorization", "Bearer " + at); gitlab_req->setMethod(drogon::HttpMethod::Get); auto res_tuple = get_gitlab_client()->sendRequest(gitlab_req); auto res = res_tuple.second; if (res->statusCode() != drogon::HttpStatusCode::k200OK) return std::nullopt; auto json = *res->jsonObject(); return std::make_optional( json["username"].as(), json.get("is_admin", false).as() ); } void auth::gitlab(req_type req, cbk_type cbk) { std::stringstream ss; ss << GITLAB_URL << "/oauth/authorize" << "?redirect_uri=" << get_redirect_uri(req) << "&client_id=" << GITLAB_ID << "&scope=read_user&response_type=code"; cbk(drogon::HttpResponse::newRedirectionResponse(ss.str())); } void auth::gitlab_callback(req_type req, cbk_type cbk, std::string code) { auto tokens = get_gitlab_tokens(req, code, false); if (!tokens.has_value()) return cbk(dto::Responses::get_unauth_res("Invalid code")); auto info = get_gitlab_user(tokens->at); if (!info.has_value()) return cbk(dto::Responses::get_unauth_res("Invalid code")); db::MapperUser user_mapper(drogon::app().getDbClient()); auto db_users = user_mapper.findBy( db::Criteria(db::User::Cols::_name, db::CompareOps::EQ, info->name) && db::Criteria(db::User::Cols::_gitlab, db::CompareOps::EQ, 1) ); if (db_users.empty()) { db::User new_user; new_user.setName(info->name); new_user.setPassword(""); new_user.setGitlab(1); new_user.setRole(info->is_admin ? db::UserRole::ADMIN : db::UserRole::DISABLED); new_user.setRootId(0); new_user.setTfaType(db::tfaTypes::NONE); user_mapper.insert(new_user); generate_root(new_user); db_users.push_back(new_user); } db::User& db_user = db_users.at(0); db_user.setGitlabAt(tokens->at); db_user.setGitlabRt(tokens->rt); user_mapper.update(db_user); const std::string& token = get_token(db_user); cbk(drogon::HttpResponse::newRedirectionResponse("/set_token?token="+token)); } } #pragma clang diagnostic pop