Add user authorization
This commit is contained in:
@@ -137,5 +137,99 @@ Json::Value AdminService::overviewStats(const auth::UserContext &user)
|
||||
"FROM reports WHERE status IN ('RESOLVED', 'CLOSED')"));
|
||||
return response;
|
||||
}
|
||||
} // namespace visione::services
|
||||
|
||||
Json::Value AdminService::listUsers(const auth::UserContext &user)
|
||||
{
|
||||
if (!auth::isAdmin(user))
|
||||
{
|
||||
return failure("forbidden");
|
||||
}
|
||||
|
||||
auto response = success();
|
||||
response["users"] = repositories::resultToJson(
|
||||
drogon::app().getDbClient()->execSqlSync(
|
||||
"SELECT u.id, u.first_name, u.last_name, u.email, u.fiscal_code, "
|
||||
"u.phone, u.points, u.banned, u.must_change_password, "
|
||||
"GROUP_CONCAT(r.name ORDER BY r.name SEPARATOR ',') AS roles "
|
||||
"FROM users u "
|
||||
"LEFT JOIN user_roles ur ON ur.user_id = u.id "
|
||||
"LEFT JOIN roles r ON r.id = ur.role_id "
|
||||
"GROUP BY u.id, u.first_name, u.last_name, u.email, u.fiscal_code, "
|
||||
"u.phone, u.points, u.banned, u.must_change_password "
|
||||
"ORDER BY u.created_at DESC LIMIT 200"));
|
||||
return response;
|
||||
}
|
||||
|
||||
Json::Value AdminService::updateUserRoles(const auth::UserContext &user,
|
||||
long long targetUserId,
|
||||
const Json::Value &body)
|
||||
{
|
||||
if (!auth::isAdmin(user) || !body["roles"].isArray())
|
||||
{
|
||||
return failure("invalid_roles");
|
||||
}
|
||||
|
||||
auto db = drogon::app().getDbClient();
|
||||
const auto target =
|
||||
db->execSqlSync("SELECT id FROM users WHERE id = ?", targetUserId);
|
||||
if (target.empty())
|
||||
{
|
||||
return failure("user_not_found");
|
||||
}
|
||||
|
||||
db->execSqlSync("DELETE FROM user_roles WHERE user_id = ?", targetUserId);
|
||||
for (const auto &role : body["roles"])
|
||||
{
|
||||
if (!role.isString())
|
||||
{
|
||||
return failure("invalid_roles");
|
||||
}
|
||||
|
||||
const auto roleRows =
|
||||
db->execSqlSync("SELECT id FROM roles WHERE name = ?", role.asString());
|
||||
if (roleRows.empty())
|
||||
{
|
||||
return failure("role_not_found");
|
||||
}
|
||||
db->execSqlSync(
|
||||
"INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)",
|
||||
targetUserId,
|
||||
roleRows[0]["id"].as<long long>());
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AdminService::updateUserBan(const auth::UserContext &user,
|
||||
long long targetUserId,
|
||||
const Json::Value &body)
|
||||
{
|
||||
if (!auth::isAdmin(user) || !body["banned"].isBool())
|
||||
{
|
||||
return failure("invalid_ban_payload");
|
||||
}
|
||||
if (targetUserId == user.id)
|
||||
{
|
||||
return failure("cannot_ban_self");
|
||||
}
|
||||
|
||||
auto db = drogon::app().getDbClient();
|
||||
const auto target =
|
||||
db->execSqlSync("SELECT id FROM users WHERE id = ?", targetUserId);
|
||||
if (target.empty())
|
||||
{
|
||||
return failure("user_not_found");
|
||||
}
|
||||
|
||||
db->execSqlSync("UPDATE users SET banned = ? WHERE id = ?",
|
||||
body["banned"].asBool(),
|
||||
targetUserId);
|
||||
if (body["banned"].asBool())
|
||||
{
|
||||
db->execSqlSync(
|
||||
"UPDATE api_tokens SET revoked_at = NOW() "
|
||||
"WHERE user_id = ? AND revoked_at IS NULL",
|
||||
targetUserId);
|
||||
}
|
||||
return success();
|
||||
}
|
||||
} // namespace visione::services
|
||||
|
||||
@@ -16,6 +16,12 @@ class AdminService
|
||||
Json::Value listTeams();
|
||||
Json::Value createTeam(const auth::UserContext &user, const Json::Value &body);
|
||||
Json::Value overviewStats(const auth::UserContext &user);
|
||||
Json::Value listUsers(const auth::UserContext &user);
|
||||
Json::Value updateUserRoles(const auth::UserContext &user,
|
||||
long long targetUserId,
|
||||
const Json::Value &body);
|
||||
Json::Value updateUserBan(const auth::UserContext &user,
|
||||
long long targetUserId,
|
||||
const Json::Value &body);
|
||||
};
|
||||
} // namespace visione::services
|
||||
|
||||
|
||||
+42
-1
@@ -205,6 +205,40 @@ Json::Value AuthService::loginWithPassword(const Json::Value &body)
|
||||
return issueToken(user[0]["id"].as<long long>());
|
||||
}
|
||||
|
||||
Json::Value AuthService::changePassword(const auth::UserContext &user,
|
||||
const Json::Value &body)
|
||||
{
|
||||
const auto missing =
|
||||
validation::missingStrings(body, {"current_password", "new_password"});
|
||||
if (!missing.empty())
|
||||
{
|
||||
return failure("missing_fields");
|
||||
}
|
||||
if (body["new_password"].asString().size() < 10)
|
||||
{
|
||||
return failure("weak_password");
|
||||
}
|
||||
|
||||
auto db = drogon::app().getDbClient();
|
||||
const auto current = db->execSqlSync(
|
||||
"SELECT password_hash FROM users WHERE id = ? AND banned = FALSE",
|
||||
user.id);
|
||||
if (current.empty() ||
|
||||
!crypto::verifyPassword(
|
||||
body["current_password"].asString(),
|
||||
current[0]["password_hash"].as<std::string>()))
|
||||
{
|
||||
return failure("invalid_credentials");
|
||||
}
|
||||
|
||||
db->execSqlSync(
|
||||
"UPDATE users SET password_hash = ?, must_change_password = FALSE "
|
||||
"WHERE id = ?",
|
||||
crypto::hashPassword(body["new_password"].asString()),
|
||||
user.id);
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AuthService::currentUser(const auth::UserContext &user)
|
||||
{
|
||||
auto db = drogon::app().getDbClient();
|
||||
@@ -226,8 +260,15 @@ Json::Value AuthService::currentUser(const auth::UserContext &user)
|
||||
response["user"]["must_change_password"] =
|
||||
result[0]["must_change_password"].as<bool>();
|
||||
response["user"]["role"] = user.role;
|
||||
for (const auto &role : user.roles)
|
||||
{
|
||||
response["user"]["roles"].append(role);
|
||||
}
|
||||
for (const auto &permission : user.permissions)
|
||||
{
|
||||
response["user"]["permissions"].append(permission);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
} // namespace visione::services
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class AuthService
|
||||
Json::Value requestOtp(const Json::Value &body);
|
||||
Json::Value verifyOtp(const Json::Value &body);
|
||||
Json::Value loginWithPassword(const Json::Value &body);
|
||||
Json::Value changePassword(const auth::UserContext &user, const Json::Value &body);
|
||||
Json::Value currentUser(const auth::UserContext &user);
|
||||
};
|
||||
} // namespace visione::services
|
||||
|
||||
|
||||
Reference in New Issue
Block a user