-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0fd9ac2
commit d3678cd
Showing
34 changed files
with
2,489 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,7 +91,6 @@ out | |
# Nuxt.js build / generate output | ||
.nuxt | ||
dist | ||
api | ||
|
||
# Gatsby files | ||
.cache/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
"use strict"; | ||
|
||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = void 0; | ||
var _AuthFactor = _interopRequireDefault(require("../models/AuthFactor")); | ||
var _User = _interopRequireDefault(require("../models/User")); | ||
var _CodedError = _interopRequireDefault(require("../config/CodedError")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/** | ||
* @typedef {Object} AuthFactorType | ||
* Options | ||
* - TOTP | ||
* - WEBAUTHN | ||
* - OAUTH | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} AuthFactor | ||
* @property {string} id - The id of the auth factor (UUID, set by the database) | ||
* @property {string} userId - The id of the user that owns the auth factor | ||
* @property {AuthFactorType} factor - The type of auth factor (TOTP, SMS, etc.) | ||
* @property {string} secret - The secret that is used to generate the TOTP | ||
*/ | ||
/** | ||
* Handles the methods for interacitng with the AuthFactor data | ||
*/ | ||
class AuthFactor { | ||
/** | ||
* Creates an auth record in the database with a secret | ||
* - Is inactive when created because the user needs to verify it | ||
* | ||
* @async | ||
* @param {string} userId - The id of the user that owns the auth factor | ||
* @param {AuthFactorType} factor - The type of auth factor (TOTP, SMS, etc.) | ||
* @param {string} secret - The secret that is used to generate the TOTP | ||
* @returns {Promise<string|boolean>} The id of the auth factor record or false if it fails | ||
*/ | ||
async createRecord(userId, factor, secret) { | ||
try { | ||
const authFactor = await _AuthFactor.default.create({ | ||
userId, | ||
factor, | ||
secret, | ||
verified: false | ||
}); | ||
return authFactor.dataValues; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, error.status ?? 500, error.location ?? "AUTHFACTOR|01"); | ||
} | ||
} | ||
|
||
/** | ||
* Activates an auth factor record | ||
* - Sets the verified flag to true | ||
* - Sets the verifiedAt date to the current date | ||
* | ||
* @async | ||
* @param {string} id - The id of the auth factor record | ||
* @returns {Promise<boolean>} True if the record was activated, false if it fails | ||
*/ | ||
async activateRecord(id) { | ||
try { | ||
const authFactor = await _AuthFactor.default.findOne({ | ||
where: { | ||
id | ||
} | ||
}); | ||
if (!authFactor) throw new _CodedError.default("Auth factor not found", 404, "AUTHFACTOR|02"); | ||
await authFactor.update({ | ||
verified: true, | ||
verifiedAt: new Date() | ||
}); | ||
|
||
// update user mfa flag | ||
const user = await _User.default.findOne({ | ||
where: { | ||
id: authFactor.userId | ||
} | ||
}); | ||
if (!user) throw new _CodedError.default("User not found", 404, "AUTHFACTOR|03"); | ||
await user.update({ | ||
mfa: true | ||
}); | ||
return true; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, error.status ?? 500, error.location ?? "AUTHFACTOR|03"); | ||
} | ||
} | ||
|
||
/** | ||
* Deletes an auth factor record | ||
* - if after deletion the user has no auth factors, the user's mfa flag is set to false | ||
* @async | ||
* @param {string} id - The id of the auth factor record | ||
* @returns {Promise<boolean>} True if the record was deleted, false if it fails | ||
*/ | ||
async deleteRecord(id) { | ||
try { | ||
const authFactor = await _AuthFactor.default.findOne({ | ||
where: { | ||
id | ||
} | ||
}); | ||
if (!authFactor) throw new _CodedError.default("Auth factor not found", 404, "AUTHFACTOR|04"); | ||
await authFactor.destroy(); | ||
const user = await _User.default.findOne({ | ||
where: { | ||
id: authFactor.userId | ||
} | ||
}); | ||
if (!user) throw new _CodedError.default("User not found", 404, "AUTHFACTOR|05"); | ||
const userAuthFactors = await user.getAuthFactors(); | ||
if (userAuthFactors.length === 0) { | ||
await user.update({ | ||
mfa: false | ||
}); | ||
} | ||
return true; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, error.status ?? 500, error.location ?? "AUTHFACTOR|05"); | ||
} | ||
} | ||
|
||
/** | ||
* Deletes all auth factor records for a user and sets the user's mfa flag to false | ||
* | ||
* @async | ||
* @param {string} userId - The id of the user that owns the auth factor | ||
* @returns {Promise<boolean>} True if the records were deleted, false if it fails | ||
*/ | ||
async disableMFA(userId) { | ||
try { | ||
const user = await _User.default.findOne({ | ||
where: { | ||
id: userId | ||
} | ||
}); | ||
if (!user) throw new _CodedError.default("User not found", 404, "AUTHFACTOR|06"); | ||
await user.update({ | ||
mfa: false | ||
}); | ||
const authFactors = await _AuthFactor.default.getAll({ | ||
where: { | ||
userId | ||
} | ||
}); | ||
if (!authFactors) throw new _CodedError.default("Auth factors not found", 404, "AUTHFACTOR|07"); | ||
authFactors.forEach(async authFactor => { | ||
await authFactor.destroy(); | ||
}); | ||
return true; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, error.status ?? 500, error.location ?? "AUTHFACTOR|08"); | ||
} | ||
} | ||
} | ||
var _default = exports.default = AuthFactor; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
"use strict"; | ||
|
||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = void 0; | ||
var _Capability = _interopRequireDefault(require("../models/Capability")); | ||
var _Role = _interopRequireDefault(require("../models/Role")); | ||
var _CodedError = _interopRequireDefault(require("../config/CodedError")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
class Capability { | ||
/** | ||
* Get all capabilities that match the conditions | ||
* @async | ||
* @param {*} conditions | ||
* @returns {Promise<Capability[]>} | ||
*/ | ||
async getCapabilities(conditions = {}) { | ||
try { | ||
const capabilities = await _Capability.default.findAll({ | ||
where: conditions | ||
}); | ||
return capabilities; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, 400, "CAPABILITY|00"); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the first capability that matches the conditions | ||
*/ | ||
async getCapability(conditions = {}) { | ||
try { | ||
const capability = await _Capability.default.findOne({ | ||
where: conditions | ||
}); | ||
return capability; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, 400, "CAPABILITY|01"); | ||
} | ||
} | ||
|
||
/** | ||
* Create a new capability | ||
* @async | ||
* @param {Object} data | ||
* @param {Object} options | ||
* - | ||
* - roles: Array - The names of the roles to assign the capability to | ||
* @returns {Promise<Capability>} | ||
*/ | ||
async createCapability(data = {}, options = {}) { | ||
const { | ||
name, | ||
description | ||
} = data; | ||
const { | ||
roles | ||
} = options; | ||
try { | ||
const capability = await _Capability.default.create({ | ||
name, | ||
description | ||
}); | ||
if (roles) { | ||
await capability.setRoles(roles); | ||
} | ||
return capability; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, 400, "CAPABILITY|02"); | ||
} | ||
} | ||
|
||
/** | ||
* Delete capabilities | ||
* - Will do nothing if no capabilities are provided | ||
* - Removes the capability from all roles | ||
* @async | ||
* @param {Array} capabilities - The names of the capabilities to delete | ||
* @returns {Promise<BOOLEAN>} - True if the capabilities were deleted | ||
*/ | ||
async deleteCapabilities(capabilities) { | ||
try { | ||
if (!capabilities) throw new _CodedError.default("No capabilities provided", 400, "CAPABILITY|02"); | ||
if (!Array.isArray(capabilities)) throw new _CodedError.default("Capabilities must be an array", 400, "CAPABILITY|03"); | ||
await _Capability.default.destroy({ | ||
where: { | ||
name: capabilities | ||
} | ||
}); | ||
|
||
// Remove the capabilities from all roles | ||
const roles = await _Role.default.findAll({ | ||
include: [{ | ||
model: _Capability.default, | ||
as: "capabilities" | ||
}] | ||
}); | ||
roles.forEach(async role => { | ||
const roleCapabilities = role.capabilities.map(capability => capability.name); | ||
const updatedCapabilities = roleCapabilities.filter(capability => !capabilities.includes(capability)); | ||
await role.setCapabilities(updatedCapabilities); | ||
}); | ||
return true; | ||
} catch (error) { | ||
throw new _CodedError.default(error.message, 400, "CAPABILITY|03"); | ||
} | ||
} | ||
} | ||
var _default = exports.default = Capability; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
"use strict"; | ||
|
||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = void 0; | ||
var _CodedError = _interopRequireDefault(require("../config/CodedError")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } | ||
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } | ||
class Cookies { | ||
constructor(req, res) { | ||
this.req = req; | ||
this.res = res; | ||
} | ||
|
||
/** | ||
* Sets a cookie in the response | ||
*/ | ||
set(name, value, options = {}) { | ||
try { | ||
this.res.cookie(name, value, options); | ||
return true; | ||
} catch (error) { | ||
throw new _CodedError.default(`Error setting cookie: ${error.message}`, 500, "LOG|05"); | ||
} | ||
} | ||
setSessionCookie(value) { | ||
this.set("session", value, { | ||
httpOnly: true, | ||
secure: process.env.NODE_ENV === "production", | ||
sameSite: "strict" | ||
}); | ||
} | ||
|
||
/** | ||
* Gets the refresh token from the request cookies | ||
* @returns {String} - The refresh token or false if not found | ||
*/ | ||
getRefreshToken() { | ||
try { | ||
const token = this.req?.cookies?.refreshToken; | ||
return token; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Adds an httpOnly cookie to the response | ||
* @param {string} name - The name of the cookie | ||
* @param {*} value - The value of the cookie | ||
* @param {*} options - The options of the cookie | ||
* @returns true if the cookie was added, throws an error otherwise | ||
*/ | ||
setHttpOnly(name, value, options = {}) { | ||
try { | ||
this.res.cookie(name, value, _objectSpread({ | ||
httpOnly: true, | ||
secure: process.env.NODE_ENV === "production" | ||
}, options)); | ||
return true; | ||
} catch (error) { | ||
throw new _CodedError.default(`Error setting cookie: ${error.message}`, 500, "LOG|05"); | ||
} | ||
} | ||
|
||
/** | ||
* Sets the refresh token in the response cookies | ||
* | ||
* @param {*} token - The refresh token | ||
* @returns true if the cookie was added, throws an error otherwise | ||
*/ | ||
setRefreshToken(token) { | ||
try { | ||
this.setHttpOnly("refreshToken", token, { | ||
maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days | ||
}); | ||
|
||
return true; | ||
} catch (error) { | ||
throw new _CodedError.default(`Error setting cookie: ${error.message}`, 500, "LOG|05"); | ||
} | ||
} | ||
} | ||
var _default = exports.default = Cookies; |
Oops, something went wrong.