2022-09-04 10:35:10 +02:00

131 lines
5.2 KiB
C++

#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 "controllers/controllers.h"
#include "dto/dto.h"
namespace config {
std::string get_id() {
static std::string val = drogon::app().getCustomConfig()["gitlab_id"].asString();
return val;
}
std::string get_secret() {
static std::string val = drogon::app().getCustomConfig()["gitlab_secret"].asString();
return val;
}
std::string get_url() {
static std::string val = drogon::app().getCustomConfig()["gitlab_url"].asString();
return val;
}
std::string get_api_url() {
static std::string val = drogon::app().getCustomConfig()["gitlab_api_url"].asString();
return val;
}
std::string get_redirect_url() {
static std::string val = drogon::app().getCustomConfig()["gitlab_redirect_url"].asString();
return val;
}
}
std::string get_redirect_uri() {
std::stringstream ss;
ss << config::get_redirect_url()
<< "/api/auth/gitlab_callback";
return drogon::utils::urlEncode(ss.str());
}
const drogon::HttpClientPtr& get_gitlab_client() {
static drogon::HttpClientPtr client = drogon::HttpClient::newHttpClient(config::get_api_url(), drogon::app().getLoop(), false, false);
return client;
}
namespace api {
std::optional<auth::gitlab_tokens> auth::get_gitlab_tokens(const std::string& code_or_token, bool token) {
std::stringstream ss;
ss << "/oauth/token"
<< "?redirect_uri=" << get_redirect_uri()
<< "&client_id=" << config::get_id()
<< "&client_secret=" << config::get_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<gitlab_tokens>(
json["access_token"].as<std::string>(),
json["refresh_token"].as<std::string>()
);
}
std::optional<auth::gitlab_user> 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<gitlab_user>(
json["username"].as<std::string>(),
json.get("is_admin", false).as<bool>()
);
}
void auth::gitlab(req_type req, cbk_type cbk) {
std::stringstream ss;
ss << config::get_url() << "/oauth/authorize"
<< "?redirect_uri=" << get_redirect_uri()
<< "&client_id=" << config::get_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(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