Skip to content

Commit

Permalink
Add in role routes
Browse files Browse the repository at this point in the history
  • Loading branch information
davidcrammer committed Dec 4, 2023
1 parent 7b36832 commit 3c10767
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 2 deletions.
14 changes: 14 additions & 0 deletions migrations/20231204055326-add-baseCategory-field.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use strict"

module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn("Roles", "baseCapabilities", {
type: Sequelize.JSON,
allowNull: true, // allows null if you want the field to be optional
})
},

down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn("Roles", "baseCapabilities")
},
}
14 changes: 14 additions & 0 deletions src/database/Role.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,22 @@ export const Role = sequelize.define("Role", {
type: DataTypes.STRING,
allowNull: true,
},
baseCapabilities: {
type: DataTypes.JSON,
allowNull: true,
},
capabilities: {
type: DataTypes.JSON,
allowNull: true,
},
inheritFrom: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: "Roles", // same table
key: "id", // primary key of Roles table
},
onUpdate: "CASCADE",
onDelete: "SET NULL",
},
})
150 changes: 148 additions & 2 deletions src/routes/roleRoutes.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,156 @@
import express from "express"
import verifyUser from "../middleware/verifyUser"
import { Role } from "../database/Role"
import { User } from "../database/User"
import CodedError from "../config/CodedError"
import updateCapabilities from "../utils/updateCapabilities"

const router = express.Router()

router.get("/", verifyUser("roles_read"), async (req, res) => {
res.json({ success: true, message: "Auth Service is live" })
router.get("/", verifyUser("roles_read"), async (req, res, next) => {
try {
const roles = await Role.findAll()
return res.status(200).json({ success: true, data: roles })
} catch (error) {
next(error)
}
})

router.post("/", verifyUser("roles_create"), async (req, res, next) => {
let { name, description, capabilities: baseCapabilities, inheritFrom } = req.body

try {
if (!name) throw new CodedError("Name is required", 400, "ROL|01")
baseCapabilities = Array.isArray(baseCapabilities) ? [...baseCapabilities] : []

const roleExists = await Role.findOne({ where: { name } })
if (roleExists) throw new CodedError("Role already exists", 400, "ROL|02")

if (inheritFrom) {
const parentRole = await Role.findOne({ where: { id: inheritFrom } })
if (!parentRole) throw new CodedError("Parent role does not exist", 400, "ROL|03")
}

const role = await Role.create({ name, description, baseCapabilities, inheritFrom })

updateCapabilities()

return res.status(200).json({ success: true, data: role })
} catch (error) {
next(error)
}
})

router.put("/:id", verifyUser("roles_update"), async (req, res, next) => {
const { id } = req.params
let { name, description, capabilities: baseCapabilities, inheritFrom } = req.body

try {
const role = await Role.findOne({ where: { id } })
if (!role) throw new CodedError("Role does not exist", 400, "ROL|04")

const newInheritFrom = inheritFrom || role.dataValues.inheritFrom
if (inheritFrom) {
const parentRole = await Role.findOne({ where: { id: newInheritFrom } })
if (!parentRole) throw new CodedError("Parent role does not exist", 400, "ROL|03")
}

await role.update({ name, description, baseCapabilities, inheritFrom })

updateCapabilities()

return res.status(200).json({ success: true, data: role })
} catch (error) {
next(error)
}
})

router.delete("/:id", verifyUser("roles_delete"), async (req, res, next) => {
const { id } = req.params

try {
const role = await Role.findOne({ where: { id } })
if (!role) throw new CodedError("Role does not exist", 400, "ROL|04")

// remove role from all users
const users = await User.findAll()
users.forEach(async (user) => {
if (user.dataValues.role === id) await user.update({ role: null })
})

// remove role from inheritFrom of all roles
const roles = await Role.findAll()
roles.forEach(async (role) => {
if (role.dataValues.inheritFrom === id) await role.update({ inheritFrom: null })
})

await role.destroy()

updateCapabilities()

return res.status(200).json({ success: true, data: role })
} catch (error) {
next(error)
}
})

router.post("/:id/capabilities", verifyUser("roles_update"), async (req, res, next) => {
const { id } = req.params
const { capabilities } = req.body

try {
const role = await Role.findOne({ where: { id } })
if (!role) throw new CodedError("Role does not exist", 400, "ROL|04")

if (!Array.isArray(capabilities)) throw new CodedError("Capabilities must be an array", 400, "ROL|05")

const newCapabilities = [...capabilities, ...role.dataValues.baseCapabilities]
const uniqueCapabilities = [...new Set(newCapabilities)]

await role.update({ baseCapabilities: uniqueCapabilities })

updateCapabilities()

return res.status(200).json({ success: true, data: role })
} catch (error) {
next(error)
}
})

router.delete("/:id/capabilities", verifyUser("roles_update"), async (req, res, next) => {
const { id } = req.params
const { capabilities } = req.body

try {
const role = await Role.findOne({ where: { id } })
if (!role) throw new CodedError("Role does not exist", 400, "ROL|04")

if (!Array.isArray(capabilities)) throw new CodedError("Capabilities must be an array", 400, "ROL|05")

const newCapabilities = role.dataValues.baseCapabilities.filter((capability) => !capabilities.includes(capability))

await role.update({ baseCapabilities: newCapabilities })

updateCapabilities()

return res.status(200).json({ success: true, data: role })
} catch (error) {
next(error)
}
})

router.post("/update", async (req, res, next) => {
const { secret } = req.body

try {
if (secret !== process.env.UPDATE_SECRET) throw new CodedError("Invalid secret", 400, "ROL|06")

updateCapabilities()

return res.status(200).json({ success: true })
} catch (error) {
next(error)
}
})

const roleRoutes = router
Expand Down
28 changes: 28 additions & 0 deletions src/utils/updateCapabilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Role } from "../database/Role"

export default async function updateCapabilities() {
const allRoles = await Role.findAll()

if (!allRoles || !Array.isArray(allRoles)) return console.log("No roles found")

async function getParentRoles(role, depth = 0) {
if (depth > 100) return console.warn("Max depth reached")
depth++

if (!role?.dataValues?.inheritFrom) return []
const parentRole = await Role.findOne({ where: { id: role.dataValues.inheritFrom } })
const parentRoles = await getParentRoles(parentRole, depth) // Await the recursive call
return [parentRole, ...parentRoles]
}

allRoles.forEach(async (role) => {
const parentRoles = await getParentRoles(role)
const allRoles = Array.isArray(role?.dataValues?.baseCapabilities) ? [role, ...parentRoles] : [...parentRoles]

const allCapabilities = allRoles.reduce((acc, role) => {
return [...acc, ...role.dataValues.baseCapabilities]
}, [])
const uniqueCapabilities = [...new Set(allCapabilities)]
await role.update({ capabilities: uniqueCapabilities })
})
}

0 comments on commit 3c10767

Please sign in to comment.