From 3a0ce7c084a34eb17930503132485b42e037b164 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 19 Mar 2023 17:01:36 +0700 Subject: [PATCH] Service account checkpoint --- .../v2/serviceAccountsController.ts | 101 ++++++++-------- backend/src/helpers/membership.ts | 32 +++-- backend/src/helpers/membershipOrg.ts | 54 +++++---- backend/src/helpers/serviceAccount.ts | 110 ++++++++++++++++++ .../dto/AddServiceAccountPermissionDto.ts | 7 ++ .../interfaces/serviceAccounts/dto/index.ts | 4 +- .../middleware/requireMembershipOrgAuth.ts | 15 +-- .../src/middleware/requireOrganizationAuth.ts | 33 ++---- .../middleware/requireServiceAccountAuth.ts | 19 ++- .../src/models/serviceAccountPermission.ts | 4 +- backend/src/routes/v2/organizations.ts | 6 +- backend/src/routes/v2/serviceAccounts.ts | 35 +++++- backend/src/utils/errors.ts | 41 ++++++- backend/src/variables/index.ts | 12 +- backend/src/variables/permissions.ts | 19 +++ 15 files changed, 356 insertions(+), 136 deletions(-) create mode 100644 backend/src/helpers/serviceAccount.ts create mode 100644 backend/src/interfaces/serviceAccounts/dto/AddServiceAccountPermissionDto.ts create mode 100644 backend/src/variables/permissions.ts diff --git a/backend/src/controllers/v2/serviceAccountsController.ts b/backend/src/controllers/v2/serviceAccountsController.ts index 78758d85b..7c25390a7 100644 --- a/backend/src/controllers/v2/serviceAccountsController.ts +++ b/backend/src/controllers/v2/serviceAccountsController.ts @@ -6,8 +6,17 @@ import { ServiceAccountPermission } from '../../models'; import { - CreateServiceAccountDto + validateCreateServiceAccountPermission +} from '../../helpers/serviceAccount'; +import { + CreateServiceAccountDto, + AddServiceAccountPermissionDto } from '../../interfaces/serviceAccounts/dto'; +import { + PERMISSION_SA_WORKSPACE_SET, + PERMISSION_SA_SET +} from '../../variables'; +import { ServiceAccountKeyNotFoundError, ValidationError } from '../../utils/errors'; /** * Create a new service account under organization with id [organizationId] @@ -70,22 +79,39 @@ export const addServiceAccountKey = async (req: Request, res: Response) => { /** * Add a permission to service account with id [serviceAccountId] - * @param req - * @param res + * @param req + * @param res */ export const addServiceAccountPermission = async (req: Request, res: Response) => { const { name, workspaceId, environment - } = req.body; // TODO: add DTO + }: AddServiceAccountPermissionDto = req.body; - // TODO: validation? + if (PERMISSION_SA_WORKSPACE_SET.has(name)) { + // case: permission named [name] is workspace-related + + // some such permissions require workspaceId and environment to be present. + + if (!workspaceId || !environment) { + throw ValidationError({ + message: 'Failed validation that is workspace-related permission must specify a workspace and environment' + }); + } else { + const serviceAccountKey = await ServiceAccountKey.findOne({ + serviceAccount: req.serviceAccount._id, + workspace: new Types.ObjectId(workspaceId) + }); + + if (!serviceAccountKey) throw ServiceAccountKeyNotFoundError({ message: 'Failed to find service account key' }); + } + } const serviceAccountPermission = await new ServiceAccountPermission({ serviceAccount: req.serviceAccount._id, name, - workspace: new Types.ObjectId(workspaceId), + workspace: workspaceId ? new Types.ObjectId(workspaceId) : undefined, environment }); @@ -100,19 +126,15 @@ export const addServiceAccountPermission = async (req: Request, res: Response) = * @param res */ export const deleteServiceAccountPermission = async (req: Request, res: Response) => { - const { - name, - workspaceId, - environment - } = req.body; // TODO: DTO + const { serviceAccountPermissionId } = req.params; + + // user must either be an admin/owner of the organization or they must + // have created the service account in the first place to be able to delete it // TODO: how to delete just 1 permission? - const serviceAccountPermission = await ServiceAccountPermission.findOneAndDelete({ - serviceAccount: req.serviceAccount._id, - name, - workspace: new Types.ObjectId(workspaceId), - environment - }); + + + const serviceAccountPermission = await ServiceAccountPermission.findByIdAndDelete(serviceAccountPermissionId); return res.status(200).send({ serviceAccountPermission @@ -130,40 +152,19 @@ export const deleteServiceAccount = async (req: Request, res: Response) => { const serviceAccount = await ServiceAccount.findByIdAndDelete(serviceAccountId); - await ServiceAccountKey.deleteMany({ - serviceAccount: new Types.ObjectId(serviceAccountId) - }); + if (serviceAccount) { + // case: service account with id [serviceAccountId] was deleted + + await ServiceAccountKey.deleteMany({ + serviceAccount: serviceAccount?._id + }); + + await ServiceAccountPermission.deleteMany({ + serviceAccount: new Types.ObjectId(serviceAccountId) + }); + } - await ServiceAccountPermission.deleteMany({ - serviceAccount: new Types.ObjectId(serviceAccountId) - }); - return res.status(200).send({ serviceAccount }); -} - -// /** -// * Add a service account key to service account with id [serviceAccountId] -// * for workspace with id [workspaceId] -// * @param req -// * @param res -// * @returns -// */ -// export const addServiceAccountKey = async (req: Request, res: Response) => { -// const { -// workspaceId, -// encryptedKey, -// nonce -// } = req.body; - -// const serviceAccountKey = await new ServiceAccountKey({ -// encryptedKey, -// nonce, -// sender: req.user._id, -// serviceAccount: req.serviceAccount._d, -// workspace: new Types.ObjectId(workspaceId) -// }).save(); - -// return serviceAccountKey; -// } \ No newline at end of file +} \ No newline at end of file diff --git a/backend/src/helpers/membership.ts b/backend/src/helpers/membership.ts index 406162a8e..fc4a31074 100644 --- a/backend/src/helpers/membership.ts +++ b/backend/src/helpers/membership.ts @@ -1,5 +1,9 @@ import * as Sentry from '@sentry/node'; import { Membership, Key } from '../models'; +import { + MembershipNotFoundError, + BadRequestError +} from '../utils/errors'; /** * Validate that user with id [userId] is a member of workspace with id [workspaceId] @@ -19,23 +23,17 @@ const validateMembership = async ({ acceptedRoles: string[]; }) => { - let membership; - //TODO: Refactor code to take advantage of using RequestError. It's possible to create new types of errors for more detailed errors - try { - membership = await Membership.findOne({ - user: userId, - workspace: workspaceId - }).populate("workspace"); - - if (!membership) throw new Error('Failed to find membership'); - - if (!acceptedRoles.includes(membership.role)) { - throw new Error('Failed to validate membership role'); - } - } catch (err) { - Sentry.setUser(null); - Sentry.captureException(err); - throw new Error('Failed to validate membership'); + const membership = await Membership.findOne({ + user: userId, + workspace: workspaceId + }).populate("workspace"); + + if (!membership) { + throw MembershipNotFoundError({ message: 'Failed to find workspace membership' }); + } + + if (!acceptedRoles.includes(membership.role)) { + throw BadRequestError({ message: 'Failed to validate workspace membership role' }); } return membership; diff --git a/backend/src/helpers/membershipOrg.ts b/backend/src/helpers/membershipOrg.ts index 4de4be82d..65c1e01c2 100644 --- a/backend/src/helpers/membershipOrg.ts +++ b/backend/src/helpers/membershipOrg.ts @@ -1,40 +1,48 @@ import * as Sentry from '@sentry/node'; import { Types } from 'mongoose'; import { MembershipOrg, Workspace, Membership, Key } from '../models'; +import { + MembershipOrgNotFoundError, + BadRequestError +} from '../utils/errors'; /** * Validate that user with id [userId] is a member of organization with id [organizationId] * and has at least one of the roles in [acceptedRoles] - * + * @param {Object} obj + * @param {Types.ObjectId} obj.userId + * @param {Types.ObjectId} obj.organizationId + * @param {String[]} obj.acceptedRoles */ -const validateMembership = async ({ +const validateMembershipOrg = async ({ userId, organizationId, - acceptedRoles + acceptedRoles, + acceptedStatuses }: { - userId: string; - organizationId: string; + userId: Types.ObjectId; + organizationId: Types.ObjectId; acceptedRoles: string[]; + acceptedStatuses: string[]; }) => { - let membership; - try { - membership = await MembershipOrg.findOne({ - user: new Types.ObjectId(userId), - organization: new Types.ObjectId(organizationId) - }); - - if (!membership) throw new Error('Failed to find organization membership'); - - if (!acceptedRoles.includes(membership.role)) { - throw new Error('Failed to validate organization membership role'); - } - } catch (err) { - Sentry.setUser(null); - Sentry.captureException(err); - throw new Error('Failed to validate organization membership'); + const membershipOrg = await MembershipOrg.findOne({ + user: userId, + organization: organizationId + }); + + if (!membershipOrg) { + throw MembershipOrgNotFoundError({ message: 'Failed to find organization membership' }); } - return membership; + if (!acceptedRoles.includes(membershipOrg.role)) { + throw BadRequestError({ message: 'Failed to validate organization membership role' }); + } + + if (!acceptedStatuses.includes(membershipOrg.status)) { + throw BadRequestError({ message: 'Failed to validate organization membership status' }); + } + + return membershipOrg; } /** @@ -156,7 +164,7 @@ const deleteMembershipOrg = async ({ }; export { - validateMembership, + validateMembershipOrg, findMembershipOrg, addMembershipsOrg, deleteMembershipOrg diff --git a/backend/src/helpers/serviceAccount.ts b/backend/src/helpers/serviceAccount.ts new file mode 100644 index 000000000..b82d6b3b0 --- /dev/null +++ b/backend/src/helpers/serviceAccount.ts @@ -0,0 +1,110 @@ +import { Types } from 'mongoose'; +import { + Workspace, + ServiceAccount, + ServiceAccountKey +} from '../models'; +import { + WorkspaceNotFoundError, + ServiceAccountNotFoundError, + ServiceAccountKeyNotFoundError +} from '../utils/errors'; +import { + PERMISSION_SA_WORKSPACE_READ, + PERMISSION_SA_WORKSPACE_WRITE, + PERMISSION_SA_SET +} from '../variables'; + +/** + * Validate that user with id [userId] can provision the permission + * named [name] for a service account with id [serviceAccountId] and + * optionally workspace with id [workspaceId] and environment [environment] + * @param {Object} obj + * @param {String} obj.name - name of permission to create + * @param {Types.ObjectId} userId - id of user creating the permission + * @param {Types.ObjectId} serviceAccountId - id of service account that permission will be bound to + * @param {Types.ObjectId} workspaceId - id of workspace that permission concerns + * @param {Types.ObjectId} workspaceId - id of service account that permission will be bound to + */ +const validateCreateServiceAccountPermission = async ({ + name, + userId, + serviceAccountId, + workspaceId, + environment +}: { + name: string; + userId: Types.ObjectId; + serviceAccountId: Types.ObjectId, + workspaceId?: Types.ObjectId; + environment?: string; +}) => { + + // TODO: as we upgrade user permissions to be more global, then we should take into account + // the user's permissions as it concerns to being able to interact with service accounts + + if (!PERMISSION_SA_SET.has(name)) throw new Error(`${name} is not a valid permission name`); + + if ([ + PERMISSION_SA_WORKSPACE_READ, + PERMISSION_SA_WORKSPACE_WRITE + ].includes(name)) { + if (workspaceId && environment) { + // case: either workspace id [workspaceId] or environment name [environment] is being passed in + // (i.e. validating a service account permission concerning a workspace and/or environment) + const workspace = await Workspace.findById(workspaceId); + + if (!workspace) { + // case: workspace does not exist + throw WorkspaceNotFoundError({ message: 'Failed to locate workspace' }); + } + + if (!workspace.environments.some((env) => env.slug === environment)) { + // case: environment name [environment] is not a valid environment slug in workspace + throw Error('Failed to locate environment in workspace'); + } + + const serviceAccount = await ServiceAccount.findById(serviceAccountId); + if (!serviceAccount) { + // case: service account does not exist + throw ServiceAccountNotFoundError({ message: 'Failed to locate service account' }); + } + + const serviceAccountKey = await ServiceAccountKey.findOne({ + serviceAccount: serviceAccount._id, + workspace: workspaceId + }); + + if (!serviceAccountKey) { + // case: service account key does not exist + throw ServiceAccountKeyNotFoundError({ message: 'Failed to locate service account key' }); + } + } else { + throw new Error('Failed to validate workspace and environment for workspace-related permission'); + } + } + +} + +const validateDeleteServiceAccountPermission = async ({ + userId, + serviceAccountId, + name, + workspaceId, + environment +}: { + userId: Types.ObjectId; + serviceAccountId: Types.ObjectId; + name: string; + workspaceId: Types.ObjectId; + environment: string; +}) => { + // does the user have the authority to delete the permission? + // does the service account permission exist? + + +} + +export { + validateCreateServiceAccountPermission +} \ No newline at end of file diff --git a/backend/src/interfaces/serviceAccounts/dto/AddServiceAccountPermissionDto.ts b/backend/src/interfaces/serviceAccounts/dto/AddServiceAccountPermissionDto.ts new file mode 100644 index 000000000..6d0954c00 --- /dev/null +++ b/backend/src/interfaces/serviceAccounts/dto/AddServiceAccountPermissionDto.ts @@ -0,0 +1,7 @@ +interface AddServiceAccountPermissionDto { + name: string; + workspaceId?: string; + environment?: string; +} + +export default AddServiceAccountPermissionDto; \ No newline at end of file diff --git a/backend/src/interfaces/serviceAccounts/dto/index.ts b/backend/src/interfaces/serviceAccounts/dto/index.ts index 916a198a6..52d8d8342 100644 --- a/backend/src/interfaces/serviceAccounts/dto/index.ts +++ b/backend/src/interfaces/serviceAccounts/dto/index.ts @@ -1,5 +1,7 @@ import CreateServiceAccountDto from './CreateServiceAccountDto'; +import AddServiceAccountPermissionDto from './AddServiceAccountPermissionDto'; export { - CreateServiceAccountDto + CreateServiceAccountDto, + AddServiceAccountPermissionDto } \ No newline at end of file diff --git a/backend/src/middleware/requireMembershipOrgAuth.ts b/backend/src/middleware/requireMembershipOrgAuth.ts index ec1a4d4f1..ea9ed9afc 100644 --- a/backend/src/middleware/requireMembershipOrgAuth.ts +++ b/backend/src/middleware/requireMembershipOrgAuth.ts @@ -3,7 +3,7 @@ import { UnauthorizedRequestError } from '../utils/errors'; import { MembershipOrg } from '../models'; -import { validateMembership } from '../helpers/membershipOrg'; +import { validateMembershipOrg } from '../helpers/membershipOrg'; type req = 'params' | 'body' | 'query'; @@ -17,9 +17,11 @@ type req = 'params' | 'body' | 'query'; */ const requireMembershipOrgAuth = ({ acceptedRoles, + acceptedStatuses, location = 'params' }: { acceptedRoles: string[]; + acceptedStatuses: string[]; location?: req; }) => { return async (req: Request, res: Response, next: NextFunction) => { @@ -29,14 +31,13 @@ const requireMembershipOrgAuth = ({ if (!membershipOrg) throw new Error('Failed to find target organization membership'); - const targetMembership = await validateMembership({ - userId: req.user._id.toString(), - organizationId: membershipOrg.organization.toString(), - acceptedRoles + req.targetMembership = await validateMembershipOrg({ + userId: req.user._id, + organizationId: membershipOrg.organization, + acceptedRoles, + acceptedStatuses }); - req.targetMembership = targetMembership; - return next(); } catch (err) { return next(UnauthorizedRequestError({ diff --git a/backend/src/middleware/requireOrganizationAuth.ts b/backend/src/middleware/requireOrganizationAuth.ts index d001d03fe..5768b4b36 100644 --- a/backend/src/middleware/requireOrganizationAuth.ts +++ b/backend/src/middleware/requireOrganizationAuth.ts @@ -1,6 +1,8 @@ import { Request, Response, NextFunction } from 'express'; +import { Types } from 'mongoose'; import { IOrganization, MembershipOrg } from '../models'; import { UnauthorizedRequestError, ValidationError } from '../utils/errors'; +import { validateMembershipOrg } from '../helpers/membershipOrg'; type req = 'params' | 'body' | 'query'; @@ -9,7 +11,7 @@ type req = 'params' | 'body' | 'query'; * on request params. * @param {Object} obj * @param {String[]} obj.acceptedRoles - accepted organization roles - * @param {String[]} obj.acceptedStatuses - accepted organization statuses + * @param {String[]} obj.accepteStatuses - accepted organization statuses */ const requireOrganizationAuth = ({ acceptedRoles, @@ -21,30 +23,13 @@ const requireOrganizationAuth = ({ location?: req; }) => { return async (req: Request, res: Response, next: NextFunction) => { - // organization authorization middleware - const { organizationId } = req[location]; - - // validate organization membership - const membershipOrg = await MembershipOrg.findOne({ - user: req.user._id, - organization: organizationId - }).populate<{ organization: IOrganization }>('organization'); - - - if (!membershipOrg) { - return next(UnauthorizedRequestError({message: "You're not a member of this Organization."})) - } - //TODO is this important to validate? I mean is it possible to save wrong role to database or get wrong role from databse? - Zamion101 - if (!acceptedRoles.includes(membershipOrg.role)) { - return next(ValidationError({message: 'Failed to validate Organization Membership Role'})) - } - - if (!acceptedStatuses.includes(membershipOrg.status)) { - return next(ValidationError({message: 'Failed to validate Organization Membership Status'})) - } - - req.membershipOrg = membershipOrg; + req.membershipOrg = await validateMembershipOrg({ + userId: req.user._id, + organizationId: new Types.ObjectId(organizationId), + acceptedRoles, + acceptedStatuses + }); return next(); }; diff --git a/backend/src/middleware/requireServiceAccountAuth.ts b/backend/src/middleware/requireServiceAccountAuth.ts index bc2cf2d12..f5bdb8f3f 100644 --- a/backend/src/middleware/requireServiceAccountAuth.ts +++ b/backend/src/middleware/requireServiceAccountAuth.ts @@ -1,9 +1,11 @@ import { Request, Response, NextFunction } from 'express'; import { ServiceAccount } from '../models'; import { - AccountNotFoundError, - UnauthorizedRequestError + ServiceAccountNotFoundError } from '../utils/errors'; +import { + validateMembershipOrg +} from '../helpers/membershipOrg'; type req = 'params' | 'body' | 'query'; @@ -20,14 +22,19 @@ const requireServiceAccountAuth = ({ const serviceAccountId = req[location].serviceAccountId; const serviceAccount = await ServiceAccount.findById(serviceAccountId); - // TODO: acceptedRoles and acceptedStatuses - if (!serviceAccount) { - return next(AccountNotFoundError({ message: 'Failed to locate Service Account' })); + return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account' })); } if (serviceAccount.user.toString() !== req.user.id.toString()) { - return next(UnauthorizedRequestError({ message: 'Failed to authenticate the Service Account' })); + // case: creator of the service account is different from + // the user on the request -> apply middleware role/status validation + await validateMembershipOrg({ + userId: req.user._id, + organizationId: serviceAccount.organization, + acceptedRoles, + acceptedStatuses + }); } req.serviceAccount = serviceAccount; diff --git a/backend/src/models/serviceAccountPermission.ts b/backend/src/models/serviceAccountPermission.ts index e51ef4df4..2199557da 100644 --- a/backend/src/models/serviceAccountPermission.ts +++ b/backend/src/models/serviceAccountPermission.ts @@ -22,9 +22,11 @@ const serviceAccountPermissionSchema = new Schema( workspace: { type: Schema.Types.ObjectId, ref: 'Workspace', + default: null }, environment: { - type: 'String' + type: 'String', + default: null } }, { diff --git a/backend/src/routes/v2/organizations.ts b/backend/src/routes/v2/organizations.ts index a99de1ec9..21e2f9cf1 100644 --- a/backend/src/routes/v2/organizations.ts +++ b/backend/src/routes/v2/organizations.ts @@ -40,7 +40,8 @@ router.patch( acceptedStatuses: [ACCEPTED] }), requireMembershipOrgAuth({ - acceptedRoles: [OWNER, ADMIN] + acceptedRoles: [OWNER, ADMIN], + acceptedStatuses: [ACCEPTED] }), organizationsController.updateOrganizationMembership ); @@ -58,7 +59,8 @@ router.delete( acceptedStatuses: [ACCEPTED] }), requireMembershipOrgAuth({ - acceptedRoles: [OWNER, ADMIN] + acceptedRoles: [OWNER, ADMIN], + acceptedStatuses: [ACCEPTED] }), organizationsController.deleteOrganizationMembership ); diff --git a/backend/src/routes/v2/serviceAccounts.ts b/backend/src/routes/v2/serviceAccounts.ts index 901fae5f4..73461196d 100644 --- a/backend/src/routes/v2/serviceAccounts.ts +++ b/backend/src/routes/v2/serviceAccounts.ts @@ -2,14 +2,17 @@ import express from 'express'; const router = express.Router(); import { requireOrganizationAuth, - requireServiceAccountAuth + requireWorkspaceAuth, + requireServiceAccountAuth, + validateRequest } from '../../middleware'; import { body } from 'express-validator'; import { OWNER, ADMIN, MEMBER, - ACCEPTED + ACCEPTED, + PERMISSION_SA_SET } from '../../variables'; import { serviceAccountsController } from '../../controllers/v2'; @@ -27,6 +30,19 @@ router.post( serviceAccountsController.createServiceAccount ); +router.post( + '/serviceAccountId/:serviceAccountId/permissions', + body('name').exists().isString().trim().custom((value) => PERMISSION_SA_SET.has(value)), + body('workspaceId').optional().isMongoId(), + body('environment').optional(), + validateRequest, + requireServiceAccountAuth({ + acceptedRoles: [OWNER, ADMIN], + acceptedStatuses: [ACCEPTED] + }), + serviceAccountsController.addServiceAccountPermission +); + // router.post( // '/:serviceAccountId/key', // body('workspaceId').exists().isString().trim(), @@ -42,7 +58,7 @@ router.post( router.delete( '/:serviceAccountId/key/:serviceAccountKeyId', requireServiceAccountAuth({ - acceptedRoles: [OWNER, ADMIN, MEMBER], + acceptedRoles: [OWNER, ADMIN], acceptedStatuses: [ACCEPTED] }), async (req, res) => { @@ -50,4 +66,17 @@ router.delete( } ); +// TODO: create service account permission +// router.post( + +// ); + +// TODO: delete service account permission + +router.delete( + '/:serviceAccountId/service-account-permission/:serviceAccountPermissionId', + +) + + export default router; \ No newline at end of file diff --git a/backend/src/utils/errors.ts b/backend/src/utils/errors.ts index e9972edf5..4799728b9 100644 --- a/backend/src/utils/errors.ts +++ b/backend/src/utils/errors.ts @@ -93,6 +93,16 @@ export const WorkspaceNotFoundError = (error?: Partial) => stack: error?.stack }); +//* ----->[WORKSPACE MEMBERSHIP ERRORS]<----- +export const MembershipNotFoundError = (error?: Partial) => new RequestError({ + logLevel: error?.logLevel ?? LogLevel.ERROR, + statusCode: error?.statusCode ?? 404, + type: error?.type ?? 'workspace_membership_not_found_error', + message: error?.message ?? 'The requested membership was not found', + context: error?.context, + stack: error?.stack +}); + //* ----->[ORGANIZATION ERRORS]<----- export const OrganizationNotFoundError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.ERROR, @@ -103,6 +113,16 @@ export const OrganizationNotFoundError = (error?: Partial) stack: error?.stack }); +//* ----->[MEMBERSHIP ORGANIZATION ERRORS]<----- +export const MembershipOrgNotFoundError = (error?: Partial) => new RequestError({ + logLevel: error?.logLevel ?? LogLevel.ERROR, + statusCode: error?.statusCode ?? 404, + type: error?.type ?? 'organization_membership_not_found_error', + message: error?.message ?? 'The requested organization membership was not found', + context: error?.context, + stack: error?.stack +}); + //* ----->[ACCOUNT ERRORS]<----- export const AccountNotFoundError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.ERROR, @@ -157,10 +177,29 @@ export const ServiceTokenDataNotFoundError = (error?: Partial) => new RequestError({ logLevel: error?.logLevel ?? LogLevel.ERROR, statusCode: error?.statusCode ?? 404, - type: error?.type ?? 'service_token_data_not_found_error', + type: error?.type ?? 'api_key_data_not_found_error', message: error?.message ?? 'The requested service token data was not found', context: error?.context, stack: error?.stack +}); + +//* ----->[SERVICE_ACCOUNT ERRORS]<----- +export const ServiceAccountNotFoundError = (error?: Partial) => new RequestError({ + logLevel: error?.logLevel ?? LogLevel.ERROR, + statusCode: error?.statusCode ?? 404, + type: error?.type ?? 'service_account_not_found_error', + message: error?.message ?? 'The requested service account was not found', + context: error?.context, + stack: error?.stack +}); + +export const ServiceAccountKeyNotFoundError = (error?: Partial) => new RequestError({ + logLevel: error?.logLevel ?? LogLevel.ERROR, + statusCode: error?.statusCode ?? 404, + type: error?.type ?? 'service_account_key_not_found_error', + message: error?.message ?? 'The requested service account key was not found', + context: error?.context, + stack: error?.stack }) //* ----->[MISC ERRORS]<----- diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts index b71044cba..64eceb786 100644 --- a/backend/src/variables/index.ts +++ b/backend/src/variables/index.ts @@ -63,6 +63,12 @@ import { TOKEN_EMAIL_ORG_INVITATION, TOKEN_EMAIL_PASSWORD_RESET } from './token'; +import { + PERMISSION_SA_WORKSPACE_READ, + PERMISSION_SA_WORKSPACE_WRITE, + PERMISSION_SA_WORKSPACE_SET, + PERMISSION_SA_SET +} from './permissions'; export { OWNER, @@ -124,5 +130,9 @@ export { TOKEN_EMAIL_CONFIRMATION, TOKEN_EMAIL_MFA, TOKEN_EMAIL_ORG_INVITATION, - TOKEN_EMAIL_PASSWORD_RESET + TOKEN_EMAIL_PASSWORD_RESET, + PERMISSION_SA_WORKSPACE_READ, + PERMISSION_SA_WORKSPACE_WRITE, + PERMISSION_SA_WORKSPACE_SET, + PERMISSION_SA_SET }; diff --git a/backend/src/variables/permissions.ts b/backend/src/variables/permissions.ts new file mode 100644 index 000000000..f553c88e3 --- /dev/null +++ b/backend/src/variables/permissions.ts @@ -0,0 +1,19 @@ +const PERMISSION_SA_WORKSPACE_READ = 'read'; +const PERMISSION_SA_WORKSPACE_WRITE = 'write'; + +const PERMISSION_SA_WORKSPACE_SET = new Set([ + PERMISSION_SA_WORKSPACE_READ, + PERMISSION_SA_WORKSPACE_WRITE +]); + +const PERMISSION_SA_SET = new Set([ + PERMISSION_SA_WORKSPACE_READ, + PERMISSION_SA_WORKSPACE_WRITE +]); + +export { + PERMISSION_SA_WORKSPACE_READ, + PERMISSION_SA_WORKSPACE_WRITE, + PERMISSION_SA_WORKSPACE_SET, + PERMISSION_SA_SET +} \ No newline at end of file