Skip to content

Commit

Permalink
Remove 'api' from .gitignore
Browse files Browse the repository at this point in the history
  • Loading branch information
davidcrammer committed Apr 1, 2024
1 parent 0fd9ac2 commit d3678cd
Show file tree
Hide file tree
Showing 34 changed files with 2,489 additions and 1 deletion.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ out
# Nuxt.js build / generate output
.nuxt
dist
api

# Gatsby files
.cache/
Expand Down
159 changes: 159 additions & 0 deletions api/classes/authFactorClass.js
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;
110 changes: 110 additions & 0 deletions api/classes/capabilitiesClass.js
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;
89 changes: 89 additions & 0 deletions api/classes/cookiesClass.js
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;
Loading

0 comments on commit d3678cd

Please sign in to comment.