Skip to content

Commit

Permalink
[feat]Super User Privilege Toggle
Browse files Browse the repository at this point in the history
  • Loading branch information
shubhdevelop committed Aug 30, 2024
1 parent b478da6 commit fdaabe5
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 5 deletions.
53 changes: 53 additions & 0 deletions controllers/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const { addLog } = require("../models/logs");
const { getUserStatus } = require("../models/userStatus");
const config = require("config");
const discordDeveloperRoleId = config.get("discordDeveloperRoleId");
const authService = require("../services/authService");

const verifyUser = async (req, res) => {
const userId = req.userData.id;
Expand Down Expand Up @@ -376,6 +377,9 @@ const getSelfDetails = async (req, res) => {
const user = await dataAccess.retrieveUsers({
userdata: req.userData,
});
if (req.userData.superUserAccess === false) {
user.roles.super_user = false;
}
return res.send(user);
}
return res.boom.notFound("User doesn't exist");
Expand Down Expand Up @@ -827,6 +831,53 @@ const setInDiscordScript = async (req, res) => {
}
};

const getSuperUserAccessStatus = async (req, res) => {
try {
if (req.userData.superUserAccess !== undefined) {
return res.json({ currentAccess: req.userData.superUserAccess });
} else {
return res.json({ message: "Super User Access Modifier Not Set!" });
}
} catch (error) {
return res.boom.badImplementation({ message: INTERNAL_SERVER_ERROR });
}
};

const setSuperUserAccessLimiter = async (req, res) => {
try {
let value;
switch (req.query.value) {
case "true":
value = true;
break;
case "false":
value = false;
break;
default:
break;
}
if (value !== undefined) {
const token = req.cookies[config.get("userToken.cookieName")];
const { userId } = authService.decodeAuthToken(token);
const newToken = authService.generateAuthToken({ userId, superUserAccess: value });
const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl"));
res.cookie(config.get("userToken.cookieName"), newToken, {
domain: rdsUiUrl.hostname,
expires: new Date(Date.now() + config.get("userToken.ttl") * 1000),
httpOnly: true,
secure: true,
sameSite: "lax",
});
return res.json({ message: "Super User Privilege Access Set!", currentAccess: value });
} else {
return res.boom.badRequest("Wrong value in query param, value can be either true or false");
}
} catch (error) {
logger.error(`Error while Setting Super Privilege Access Limiter: ${error}`);
return res.boom.badImplementation({ message: INTERNAL_SERVER_ERROR });
}
};

const updateRoles = async (req, res) => {
try {
const result = await dataAccess.retrieveUsers({ id: req.params.id });
Expand Down Expand Up @@ -985,5 +1036,7 @@ module.exports = {
archiveUserIfNotInDiscord,
usersPatchHandler,
isDeveloper,
setSuperUserAccessLimiter,
getSuperUserAccessStatus,
getIdentityStats,
};
21 changes: 16 additions & 5 deletions middlewares/authenticate.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const checkRestricted = async (req, res, next) => {
module.exports = async (req, res, next) => {
try {
let token = req.cookies[config.get("userToken.cookieName")];

/**
* Enable Bearer Token authentication for NON-PRODUCTION environments
* This is enabled as Swagger UI does not support cookie authe
Expand All @@ -51,12 +50,24 @@ module.exports = async (req, res, next) => {
token = req.headers.authorization.split(" ")[1];
}

const { userId } = authService.verifyAuthToken(token);
const { userId, superUserAccess } = authService.verifyAuthToken(token);
const userDoc = await dataAccess.retrieveUsers({ id: userId });
let userData;

// add user data to `req.userData` for further use
const userData = await dataAccess.retrieveUsers({ id: userId });
req.userData = userData.user;

if (superUserAccess === false) {
userData = userDoc.user;
userData.roles.super_user = false;
userData.superUserAccess = false;
} else if (superUserAccess === true || superUserAccess === undefined) {
userData = userDoc.user;
if (superUserAccess === true) {
userData.superUserAccess = true;
} else {
userData.superUserAccess = undefined;
}
}
req.userData = userData;
return checkRestricted(req, res, next);
} catch (err) {
logger.error(err);
Expand Down
2 changes: 2 additions & 0 deletions routes/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ router.get("/userId/:userId", users.getUserById);
router.patch("/self", authenticate, userValidator.updateUser, users.updateSelf);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
router.get("/", userValidator.getUsers, users.getUsers);
router.get("/self", authenticate, users.getSelfDetails);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
router.get("/set-super-user-access", authenticate, users.setSuperUserAccessLimiter);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
router.get("/get-super-user-access-status", authenticate, users.getSuperUserAccessStatus);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
router.get("/isDeveloper", authenticate, users.isDeveloper);
router.get("/isUsernameAvailable/:username", authenticate, users.getUsernameAvailabilty);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
router.get("/username", authenticate, userValidator.validateGenerateUsernameQuery, users.generateUsername);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
Expand Down
174 changes: 174 additions & 0 deletions test/integration/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,42 @@ describe("Users", function () {
});
});

it("Should return the logged in super_user with modified privilege, super_user : false, based on superUserAccess in jwt token", async function () {
const superUser = userData[4];
const userId = await addUser(superUser);
jwt = authService.generateAuthToken({ userId, superUserAccess: false });
const res = await chai.request(app).get("/users/self").set("cookie", `${cookieName}=${jwt}`);

expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body.roles.super_user).to.be.equal(false);
await cleanDb();
});

it("Should return the logged in super_user with unmodified privileges, super_user : true, based on superUserAccess in jwt token", async function () {
const superUser = userData[4];
const userId = await addUser(superUser);
jwt = authService.generateAuthToken({ userId, superUserAccess: true });
const res = await chai.request(app).get("/users/self").set("cookie", `${cookieName}=${jwt}`);

expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body.roles.super_user).to.be.equal(true);
await cleanDb();
});

it("Should return the logged in super_user as it is if the superUserAccess not found in jwt token", async function () {
const superUser = userData[4];
const userId = await addUser(superUser);
jwt = authService.generateAuthToken({ userId });
const res = await chai.request(app).get("/users/self").set("cookie", `${cookieName}=${jwt}`);

expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body.roles.super_user).to.be.equal(true);
await cleanDb();
});

it("Should return 401 if not logged in", function (done) {
chai
.request(app)
Expand Down Expand Up @@ -2419,4 +2455,142 @@ describe("Users", function () {
});
});
});

describe("GET /get-super-user-access-status", function () {
beforeEach(async function () {
userId = await addUser();
});

afterEach(async function () {
await cleanDb();
});

it("Should return true if the token has superUserAccess role in it, and it's set to be true", function (done) {
jwt = authService.generateAuthToken({ userId, superUserAccess: true });
chai
.request(app)
.get("/users/get-super-user-access-status")
.set("cookie", `${cookieName}=${jwt}`)
.end((err, res) => {
if (err) {
return done(err);
}
expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body).to.have.a.property("currentAccess");
expect(res.body.currentAccess).to.be.equal(true);
return done();
});
});

it("Should return false if the token has superUserAccess role in it, and it's set to be false", function (done) {
jwt = authService.generateAuthToken({ userId, superUserAccess: false });
chai
.request(app)
.get("/users/get-super-user-access-status")
.set("cookie", `${cookieName}=${jwt}`)
.end((err, res) => {
if (err) {
return done(err);
}

expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body).to.have.property("currentAccess");
expect(res.body.currentAccess).to.be.equal(false);
return done();
});
});

it("Should return a message: 'Super User Access Modifier Not Set!', if superUserAccess not set in jwt token", function (done) {
jwt = authService.generateAuthToken({ userId });
chai
.request(app)
.get("/users/get-super-user-access-status")
.set("cookie", `${cookieName}=${jwt}`)
.end((err, res) => {
if (err) {
return done(err);
}

expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body).to.not.have.property("currentAccess");
expect(res.body).to.have.property("message");
expect(res.body.currentAccess).to.be.equal(undefined);
expect(res.body.message).to.be.equal("Super User Access Modifier Not Set!");
return done();
});
});
});

describe("GET /set-super-user-access", function () {
beforeEach(async function () {
userId = await addUser();
});

afterEach(async function () {
await cleanDb();
});

it("Should return currentAccess: true, with message: 'Super User Privilege Access Set!'", function (done) {
jwt = authService.generateAuthToken({ userId });
chai
.request(app)
.get("/users/set-super-user-access?value=true")
.set("cookie", `${cookieName}=${jwt}`)
.end((err, res) => {
if (err) {
return done(err);
}
expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body).to.have.a.property("currentAccess");
expect(res.body).to.have.a.property("message");
expect(res.body.currentAccess).to.be.equal(true);
expect(res.body.message).to.be.equal("Super User Privilege Access Set!");
return done();
});
});

it("Should return currentAccess: false, with message: 'Super User Privilege Access Set!'", function (done) {
jwt = authService.generateAuthToken({ userId });
chai
.request(app)
.get("/users/set-super-user-access?value=false")
.set("cookie", `${cookieName}=${jwt}`)
.end((err, res) => {
if (err) {
return done(err);
}
expect(res).to.have.status(200);
expect(res.body).to.be.a("object");
expect(res.body).to.have.a.property("currentAccess");
expect(res.body).to.have.a.property("message");
expect(res.body.currentAccess).to.be.equal(false);
expect(res.body.message).to.be.equal("Super User Privilege Access Set!");
return done();
});
});

it("Should return status 400", function (done) {
jwt = authService.generateAuthToken({ userId });
chai
.request(app)
.get("/users/set-super-user-access?value=xyz")
.set("cookie", `${cookieName}=${jwt}`)
.end((err, res) => {
if (err) {
return done(err);
}
expect(res).to.have.status(400);
expect(res.body).to.be.a("object");
expect(res.body).to.not.have.a.property("currentAccess");
expect(res.body).to.have.a.property("message");
expect(res.body.currentAccess).to.be.equal(undefined);
expect(res.body.message).to.be.equal("Wrong value in query param, value can be either true or false");
return done();
});
});
});
});

0 comments on commit fdaabe5

Please sign in to comment.