Begin refactoring middleware for service accounts

This commit is contained in:
Tuan Dang
2023-04-04 11:08:03 +03:00
parent c797901778
commit aa53de9070
39 changed files with 943 additions and 577 deletions
+31 -28
View File
@@ -10,6 +10,17 @@ import {
import {
UnauthorizedRequestError
} from '../utils/errors';
import {
IUser,
IServiceAccount,
IServiceTokenData
} from '../models';
import {
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_API_KEY
} from '../variables';
declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -28,59 +39,51 @@ declare module 'jsonwebtoken' {
* @returns
*/
const requireAuth = ({
acceptedAuthModes = ['jwt'],
requiredServiceTokenPermissions = []
acceptedAuthModes = [AUTH_MODE_JWT],
}: {
acceptedAuthModes: string[];
requiredServiceTokenPermissions?: string[];
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
// validate auth token against accepted auth modes [acceptedAuthModes]
// and return token type [authTokenType] and value [authTokenValue]
const { authTokenType, authTokenValue } = validateAuthMode({
const { authMode, authTokenValue } = validateAuthMode({
headers: req.headers,
acceptedAuthModes
});
req.authTokenType = authTokenType;
// attach auth payloads
let serviceTokenData: any;
switch (authTokenType) {
case 'serviceAccount':
req.serviceAccount = await getAuthSAAKPayload({
let authPayload: IUser | IServiceAccount | IServiceTokenData;
switch (authMode) {
case AUTH_MODE_SERVICE_ACCOUNT:
authPayload = await getAuthSAAKPayload({
authTokenValue
});
req.serviceAccount = authPayload;
break;
case 'serviceToken':
serviceTokenData = await getAuthSTDPayload({
case AUTH_MODE_SERVICE_TOKEN:
authPayload = await getAuthSTDPayload({
authTokenValue
});
// TODO: bring this into a separate collection
requiredServiceTokenPermissions.forEach((requiredServiceTokenPermission) => {
if (!serviceTokenData.permissions.includes(requiredServiceTokenPermission)) {
return next(UnauthorizedRequestError({ message: 'Failed to authorize service token for endpoint' }));
}
});
req.serviceTokenData = serviceTokenData;
req.user = serviceTokenData?.user;
req.serviceTokenData = authPayload;
break;
case 'apiKey':
// TODO: deprecate API key
req.user = await getAuthAPIKeyPayload({
case AUTH_MODE_API_KEY:
authPayload = await getAuthAPIKeyPayload({
authTokenValue
});
req.user = authPayload;
break;
default:
req.user = await getAuthUserPayload({
authPayload = await getAuthUserPayload({
authTokenValue
});
req.user = authPayload;
break;
}
req.authData = {
authMode,
authPayload
}
return next();
}
}
+21 -35
View File
@@ -1,48 +1,34 @@
import { Request, Response, NextFunction } from 'express';
import { UnauthorizedRequestError } from '../utils/errors';
import { Secret, Membership } from '../models';
import { validateSecrets } from '../helpers/secret';
// TODO: make this work for delete route
import { validateClientForSecrets } from '../helpers/secrets';
const requireSecretsAuth = ({
acceptedRoles
acceptedRoles,
requiredPermissions = []
}: {
acceptedRoles: string[];
requiredPermissions?: string[];
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
let secrets;
try {
if (Array.isArray(req.body.secrets)) {
// case: validate multiple secrets
secrets = await validateSecrets({
userId: req.user._id.toString(),
secretIds: req.body.secrets.map((s: any) => s.id)
});
} else if (typeof req.body.secrets === 'object') { // change this to check for object
// case: validate 1 secret
secrets = await validateSecrets({
userId: req.user._id.toString(),
secretIds: [req.body.secrets.id]
});
} else if (Array.isArray(req.body.secretIds)) {
secrets = await validateSecrets({
userId: req.user._id.toString(),
secretIds: req.body.secretIds
});
} else if (typeof req.body.secretIds === 'string') {
// case: validate secretIds
secrets = await validateSecrets({
userId: req.user._id.toString(),
secretIds: [req.body.secretIds]
});
}
req.secrets = secrets;
return next();
} catch (err) {
return next(UnauthorizedRequestError({ message: 'Unable to authenticate secret(s)' }));
let secretIds = [];
if (Array.isArray(req.body.secrets)) {
secretIds = req.body.secrets.map((s: any) => s.id);
} else if (typeof req.body.secrets === 'object') {
secretIds = [req.body.secrets.id];
} else if (Array.isArray(req.body.secretIds)) {
secretIds = req.body.secretIds;
} else if (typeof req.body.secretIds === 'string') {
secretIds = [req.body.secretIds];
}
req.secrets = await validateClientForSecrets({
authData: req.authData,
secretIds: [req.body.secretIds],
requiredPermissions
});
return next();
}
}
+18 -30
View File
@@ -16,44 +16,32 @@ type req = 'params' | 'body' | 'query';
const requireWorkspaceAuth = ({
acceptedRoles,
locationWorkspaceId,
locationEnvironment = undefined
locationEnvironment = undefined,
requiredPermissions = []
}: {
acceptedRoles: string[];
locationWorkspaceId: req;
locationEnvironment?: req | undefined;
requiredPermissions?: string[];
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
try {
// TODO: throw errors if workspaceId or environemnt are not present
const workspaceId = req[locationWorkspaceId]?.workspaceId;
const environment = locationEnvironment ? req[locationEnvironment]?.environment : undefined;
// validate clients
const { membership } = await validateClientForWorkspace({
userId: req.user?._id,
serviceAccountId: req.serviceAccount?._id,
serviceTokenDataId: req.serviceTokenData?._id,
workspaceId: new Types.ObjectId(workspaceId),
environment
});
if (membership) {
req.membership = membership;
}
if (
req.serviceTokenData
&& req.serviceTokenData.workspace.toString() !== workspaceId
&& req.serviceTokenData.environment !== req.body.environment
) {
next(UnauthorizedRequestError({message: 'Unable to authenticate workspace'}))
}
return next();
} catch (err) {
return next(UnauthorizedRequestError({message: 'Unable to authenticate workspace'}))
const workspaceId = req[locationWorkspaceId]?.workspaceId;
const environment = locationEnvironment ? req[locationEnvironment]?.environment : undefined;
// validate clients
const { membership } = await validateClientForWorkspace({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId),
environment,
requiredPermissions
});
if (membership) {
req.membership = membership;
}
return next();
};
};