Finish preliminary v2 audit logs

This commit is contained in:
Tuan Dang
2023-08-05 16:55:06 +07:00
parent 6fbcbc4807
commit 142fcf0a01
100 changed files with 1885 additions and 1269 deletions

View File

@@ -15,20 +15,20 @@ import { checkUserDevice } from "../../helpers/user";
import {
ACTION_LOGIN,
ACTION_LOGOUT,
AUTH_MODE_JWT,
} from "../../variables";
import {
BadRequestError,
UnauthorizedRequestError,
} from "../../utils/errors";
import { EELogService } from "../../ee/services";
import { getChannelFromUserAgent } from "../../utils/posthog";
import { getUserAgentType } from "../../utils/posthog";
import {
getHttpsEnabled,
getJwtAuthLifetime,
getJwtAuthSecret,
getJwtRefreshSecret,
} from "../../config";
import { ActorType } from "../../ee/models";
declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -142,7 +142,7 @@ export const login2 = async (req: Request, res: Response) => {
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.realIP,
});
@@ -170,7 +170,7 @@ export const login2 = async (req: Request, res: Response) => {
* @returns
*/
export const logout = async (req: Request, res: Response) => {
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User && req.authData.tokenVersionId) {
if (req.authData.actor.type === ActorType.USER && req.authData.tokenVersionId) {
await clearTokens(req.authData.tokenVersionId)
}
@@ -190,7 +190,7 @@ export const logout = async (req: Request, res: Response) => {
logoutAction && await EELogService.createLog({
userId: req.user._id,
actions: [logoutAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.realIP,
});

View File

@@ -103,14 +103,14 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
// validate membership
const membershipOrg = await MembershipOrg.findOne({
user: req.user._id,
organization: organizationId
organization: new Types.ObjectId(organizationId)
});
if (!membershipOrg) {
throw new Error("Failed to validate organization membership");
}
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
const ssoConfig = await SSOConfig.findOne({
organization: new Types.ObjectId(organizationId)

View File

@@ -5,7 +5,7 @@ import * as bigintConversion from "bigint-conversion";
import { BackupPrivateKey, LoginSRPDetail, User } from "../../models";
import { clearTokens, createToken, sendMail } from "../../helpers";
import { TokenService } from "../../services";
import { AUTH_MODE_JWT, TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
import { TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
import { BadRequestError } from "../../utils/errors";
import {
getHttpsEnabled,
@@ -13,6 +13,7 @@ import {
getJwtSignupSecret,
getSiteURL
} from "../../config";
import { ActorType } from "../../ee/models";
/**
* Password reset step 1: Send email verification link to email [email]
@@ -208,8 +209,7 @@ export const changePassword = async (req: Request, res: Response) => {
);
if (
req.authData.authMode === AUTH_MODE_JWT &&
req.authData.authPayload instanceof User &&
req.authData.actor.type === ActorType.USER &&
req.authData.tokenVersionId
) {
await clearTokens(req.authData.tokenVersionId);

View File

@@ -1,3 +1,4 @@
import { Types } from "mongoose";
import { Request, Response } from "express";
import {
IUser,
@@ -108,14 +109,14 @@ export const createWorkspace = async (req: Request, res: Response) => {
// validate organization membership
const membershipOrg = await MembershipOrg.findOne({
user: req.user._id,
organization: organizationId,
organization: new Types.ObjectId(organizationId),
});
if (!membershipOrg) {
throw new Error("Failed to validate organization membership");
}
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (plan.workspaceLimit !== null) {
// case: limit imposed on number of workspaces allowed
@@ -134,7 +135,7 @@ export const createWorkspace = async (req: Request, res: Response) => {
// create workspace and add user as member
const workspace = await create({
name: workspaceName,
organizationId,
organizationId: new Types.ObjectId(organizationId),
});
await addMemberships({

View File

@@ -14,7 +14,7 @@ import {
ACTION_LOGIN,
TOKEN_EMAIL_MFA,
} from "../../variables";
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
import {
getHttpsEnabled,
getJwtMfaLifetime,
@@ -203,7 +203,7 @@ export const login2 = async (req: Request, res: Response) => {
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.ip,
});
@@ -336,7 +336,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.realIP,
});

View File

@@ -1,4 +1,5 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import {
Integration,
Membership,
@@ -30,7 +31,7 @@ export const createWorkspaceEnvironment = async (
if (!workspace) throw WorkspaceNotFoundError();
const plan = await EELicenseService.getPlan(workspace.organization.toString());
const plan = await EELicenseService.getPlan(workspace.organization);
if (plan.environmentLimit !== null) {
// case: limit imposed on number of environments allowed
@@ -58,7 +59,7 @@ export const createWorkspaceEnvironment = async (
});
await workspace.save();
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId));
return res.status(200).send({
message: "Successfully created new environment",
@@ -215,7 +216,7 @@ export const deleteWorkspaceEnvironment = async (
{ $pull: { deniedPermissions: { environmentSlug: environmentSlug } } }
);
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId));
return res.status(200).send({
message: "Successfully deleted environment",

View File

@@ -16,7 +16,7 @@ import { EventService } from "../../services";
import { eventPushSecrets } from "../../events";
import { EELogService, EESecretService } from "../../ee/services";
import { SecretService, TelemetryService } from "../../services";
import { getChannelFromUserAgent } from "../../utils/posthog";
import { getUserAgentType } from "../../utils/posthog";
import { PERMISSION_WRITE_SECRETS } from "../../variables";
import {
userHasNoAbility,
@@ -44,7 +44,7 @@ import { getAllImportedSecrets } from "../../services/SecretImportService";
* @param res
*/
export const batchSecrets = async (req: Request, res: Response) => {
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const postHogClient = await TelemetryService.getPostHogClient();
const {
@@ -416,7 +416,7 @@ export const createSecrets = async (req: Request, res: Response) => {
}
*/
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const {
workspaceId,
environment,
@@ -834,7 +834,7 @@ export const getSecrets = async (req: Request, res: Response) => {
importedSecrets = await getAllImportedSecrets(workspaceId, environment, folderId as string);
}
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const readAction = await EELogService.createAction({
name: ACTION_READ_SECRETS,
@@ -1170,7 +1170,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
}
*/
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const toDelete = req.secrets.map((s: any) => s._id);
await Secret.deleteMany({

View File

@@ -1,10 +1,10 @@
import { Request, Response } from "express";
import crypto from "crypto";
import bcrypt from "bcrypt";
import { ServiceAccount, ServiceTokenData, User } from "../../models";
import { AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT } from "../../variables";
import { ServiceTokenData } from "../../models";
import { getSaltRounds } from "../../config";
import { BadRequestError } from "../../utils/errors";
import { ActorType } from "../../ee/models";
/**
* Return service token data associated with service token on request
@@ -73,24 +73,16 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
}
let user, serviceAccount;
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User) {
let user;
if (req.authData.actor.type === ActorType.USER) {
user = req.authData.authPayload._id;
}
if (
req.authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
req.authData.authPayload instanceof ServiceAccount
) {
serviceAccount = req.authData.authPayload._id;
}
serviceTokenData = await new ServiceTokenData({
name,
workspace: workspaceId,
user,
serviceAccount,
scopes,
lastUsed: new Date(),
expiresAt,

View File

@@ -15,7 +15,7 @@ import {
ACTION_LOGIN,
TOKEN_EMAIL_MFA,
} from "../../variables";
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
import {
getHttpsEnabled,
getJwtMfaLifetime,
@@ -241,7 +241,7 @@ export const login2 = async (req: Request, res: Response) => {
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.realIP,
});

View File

@@ -1,3 +1,4 @@
import { Types } from "mongoose";
import { Request, Response } from "express";
import { getLicenseServerUrl } from "../../../config";
import { licenseServerKeyRequest } from "../../../config/request";
@@ -20,7 +21,7 @@ export const getOrganizationPlan = async (req: Request, res: Response) => {
const { organizationId } = req.params;
const workspaceId = req.query.workspaceId as string;
const plan = await EELicenseService.getPlan(organizationId, workspaceId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId), new Types.ObjectId(workspaceId));
return res.status(200).send({
plan,
@@ -44,7 +45,7 @@ export const startOrganizationTrial = async (req: Request, res: Response) => {
}
);
EELicenseService.delPlan(organizationId);
EELicenseService.delPlan(new Types.ObjectId(organizationId));
return res.status(200).send({
url

View File

@@ -59,7 +59,7 @@ export const updateSSOConfig = async (req: Request, res: Response) => {
cert,
} = req.body;
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO) return res.status(400).send({
message: "Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
@@ -194,7 +194,7 @@ export const createSSOConfig = async (req: Request, res: Response) => {
cert
} = req.body;
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO) return res.status(400).send({
message: "Failed to create SAML SSO configuration due to plan restriction. Upgrade plan to add SSO configuration."

View File

@@ -8,6 +8,6 @@ import { Request, Response } from "express";
*/
export const getMyIp = (req: Request, res: Response) => {
return res.status(200).send({
ip: req.authData.authIP
ip: req.authData.ipAddress
});
}

View File

@@ -1,6 +1,6 @@
import { Request, Response } from "express";
import { PipelineStage, Types } from "mongoose";
import { Secret } from "../../../models";
import { Secret, Membership, User, ServiceTokenData } from "../../../models";
import {
FolderVersion,
IPType,
@@ -9,7 +9,12 @@ import {
SecretSnapshot,
SecretVersion,
TFolderRootVersionSchema,
TrustedIP
TrustedIP,
AuditLog,
Actor,
ActorType,
UserActor,
ServiceActor
} from "../../models";
import { EESecretService } from "../../services";
import { getLatestSecretVersionIds } from "../../helpers/secretVersion";
@@ -593,6 +598,83 @@ export const getWorkspaceLogs = async (req: Request, res: Response) => {
});
};
/**
* Return trusted ips for workspace with id [workspaceId]
* @param req
* @param res
*/
export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const eventType = req.query.eventType;
const userAgentType = req.query.userAgentType;
const actor = req.query.actor as string | undefined;
const auditLogs = await AuditLog.find({
workspace: new Types.ObjectId(workspaceId),
...(eventType ? {
"event.type": eventType
} : {}),
...(userAgentType ? {
userAgentType
} : {}),
...(actor ? {
"actor.type": actor.split("-", 2)[0],
...(actor.split("-", 2)[0] === ActorType.USER ? {
"actor.metadata.userId": actor.split("-", 2)[1]
} : {
"actor.metadata.serviceId": actor.split("-", 2)[1]
})
} : {})
})
.sort({ createdAt: -1 });
return res.status(200).send({
auditLogs
});
}
/**
* Return trusted ips for workspace with id [workspaceId]
* @param req
* @param res
*/
export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const userIds = await Membership.distinct("user", {
workspace: new Types.ObjectId(workspaceId)
});
const userActors: UserActor[] = (await User.find({
_id: {
$in: userIds
}
})
.select("email"))
.map((user) => ({
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
}));
const serviceActors: ServiceActor[] = (await ServiceTokenData.find({
workspace: new Types.ObjectId(workspaceId)
})
.select("name"))
.map((serviceTokenData) => ({
type: ActorType.SERVICE,
metadata: {
serviceId: serviceTokenData._id.toString(),
name: serviceTokenData.name
}
}));
return res.status(200).send({
actors: [...userActors, ...serviceActors]
});
}
/**
* Return trusted ips for workspace with id [workspaceId]
* @param req
@@ -623,7 +705,7 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
isActive
} = req.body;
const plan = await EELicenseService.getPlan(req.workspace.organization.toString());
const plan = await EELicenseService.getPlan(req.workspace.organization);
if (!plan.ipAllowlisting) return res.status(400).send({
message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range."
@@ -663,7 +745,7 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
comment
} = req.body;
const plan = await EELicenseService.getPlan(req.workspace.organization.toString());
const plan = await EELicenseService.getPlan(req.workspace.organization);
if (!plan.ipAllowlisting) return res.status(400).send({
message: "Failed to update IP access range due to plan restriction. Upgrade plan to update IP access range."
@@ -721,7 +803,7 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => {
const { workspaceId, trustedIpId } = req.params;
const plan = await EELicenseService.getPlan(req.workspace.organization.toString());
const plan = await EELicenseService.getPlan(req.workspace.organization);
if (!plan.ipAllowlisting) return res.status(400).send({
message: "Failed to delete IP access range due to plan restriction. Upgrade plan to delete IP access range."

View File

@@ -0,0 +1,76 @@
import { Schema, Types, model } from "mongoose";
import {
ActorType,
EventType,
UserAgentType
} from "./enums";
import {
Actor,
Event
} from "./types";
export interface IAuditLog {
actor: Actor;
organization: Types.ObjectId;
workspace: Types.ObjectId;
ipAddress: string;
event: Event;
userAgent: string;
userAgentType: UserAgentType;
expiresAt: Date;
}
const auditLogSchema = new Schema<IAuditLog>(
{
actor: {
type: {
type: String,
enum: ActorType,
required: true
},
metadata: {
type: Schema.Types.Mixed
}
},
organization: {
type: Schema.Types.ObjectId,
required: false
},
workspace: {
type: Schema.Types.ObjectId,
required: false
},
ipAddress: {
type: String,
required: true
},
event: {
type: {
type: String,
enum: EventType,
required: true
},
metadata: {
type: Schema.Types.Mixed
}
},
userAgent: {
type: String,
required: true
},
userAgentType: {
type: String,
enum: UserAgentType,
required: true
},
expiresAt: {
type: Date,
expires: 0
}
},
{
timestamps: true
}
);
export const AuditLog = model<IAuditLog>("AuditLog", auditLogSchema);

View File

@@ -0,0 +1,20 @@
export enum ActorType {
USER = "user",
SERVICE = "service"
}
export enum UserAgentType {
WEB = "web",
CLI = "cli",
K8_OPERATOR = "k8-operator",
OTHER = "other"
}
export enum EventType {
GET_SECRETS = "get-secrets",
GET_SECRET = "get-secret",
REVEAL_SECRET = "reveal-secret",
CREATE_SECRET = "create-secret",
UPDATE_SECRET = "update-secret",
DELETE_SECRET = "delete-secret"
}

View File

@@ -0,0 +1,3 @@
export * from "./auditLog";
export * from "./enums";
export * from "./types";

View File

@@ -0,0 +1,88 @@
import {
ActorType,
EventType
} from "./enums";
interface UserActorMetadata {
userId: string;
email: string;
}
interface ServiceActorMetadata {
serviceId: string;
name: string;
}
export interface UserActor {
type: ActorType.USER;
metadata: UserActorMetadata;
}
export interface ServiceActor {
type: ActorType.SERVICE;
metadata: ServiceActorMetadata;
}
export type Actor =
| UserActor
| ServiceActor;
interface GetSecretsEvent {
type: EventType.GET_SECRETS;
metadata: {
environment: string;
secretPath: string;
numberOfSecrets: number;
};
}
interface GetSecretEvent {
type: EventType.GET_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
};
}
interface CreateSecretEvent {
type: EventType.CREATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
interface UpdateSecretEvent {
type: EventType.UPDATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
interface DeleteSecretEvent {
type: EventType.DELETE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
| CreateSecretEvent
| UpdateSecretEvent
| DeleteSecretEvent;

View File

@@ -4,4 +4,5 @@ export * from "./folderVersion";
export * from "./log";
export * from "./action";
export * from "./ssoConfig";
export * from "./trustedIp";
export * from "./trustedIp";
export * from "./auditLog";

View File

@@ -6,11 +6,12 @@ import {
} from "../../../middleware";
import { query } from "express-validator";
import { cloudProductsController } from "../../controllers/v1";
import { AuthMode } from "../../../variables";
router.get(
"/",
requireAuth({
acceptedAuthModes: ["jwt", "apiKey"],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
query("billing-cycle").exists().isIn(["monthly", "yearly"]),
validateRequest,

View File

@@ -8,13 +8,13 @@ import {
import { body, param, query } from "express-validator";
import { organizationsController } from "../../controllers/v1";
import {
ACCEPTED, ADMIN, MEMBER, OWNER,
ACCEPTED, ADMIN, MEMBER, OWNER, AuthMode
} from "../../../variables";
router.get(
"/:organizationId/plans/table",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -29,7 +29,7 @@ router.get(
router.get(
"/:organizationId/plan",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -44,7 +44,7 @@ router.get(
router.post(
"/:organizationId/session/trial",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -59,7 +59,7 @@ router.post(
router.get(
"/:organizationId/plan/billing",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -74,7 +74,7 @@ router.get(
router.get(
"/:organizationId/plan/table",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -89,7 +89,7 @@ router.get(
router.get(
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -103,7 +103,7 @@ router.get(
router.patch(
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -119,7 +119,7 @@ router.patch(
router.get(
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -133,7 +133,7 @@ router.get(
router.post(
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -149,7 +149,7 @@ router.post(
router.delete(
"/:organizationId/billing-details/payment-methods/:pmtMethodId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -164,7 +164,7 @@ router.delete(
router.get(
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -178,7 +178,7 @@ router.get(
router.post(
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -194,7 +194,7 @@ router.post(
router.delete(
"/:organizationId/billing-details/tax-ids/:taxId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -209,7 +209,7 @@ router.delete(
router.get(
"/:organizationId/invoices",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -223,7 +223,7 @@ router.get(
router.get(
"/:organizationId/licenses",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],

View File

@@ -12,12 +12,13 @@ import {
MEMBER,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
AuthMode
} from "../../../variables";
router.get(
"/:secretId/secret-versions",
requireAuth({
acceptedAuthModes: ["jwt", "apiKey"],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -33,7 +34,7 @@ router.get(
router.post(
"/:secretId/secret-versions/rollback",
requireAuth({
acceptedAuthModes: ["jwt", "apiKey"],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -8,13 +8,13 @@ import {
validateRequest,
} from "../../../middleware";
import { param } from "express-validator";
import { ADMIN, MEMBER } from "../../../variables";
import { ADMIN, MEMBER, AuthMode } from "../../../variables";
import { secretSnapshotController } from "../../controllers/v1";
router.get(
"/:secretSnapshotId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireSecretSnapshotAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -15,7 +15,8 @@ import { authLimiter } from "../../../helpers/rateLimiter";
import {
ACCEPTED,
ADMIN,
OWNER
OWNER,
AuthMode
} from "../../../variables";
router.get(
@@ -90,7 +91,7 @@ router.post("/saml2/:ssoIdentifier",
router.get(
"/config",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@@ -105,7 +106,7 @@ router.get(
router.post(
"/config",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@@ -125,7 +126,7 @@ router.post(
router.patch(
"/config",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],

View File

@@ -3,13 +3,13 @@ const router = express.Router();
import {
requireAuth
} from "../../../middleware";
import { AUTH_MODE_API_KEY, AUTH_MODE_JWT } from "../../../variables";
import { AuthMode } from "../../../variables";
import { usersController } from "../../controllers/v1";
router.get(
"/me/ip",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
usersController.getMyIp
);

View File

@@ -8,16 +8,16 @@ import {
import { body, param, query } from "express-validator";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER
MEMBER,
AuthMode
} from "../../../variables";
import { workspaceController } from "../../controllers/v1";
import { EventType, UserAgentType } from "../../models";
router.get(
"/:workspaceId/secret-snapshots",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -35,7 +35,7 @@ router.get(
router.get(
"/:workspaceId/secret-snapshots/count",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -51,7 +51,7 @@ router.get(
router.post(
"/:workspaceId/secret-snapshots/rollback",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -68,7 +68,7 @@ router.post(
router.get(
"/:workspaceId/logs",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -84,11 +84,42 @@ router.get(
workspaceController.getWorkspaceLogs
);
router.get(
"/:workspaceId/audit-logs",
requireAuth({
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: "params",
}),
param("workspaceId").exists().trim(),
query("eventType").isString().isIn(Object.values(EventType)).optional({ nullable: true }),
query("userAgentType").isString().isIn(Object.values(UserAgentType)).optional({ nullable: true }),
query("actor").isString().optional({ nullable: true }),
validateRequest,
workspaceController.getWorkspaceAuditLogs
);
router.get(
"/:workspaceId/audit-logs/filters/actors",
requireAuth({
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: "params",
}),
param("workspaceId").exists().trim(),
validateRequest,
workspaceController.getWorkspaceAuditLogActorFilterOpts
);
router.get(
"/:workspaceId/trusted-ips",
param("workspaceId").exists().isString().trim(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -105,7 +136,7 @@ router.post(
body("isActive").exists().isBoolean(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -122,7 +153,7 @@ router.patch(
body("comment").default("").isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -137,7 +168,7 @@ router.delete(
param("trustedIpId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],

View File

@@ -0,0 +1,46 @@
import { Types } from "mongoose";
import { AuditLog, Event } from "../models";
import { AuthData } from "../../interfaces/middleware";
import EELicenseService from "./EELicenseService";
import { Workspace } from "../../models";
import { OrganizationNotFoundError } from "../../utils/errors";
interface EventScope {
workspaceId?: Types.ObjectId;
organizationId?: Types.ObjectId;
}
type ValidEventScope =
| Required<Pick<EventScope, 'workspaceId'>>
| Required<Pick<EventScope, 'organizationId'>>
| Required<EventScope>
export default class EEAuditLogService {
static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope) {
const MS_IN_DAY = 24 * 60 * 60 * 1000;
const organizationId = ("organizationId" in eventScope)
? eventScope.organizationId
: (await Workspace.findById(eventScope.workspaceId).select("organization").lean())?.organization;
if (!organizationId) throw OrganizationNotFoundError({
message: "createAuditLog: Failed to create audit log due to missing organizationId"
});
const ttl = (await EELicenseService.getPlan(organizationId)).auditLogsRetentionDays * MS_IN_DAY;
const auditLog = await new AuditLog({
actor: authData.actor,
organization: organizationId,
workspace: ("workspaceId" in eventScope) ? eventScope.workspaceId : undefined,
ipAddress: authData.ipAddress,
event,
userAgent: authData.userAgent,
userAgentType: authData.userAgentType,
expiresAt: new Date(Date.now() + ttl)
}).save();
return auditLog;
}
}

View File

@@ -1,3 +1,4 @@
import { Types } from "mongoose";
import * as Sentry from "@sentry/node";
import NodeCache from "node-cache";
import {
@@ -31,6 +32,7 @@ interface FeatureSet {
customRateLimits: boolean;
customAlerts: boolean;
auditLogs: boolean;
auditLogsRetentionDays: number;
samlSSO: boolean;
status: "incomplete" | "incomplete_expired" | "trialing" | "active" | "past_due" | "canceled" | "unpaid" | null;
trial_end: number | null;
@@ -66,6 +68,7 @@ class EELicenseService {
customRateLimits: true,
customAlerts: true,
auditLogs: false,
auditLogsRetentionDays: 0,
samlSSO: false,
status: null,
trial_end: null,
@@ -81,10 +84,10 @@ class EELicenseService {
});
}
public async getPlan(organizationId: string, workspaceId?: string): Promise<FeatureSet> {
public async getPlan(organizationId: Types.ObjectId, workspaceId?: Types.ObjectId): Promise<FeatureSet> {
try {
if (this.instanceType === "cloud") {
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ""}`);
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`);
if (cachedPlan) {
return cachedPlan;
}
@@ -101,7 +104,7 @@ class EELicenseService {
const { data: { currentPlan } } = await licenseServerKeyRequest.get(url);
// cache fetched plan for organization
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ""}`, currentPlan);
this.localFeatureSet.set(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`, currentPlan);
return currentPlan;
}
@@ -112,16 +115,16 @@ class EELicenseService {
return this.globalFeatureSet;
}
public async refreshPlan(organizationId: string, workspaceId?: string) {
public async refreshPlan(organizationId: Types.ObjectId, workspaceId?: Types.ObjectId) {
if (this.instanceType === "cloud") {
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ""}`);
this.localFeatureSet.del(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`);
await this.getPlan(organizationId, workspaceId);
}
}
public async delPlan(organizationId: string) {
public async delPlan(organizationId: Types.ObjectId) {
if (this.instanceType === "cloud") {
this.localFeatureSet.del(`${organizationId}-`);
this.localFeatureSet.del(`${organizationId.toString()}-`);
}
}

View File

@@ -10,7 +10,7 @@ import EELicenseService from "./EELicenseService";
/**
* Class to handle Enterprise Edition secret actions
*/
class EESecretService {
export default class EESecretService {
/**
* Save a secret snapshot that is a copy of the current state of secrets in workspace with id
* [workspaceId] under a new snapshot with incremented version under the
@@ -71,5 +71,3 @@ class EESecretService {
});
}
}
export default EESecretService;

View File

@@ -1,9 +1,11 @@
import EELicenseService from "./EELicenseService";
import EESecretService from "./EESecretService";
import EELogService from "./EELogService";
import EEAuditLogService from "./EEAuditLogService";
export {
EELicenseService,
EESecretService,
EELogService,
EEAuditLogService
}

View File

@@ -1,3 +1,4 @@
import { Request } from "express";
import { Types } from "mongoose";
import jwt from "jsonwebtoken";
import bcrypt from "bcrypt";
@@ -5,7 +6,6 @@ import {
APIKeyData,
ITokenVersion,
IUser,
ServiceAccount,
ServiceTokenData,
TokenVersion,
User,
@@ -14,7 +14,6 @@ import {
APIKeyDataNotFoundError,
AccountNotFoundError,
BadRequestError,
ServiceAccountNotFoundError,
ServiceTokenDataNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
@@ -26,11 +25,15 @@ import {
getJwtRefreshSecret,
} from "../config";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AuthMode
} from "../variables";
import {
UserAuthData,
ServiceTokenAuthData
} from "../interfaces/middleware";
import { ActorType } from "../ee/models";
import { getUserAgentType } from "../utils/posthog";
/**
*
@@ -42,7 +45,7 @@ export const validateAuthMode = ({
acceptedAuthModes,
}: {
headers: { [key: string]: string | string[] | undefined },
acceptedAuthModes: string[]
acceptedAuthModes: AuthMode[]
}) => {
const apiKey = headers["x-api-key"];
const authHeader = headers["authorization"];
@@ -55,7 +58,7 @@ export const validateAuthMode = ({
if (typeof apiKey === "string") {
// case: treat request authentication type as via X-API-KEY (i.e. API Key)
authMode = AUTH_MODE_API_KEY;
authMode = AuthMode.API_KEY;
authTokenValue = apiKey;
}
@@ -71,13 +74,10 @@ export const validateAuthMode = ({
switch (tokenValue.split(".", 1)[0]) {
case "st":
authMode = AUTH_MODE_SERVICE_TOKEN;
break;
case "sa":
authMode = AUTH_MODE_SERVICE_ACCOUNT;
authMode = AuthMode.SERVICE_TOKEN;
break;
default:
authMode = AUTH_MODE_JWT;
authMode = AuthMode.JWT;
}
authTokenValue = tokenValue;
@@ -100,10 +100,12 @@ export const validateAuthMode = ({
* @returns {User} user - user corresponding to JWT token
*/
export const getAuthUserPayload = async ({
req,
authTokenValue,
}: {
req: Request,
authTokenValue: string;
}) => {
}): Promise<UserAuthData> => {
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(authTokenValue, await getJwtAuthSecret())
);
@@ -130,11 +132,25 @@ export const getAuthUserPayload = async ({
if (decodedToken.accessVersion !== tokenVersion.accessVersion) throw UnauthorizedRequestError({
message: "Failed to validate access token",
});
return ({
user,
tokenVersionId: tokenVersion._id,
});
return {
actor: {
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
},
authPayload: user,
ipAddress: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
userAgentType: getUserAgentType(req.headers["user-agent"])
}
// return ({
// user,
// tokenVersionId: tokenVersion._id, // what to do with this? // move this out
// });
}
/**
@@ -144,10 +160,12 @@ export const getAuthUserPayload = async ({
* @returns {ServiceTokenData} serviceTokenData - service token data
*/
export const getAuthSTDPayload = async ({
req,
authTokenValue,
}: {
req: Request,
authTokenValue: string;
}) => {
}): Promise<ServiceTokenAuthData> => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
const serviceTokenData = await ServiceTokenData
@@ -180,36 +198,21 @@ export const getAuthSTDPayload = async ({
if (!serviceTokenDataToReturn) throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
return serviceTokenDataToReturn;
}
/**
* Return service account access key payload
* @param {Object} obj
* @param {String} obj.authTokenValue - service account access token value
* @returns {ServiceAccount} serviceAccount
*/
export const getAuthSAAKPayload = async ({
authTokenValue,
}: {
authTokenValue: string;
}) => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
const serviceAccount = await ServiceAccount.findById(
Buffer.from(TOKEN_IDENTIFIER, "base64").toString("hex")
).select("+secretHash");
if (!serviceAccount) {
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
return {
actor: {
type: ActorType.SERVICE,
metadata: {
serviceId: serviceTokenDataToReturn._id.toString(),
name: serviceTokenDataToReturn.name
}
},
authPayload: serviceTokenDataToReturn,
ipAddress: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
userAgentType: getUserAgentType(req.headers["user-agent"])
}
const result = await bcrypt.compare(TOKEN_SECRET, serviceAccount.secretHash);
if (!result) throw UnauthorizedRequestError({
message: "Failed to authenticate service account access key",
});
return serviceAccount;
// return serviceTokenDataToReturn;
}
/**
@@ -219,10 +222,12 @@ export const getAuthSAAKPayload = async ({
* @returns {APIKeyData} apiKeyData - API key data
*/
export const getAuthAPIKeyPayload = async ({
req,
authTokenValue,
}: {
req: Request,
authTokenValue: string;
}) => {
}): Promise<UserAuthData> => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
let apiKeyData = await APIKeyData
@@ -264,7 +269,19 @@ export const getAuthAPIKeyPayload = async ({
});
}
return user;
return {
actor: {
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
},
authPayload: user,
ipAddress: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
userAgentType: getUserAgentType(req.headers["user-agent"])
}
}
/**

View File

@@ -115,5 +115,5 @@ export const updateSubscriptionOrgQuantity = async ({
);
}
await EELicenseService.refreshPlan(organizationId);
await EELicenseService.refreshPlan(new Types.ObjectId(organizationId));
};

View File

@@ -11,9 +11,9 @@ import {
IServiceTokenData,
Secret,
SecretBlindIndexData,
ServiceTokenData
ServiceTokenData,
} from "../models";
import { SecretVersion } from "../ee/models";
import { SecretVersion, EventType } from "../ee/models";
import {
BadRequestError,
InternalServerError,
@@ -40,7 +40,7 @@ import {
} from "../utils/crypto";
import { TelemetryService } from "../services";
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
import { EELogService, EESecretService } from "../ee/services";
import { EELogService, EESecretService, EEAuditLogService } from "../ee/services";
import { getAuthDataPayloadIdObj, getAuthDataPayloadUserObj } from "../utils/auth";
import { getFolderByPath, getFolderIdFromServiceToken } from "../services/FolderService";
import picomatch from "picomatch";
@@ -433,10 +433,27 @@ export const createSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.CREATE_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId,
@@ -457,8 +474,8 @@ export const createSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@@ -528,9 +545,24 @@ export const getSecretsHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.GET_SECRETS,
metadata: {
environment,
secretPath,
numberOfSecrets: secrets.length
}
},
{
workspaceId
}
);
const postHogClient = await TelemetryService.getPostHogClient();
@@ -545,8 +577,8 @@ export const getSecretsHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@@ -622,10 +654,27 @@ export const getSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.GET_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
@@ -639,8 +688,8 @@ export const getSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@@ -771,9 +820,26 @@ export const updateSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.UPDATE_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
@@ -795,8 +861,8 @@ export const updateSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@@ -894,10 +960,27 @@ export const deleteSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.DELETE_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId,
@@ -918,8 +1001,8 @@ export const deleteSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}

View File

@@ -1,3 +1,4 @@
import { Types } from "mongoose";
import {
Bot,
Key,
@@ -25,7 +26,7 @@ export const createWorkspace = async ({
organizationId,
}: {
name: string;
organizationId: string;
organizationId: Types.ObjectId;
}) => {
// create workspace
const workspace = await new Workspace({

View File

@@ -1,15 +1,31 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
} from "../../models";
import {
UserActor,
ServiceActor,
UserAgentType
} from "../../ee/models";
export interface AuthData {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
authChannel: string;
authIP: string;
authUserAgent: string;
interface BaseAuthData {
ipAddress: string;
userAgent: string;
userAgentType: UserAgentType;
tokenVersionId?: Types.ObjectId;
}
}
export interface UserAuthData extends BaseAuthData {
actor: UserActor;
authPayload: IUser;
}
export interface ServiceTokenAuthData extends BaseAuthData {
actor: ServiceActor;
authPayload: IServiceTokenData;
}
export type AuthData =
| UserAuthData
| ServiceTokenAuthData;

View File

@@ -1,25 +1,13 @@
import jwt from "jsonwebtoken";
import { Types } from "mongoose";
import { NextFunction, Request, Response } from "express";
import {
getAuthAPIKeyPayload,
getAuthSAAKPayload,
getAuthSTDPayload,
getAuthUserPayload,
validateAuthMode,
} from "../helpers/auth";
import {
IServiceAccount,
IServiceTokenData,
IUser,
} from "../models";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { getChannelFromUserAgent } from "../utils/posthog";
import { AuthMode } from "../variables";
import { AuthData } from "../interfaces/middleware";
declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -38,9 +26,9 @@ declare module "jsonwebtoken" {
* @returns
*/
const requireAuth = ({
acceptedAuthModes = [AUTH_MODE_JWT],
acceptedAuthModes = [AuthMode.JWT],
}: {
acceptedAuthModes: string[];
acceptedAuthModes: AuthMode[];
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
@@ -50,55 +38,36 @@ const requireAuth = ({
headers: req.headers,
acceptedAuthModes,
});
let authPayload: IUser | IServiceAccount | IServiceTokenData;
let authUserPayload: {
user: IUser;
tokenVersionId: Types.ObjectId;
};
let authData: AuthData;
switch (authMode) {
case AUTH_MODE_SERVICE_ACCOUNT:
authPayload = await getAuthSAAKPayload({
case AuthMode.SERVICE_TOKEN:
authData = await getAuthSTDPayload({
req,
authTokenValue,
});
req.serviceAccount = authPayload;
req.serviceTokenData = authData.authPayload;
break;
case AUTH_MODE_SERVICE_TOKEN:
authPayload = await getAuthSTDPayload({
authTokenValue,
case AuthMode.API_KEY:
authData = await getAuthAPIKeyPayload({
req,
authTokenValue
});
req.serviceTokenData = authPayload;
req.user = authData.authPayload;
break;
case AUTH_MODE_API_KEY:
authPayload = await getAuthAPIKeyPayload({
authTokenValue,
case AuthMode.JWT:
authData = await getAuthUserPayload({
req,
authTokenValue
});
req.user = authPayload;
break;
default:
authUserPayload = await getAuthUserPayload({
authTokenValue,
});
authPayload = authUserPayload.user;
req.user = authUserPayload.user;
req.tokenVersionId = authUserPayload.tokenVersionId;
// authPayload = authUserPayload.user;
req.user = authData.authPayload;
// req.tokenVersionId = authUserPayload.tokenVersionId; // TODO
break;
}
req.requestData = {
...req.params,
...req.query,
...req.body,
}
req.authData = {
authMode,
authPayload, // User, ServiceAccount, ServiceTokenData
authChannel: getChannelFromUserAgent(req.headers["user-agent"]),
authIP: req.realIP,
authUserAgent: req.headers["user-agent"] ?? "other",
tokenVersionId: req.tokenVersionId,
}
req.authData = authData;
return next();
}

View File

@@ -32,6 +32,8 @@ const requireWorkspaceAuth = ({
const workspaceId = req[locationWorkspaceId]?.workspaceId;
const environment = locationEnvironment ? req[locationEnvironment]?.environment : undefined;
console.log("workspaceId: ", workspaceId);
// validate clients
const { membership, workspace } = await validateClientForWorkspace({
authData: req.authData,

View File

@@ -4,7 +4,7 @@ import { body } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { authController } from "../../controllers/v1";
import { authLimiter } from "../../helpers/rateLimiter";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
router.post("/token", validateRequest, authController.getNewToken);
@@ -30,7 +30,7 @@ router.post(
"/logout",
authLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
authController.logout
);
@@ -38,7 +38,7 @@ router.post(
router.post(
"/checkAuth",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
authController.checkAuth
);
@@ -53,9 +53,9 @@ router.delete(
"/sessions",
authLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
authController.revokeAllSessions
);
export default router;
export default router;

View File

@@ -8,12 +8,12 @@ import {
validateRequest,
} from "../../middleware";
import { botController } from "../../controllers/v1";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, MEMBER, AuthMode } from "../../variables";
router.get(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -27,7 +27,7 @@ router.get(
router.patch(
"/:botId/active",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireBotAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -8,9 +8,8 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER,
AuthMode
} from "../../variables";
import { body, param } from "express-validator";
import { integrationController } from "../../controllers/v1";
@@ -18,7 +17,7 @@ import { integrationController } from "../../controllers/v1";
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -44,7 +43,7 @@ router.post(
router.patch(
"/:integrationId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireIntegrationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -64,7 +63,7 @@ router.patch(
router.delete(
"/:integrationId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireIntegrationAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -9,16 +9,15 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER,
AuthMode
} from "../../variables";
import { integrationAuthController } from "../../controllers/v1";
router.get(
"/integration-options",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
integrationAuthController.getIntegrationOptions
);
@@ -26,7 +25,7 @@ router.get(
router.get(
"/:integrationAuthId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -39,7 +38,7 @@ router.get(
router.post(
"/oauth-token",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -62,7 +61,7 @@ router.post(
body("integration").exists().trim().notEmpty(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -74,7 +73,7 @@ router.post(
router.get(
"/:integrationAuthId/apps",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -89,7 +88,7 @@ router.get(
router.get(
"/:integrationAuthId/teams",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -102,7 +101,7 @@ router.get(
router.get(
"/:integrationAuthId/vercel/branches",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -117,7 +116,7 @@ router.get(
router.get(
"/:integrationAuthId/railway/environments",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -131,7 +130,7 @@ router.get(
router.get(
"/:integrationAuthId/railway/services",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -145,7 +144,7 @@ router.get(
router.get(
"/:integrationAuthId/bitbucket/workspaces",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -158,7 +157,7 @@ router.get(
router.get(
"/:integrationAuthId/northflank/secret-groups",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -172,7 +171,7 @@ router.get(
router.delete(
"/:integrationAuthId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -3,12 +3,12 @@ const router = express.Router();
import { body } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { membershipOrgController } from "../../controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
router.post(
"/signup",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("inviteeEmail").exists().trim().notEmpty().isEmail(),
body("organizationId").exists().trim().notEmpty(),

View File

@@ -6,13 +6,13 @@ import {
validateRequest,
} from "../../middleware";
import { body, param } from "express-validator";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, MEMBER, AuthMode } from "../../variables";
import { keyController } from "../../controllers/v1";
router.post(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -27,7 +27,7 @@ router.post(
router.get(
"/:workspaceId/latest",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -4,14 +4,14 @@ import { body, param } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { membershipController } from "../../controllers/v1";
import { membershipController as EEMembershipControllers } from "../../ee/controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
// note: ALL DEPRECIATED (moved to api/v2/workspace/:workspaceId/memberships/:membershipId)
router.get( // used for old CLI (deprecate)
"/:workspaceId/connect",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("workspaceId").exists().trim(),
validateRequest,
@@ -21,7 +21,7 @@ router.get( // used for old CLI (deprecate)
router.delete(
"/:membershipId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipId").exists().trim(),
validateRequest,
@@ -31,7 +31,7 @@ router.delete(
router.post(
"/:membershipId/change-role",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("role").exists().trim(),
validateRequest,
@@ -41,7 +41,7 @@ router.post(
router.post(
"/:membershipId/deny-permissions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipId").isMongoId().exists().trim(),
body("permissions").isArray().exists(),

View File

@@ -3,13 +3,13 @@ const router = express.Router();
import { param } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { membershipOrgController } from "../../controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
router.post(
// TODO
"/membershipOrg/:membershipOrgId/change-role",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipOrgId"),
validateRequest,
@@ -19,7 +19,7 @@ router.post(
router.delete(
"/:membershipOrgId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipOrgId").exists().trim(),
validateRequest,

View File

@@ -9,16 +9,16 @@ import {
import {
ACCEPTED,
ADMIN,
AUTH_MODE_JWT,
MEMBER,
OWNER,
AuthMode
} from "../../variables";
import { organizationController } from "../../controllers/v1";
router.get( // deprecated (moved to api/v2/users/me/organizations)
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
organizationController.getOrganizations
);
@@ -26,7 +26,7 @@ router.get( // deprecated (moved to api/v2/users/me/organizations)
router.post( // not used on frontend
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("organizationName").exists().trim().notEmpty(),
validateRequest,
@@ -36,7 +36,7 @@ router.post( // not used on frontend
router.get(
"/:organizationId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -50,7 +50,7 @@ router.get(
router.get( // deprecated (moved to api/v2/organizations/:organizationId/memberships)
"/:organizationId/users",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -64,7 +64,7 @@ router.get( // deprecated (moved to api/v2/organizations/:organizationId/members
router.get(
"/:organizationId/my-workspaces", // deprecated (moved to api/v2/organizations/:organizationId/workspaces)
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -78,7 +78,7 @@ router.get(
router.patch(
"/:organizationId/name",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -93,7 +93,7 @@ router.patch(
router.get(
"/:organizationId/incidentContactOrg",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -107,7 +107,7 @@ router.get(
router.post(
"/:organizationId/incidentContactOrg",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -122,7 +122,7 @@ router.post(
router.delete(
"/:organizationId/incidentContactOrg",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -137,7 +137,7 @@ router.delete(
router.post(
"/:organizationId/customer-portal-session",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -151,7 +151,7 @@ router.post(
router.get(
"/:organizationId/subscriptions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -165,7 +165,7 @@ router.get(
router.get(
"/:organizationId/workspace-memberships",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],

View File

@@ -4,14 +4,12 @@ import { body } from "express-validator";
import { requireAuth, requireSignupAuth, validateRequest } from "../../middleware";
import { passwordController } from "../../controllers/v1";
import { passwordLimiter } from "../../helpers/rateLimiter";
import {
AUTH_MODE_JWT,
} from "../../variables";
import { AuthMode } from "../../variables";
router.post(
"/srp1",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("clientPublicKey").exists().isString().trim().notEmpty(),
validateRequest,
@@ -22,7 +20,7 @@ router.post(
"/change-password",
passwordLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("clientProof").exists().trim().notEmpty(),
body("protectedKey").exists().isString().trim().notEmpty(),
@@ -65,7 +63,7 @@ router.post(
"/backup-private-key",
passwordLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("clientProof").exists().isString().trim().notEmpty(),
body("encryptedPrivateKey").exists().isString().trim().notEmpty(), // (backup) private key encrypted under a strong key

View File

@@ -10,8 +10,8 @@ import { body, param, query } from "express-validator";
import { secretController } from "../../controllers/v1";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode
} from "../../variables";
// note to devs: these endpoints will be deprecated in favor of v2
@@ -19,7 +19,7 @@ import {
router.post(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -37,7 +37,7 @@ router.post(
router.get(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -3,12 +3,12 @@ const router = express.Router();
import { body, param, query } from "express-validator";
import { secretImportController } from "../../controllers/v1";
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, MEMBER, AuthMode } from "../../variables";
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -27,7 +27,7 @@ router.post(
router.put(
"/:id",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT]
}),
param("id").exists().isString().trim(),
body("secretImports").exists().isArray(),
@@ -40,7 +40,7 @@ router.put(
router.delete(
"/:id",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT]
}),
param("id").exists().isString().trim(),
body("secretImportPath").isString().exists().trim(),
@@ -52,7 +52,7 @@ router.delete(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -68,7 +68,7 @@ router.get(
router.get(
"/secrets",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -7,12 +7,12 @@ import {
} from "../../middleware";
import { body, param } from "express-validator";
import { createInstallationSession, getCurrentOrganizationInstallationStatus, getRisksForOrganization, linkInstallationToOrganization, updateRisksStatus } from "../../controllers/v1/secretScanningController";
import { ACCEPTED, ADMIN, MEMBER, OWNER } from "../../variables";
import { ACCEPTED, ADMIN, MEMBER, OWNER, AuthMode } from "../../variables";
router.post(
"/create-installation-session/organization/:organizationId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
@@ -26,7 +26,7 @@ router.post(
router.post(
"/link-installation",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
body("installationId").exists().trim(),
body("sessionId").exists().trim(),
@@ -37,7 +37,7 @@ router.post(
router.get(
"/installation-status/organization/:organizationId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
@@ -51,7 +51,7 @@ router.get(
router.get(
"/organization/:organizationId/risks",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
@@ -65,7 +65,7 @@ router.get(
router.post(
"/organization/:organizationId/risks/:riskId/status",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
param("riskId").exists().trim(),

View File

@@ -12,12 +12,12 @@ import {
getFolders,
updateFolderById,
} from "../../controllers/v1/secretsFolderController";
import { ADMIN, MEMBER } from "../../variables";
import { ADMIN, MEMBER, AuthMode } from "../../variables";
router.post(
"/",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -34,7 +34,7 @@ router.post(
router.patch(
"/:folderId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
body("workspaceId").exists(),
body("environment").exists(),
@@ -46,7 +46,7 @@ router.patch(
router.delete(
"/:folderId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
body("workspaceId").exists(),
body("environment").exists(),
@@ -58,7 +58,7 @@ router.delete(
router.get(
"/",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
query("workspaceId").exists().isString().trim(),
query("environment").exists().isString().trim(),

View File

@@ -9,8 +9,8 @@ import {
import { body } from "express-validator";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode
} from "../../variables";
import { serviceTokenController } from "../../controllers/v1";
@@ -25,7 +25,7 @@ router.get(
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -2,14 +2,12 @@ import express from "express";
const router = express.Router();
import { requireAuth } from "../../middleware";
import { userController } from "../../controllers/v1";
import {
AUTH_MODE_JWT,
} from "../../variables";
import { AuthMode } from "../../variables";
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
userController.getUser
);

View File

@@ -3,13 +3,13 @@ const router = express.Router();
import { requireAuth, validateRequest } from "../../middleware";
import { body, query } from "express-validator";
import { userActionController } from "../../controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
// note: [userAction] will be deprecated in /v2 in favor of [action]
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("action"),
validateRequest,
@@ -19,7 +19,7 @@ router.post(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
query("action"),
validateRequest,

View File

@@ -2,13 +2,13 @@ import express from "express";
const router = express.Router();
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
import { body, param, query } from "express-validator";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, MEMBER, AuthMode } from "../../variables";
import { webhookController } from "../../controllers/v1";
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -27,7 +27,7 @@ router.post(
router.patch(
"/:webhookId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
param("webhookId").exists().isString().trim(),
body("isDisabled").default(false).isBoolean(),
@@ -38,7 +38,7 @@ router.patch(
router.post(
"/:webhookId/test",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
param("webhookId").exists().isString().trim(),
validateRequest,
@@ -48,7 +48,7 @@ router.post(
router.delete(
"/:webhookId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
param("webhookId").exists().isString().trim(),
validateRequest,
@@ -58,7 +58,7 @@ router.delete(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -8,16 +8,15 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER,
AuthMode
} from "../../variables";
import { membershipController, workspaceController } from "../../controllers/v1";
router.get(
"/:workspaceId/keys",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -31,7 +30,7 @@ router.get(
router.get(
"/:workspaceId/users",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -45,7 +44,7 @@ router.get(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
workspaceController.getWorkspaces
);
@@ -53,7 +52,7 @@ router.get(
router.get(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -67,7 +66,7 @@ router.get(
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("workspaceName").exists().trim().notEmpty(),
body("organizationId").exists().trim().notEmpty(),
@@ -78,7 +77,7 @@ router.post(
router.delete(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -92,7 +91,7 @@ router.delete(
router.post(
"/:workspaceId/name",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -107,7 +106,7 @@ router.post(
router.post(
"/:workspaceId/invite-signup",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -122,7 +121,7 @@ router.post(
router.get(
"/:workspaceId/integrations",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -136,7 +135,7 @@ router.get(
router.get(
"/:workspaceId/authorizations",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -150,7 +149,7 @@ router.get(
router.get(
"/:workspaceId/service-tokens", // deprecate
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -9,14 +9,14 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode
} from "../../variables";
router.post(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -32,7 +32,7 @@ router.post(
router.put(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -49,7 +49,7 @@ router.put(
router.delete(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -64,7 +64,7 @@ router.delete(
router.get(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [MEMBER, ADMIN],

View File

@@ -10,10 +10,9 @@ import { body, param } from "express-validator";
import {
ACCEPTED,
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER,
OWNER,
AuthMode
} from "../../variables";
import { organizationsController } from "../../controllers/v2";
@@ -24,7 +23,7 @@ router.get(
param("organizationId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@@ -40,7 +39,7 @@ router.patch(
body("role").exists().isString().trim().isIn([OWNER, ADMIN, MEMBER]),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@@ -59,7 +58,7 @@ router.delete(
param("membershipId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@@ -77,7 +76,7 @@ router.get(
param("organizationId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@@ -91,7 +90,7 @@ router.get(
param("organizationId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],

View File

@@ -8,9 +8,8 @@ import {
import { body, param, query } from "express-validator";
import {
ADMIN,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_TOKEN,
MEMBER,
AuthMode,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
} from "../../variables";
@@ -24,7 +23,7 @@ const router = express.Router();
router.post(
"/batch-create/workspace/:workspaceId/environment/:environment",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -41,7 +40,7 @@ router.post(
router.post(
"/workspace/:workspaceId/environment/:environment",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -60,7 +59,7 @@ router.get(
param("workspaceId").exists().trim(),
query("environment").exists(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_TOKEN],
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -74,7 +73,7 @@ router.get(
router.get(
"/:secretId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_TOKEN],
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -87,7 +86,7 @@ router.get(
router.delete(
"/batch/workspace/:workspaceId/environment/:environmentName",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("workspaceId").exists().isMongoId().trim(),
param("environmentName").exists().trim(),
@@ -103,7 +102,7 @@ router.delete(
router.delete(
"/:secretId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -117,7 +116,7 @@ router.delete(
router.patch(
"/batch-modify/workspace/:workspaceId/environment/:environmentName",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("secrets").exists().isArray().custom((secrets: ModifySecretRequestBody[]) => secrets.length > 0),
param("workspaceId").exists().isMongoId().trim(),
@@ -133,7 +132,7 @@ router.patch(
router.patch(
"/workspace/:workspaceId/environment/:environmentName",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("secret").isObject(),
param("workspaceId").exists().isMongoId().trim(),

View File

@@ -12,11 +12,8 @@ import { body, query } from "express-validator";
import { secretsController } from "../../controllers/v2";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
MEMBER,
AuthMode,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
SECRET_PERSONAL,
@@ -27,7 +24,7 @@ import { BatchSecretRequest } from "../../types/secret";
router.post(
"/batch",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -109,7 +106,7 @@ router.post(
}),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -130,12 +127,7 @@ router.get(
query("include_imports").optional().default(false).isBoolean(),
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -172,7 +164,7 @@ router.patch(
}),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireSecretsAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -201,7 +193,7 @@ router.delete(
.isEmpty(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireSecretsAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -1,159 +1,158 @@
import express from "express";
const router = express.Router();
import {
requireAuth,
requireOrganizationAuth,
requireServiceAccountAuth,
requireServiceAccountWorkspacePermissionAuth,
requireWorkspaceAuth,
validateRequest,
} from "../../middleware";
import { body, param, query } from "express-validator";
import {
ACCEPTED,
ADMIN,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
MEMBER,
OWNER,
} from "../../variables";
import { serviceAccountsController } from "../../controllers/v2";
// import {
// requireAuth,
// requireOrganizationAuth,
// requireServiceAccountAuth,
// requireServiceAccountWorkspacePermissionAuth,
// requireWorkspaceAuth,
// validateRequest,
// } from "../../middleware";
// import { body, param, query } from "express-validator";
// import {
// ACCEPTED,
// ADMIN,
// MEMBER,
// OWNER,
// AuthMode
// } from "../../variables";
// import { serviceAccountsController } from "../../controllers/v2";
router.get( // TODO: check
"/me",
requireAuth({
acceptedAuthModes: [AUTH_MODE_SERVICE_ACCOUNT],
}),
serviceAccountsController.getCurrentServiceAccount
);
// router.get( // TODO: check
// "/me",
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_SERVICE_ACCOUNT],
// }),
// serviceAccountsController.getCurrentServiceAccount
// );
router.get(
"/:serviceAccountId",
param("serviceAccountId").exists().isString().trim(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.getServiceAccountById
);
// router.get(
// "/:serviceAccountId",
// param("serviceAccountId").exists().isString().trim(),
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.getServiceAccountById
// );
router.post(
"/",
body("organizationId").exists().isString().trim(),
body("name").exists().isString().trim(),
body("publicKey").exists().isString().trim(),
body("expiresIn").isNumeric(), // measured in ms
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
locationOrganizationId: "body",
}),
serviceAccountsController.createServiceAccount
);
// router.post(
// "/",
// body("organizationId").exists().isString().trim(),
// body("name").exists().isString().trim(),
// body("publicKey").exists().isString().trim(),
// body("expiresIn").isNumeric(), // measured in ms
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireOrganizationAuth({
// acceptedRoles: [OWNER, ADMIN, MEMBER],
// acceptedStatuses: [ACCEPTED],
// locationOrganizationId: "body",
// }),
// serviceAccountsController.createServiceAccount
// );
router.patch(
"/:serviceAccountId/name",
param("serviceAccountId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.changeServiceAccountName
);
// router.patch(
// "/:serviceAccountId/name",
// param("serviceAccountId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.changeServiceAccountName
// );
router.delete(
"/:serviceAccountId",
param("serviceAccountId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.deleteServiceAccount
);
// router.delete(
// "/:serviceAccountId",
// param("serviceAccountId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.deleteServiceAccount
// );
router.get(
"/:serviceAccountId/permissions/workspace",
param("serviceAccountId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.getServiceAccountWorkspacePermissions
);
// router.get(
// "/:serviceAccountId/permissions/workspace",
// param("serviceAccountId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.getServiceAccountWorkspacePermissions
// );
router.post(
"/:serviceAccountId/permissions/workspace",
param("serviceAccountId").exists().isString().trim(),
body("workspaceId").exists().isString().notEmpty(),
body("environment").exists().isString().notEmpty(),
body("read").isBoolean().optional(),
body("write").isBoolean().optional(),
body("encryptedKey").exists().isString().notEmpty(),
body("nonce").exists().isString().notEmpty(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: "body",
}),
serviceAccountsController.addServiceAccountWorkspacePermission
);
// router.post(
// "/:serviceAccountId/permissions/workspace",
// param("serviceAccountId").exists().isString().trim(),
// body("workspaceId").exists().isString().notEmpty(),
// body("environment").exists().isString().notEmpty(),
// body("read").isBoolean().optional(),
// body("write").isBoolean().optional(),
// body("encryptedKey").exists().isString().notEmpty(),
// body("nonce").exists().isString().notEmpty(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// requireWorkspaceAuth({
// acceptedRoles: [ADMIN, MEMBER],
// locationWorkspaceId: "body",
// }),
// serviceAccountsController.addServiceAccountWorkspacePermission
// );
router.delete(
"/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionId",
param("serviceAccountId").exists().isString().trim(),
param("serviceAccountWorkspacePermissionId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
requireServiceAccountWorkspacePermissionAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.deleteServiceAccountWorkspacePermission
);
// router.delete(
// "/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionId",
// param("serviceAccountId").exists().isString().trim(),
// param("serviceAccountWorkspacePermissionId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// requireServiceAccountWorkspacePermissionAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.deleteServiceAccountWorkspacePermission
// );
router.get(
"/:serviceAccountId/keys",
query("workspaceId").optional().isString(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.getServiceAccountKeys
);
// router.get(
// "/:serviceAccountId/keys",
// query("workspaceId").optional().isString(),
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.getServiceAccountKeys
// );
export default router;

View File

@@ -9,10 +9,8 @@ import {
import { body, param } from "express-validator";
import {
ADMIN,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
MEMBER,
AuthMode,
PERMISSION_WRITE_SECRETS
} from "../../variables";
import { serviceTokenDataController } from "../../controllers/v2";
@@ -20,7 +18,7 @@ import { serviceTokenDataController } from "../../controllers/v2";
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.SERVICE_TOKEN]
}),
serviceTokenDataController.getServiceTokenData
);
@@ -28,7 +26,7 @@ router.get(
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT]
acceptedAuthModes: [AuthMode.JWT]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -63,7 +61,7 @@ router.post(
router.delete(
"/:serviceTokenDataId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT]
}),
requireServiceTokenDataAuth({
acceptedRoles: [ADMIN, MEMBER]

View File

@@ -9,14 +9,14 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode
} from "../../variables";
router.get(
"/:workspaceId/tags",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [MEMBER, ADMIN],
@@ -30,7 +30,7 @@ router.get(
router.delete(
"/tags/:tagId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("tagId").exists().trim(),
validateRequest,
@@ -40,7 +40,7 @@ router.delete(
router.post(
"/:workspaceId/tags",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [MEMBER, ADMIN],

View File

@@ -6,10 +6,7 @@ import {
} from "../../middleware";
import { body, param } from "express-validator";
import { usersController } from "../../controllers/v2";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
} from "../../variables";
import { AuthMode } from "../../variables";
import {
AuthProvider
} from "../../models";
@@ -17,7 +14,7 @@ import {
router.get(
"/me",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
usersController.getMe
);
@@ -25,7 +22,7 @@ router.get(
router.patch(
"/me/mfa",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
body("isMfaEnabled").exists().isBoolean(),
validateRequest,
@@ -35,7 +32,7 @@ router.patch(
router.patch(
"/me/name",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
body("firstName").exists().isString(),
body("lastName").isString(),
@@ -46,7 +43,7 @@ router.patch(
router.patch(
"/me/auth-provider",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
body("authProvider").exists().isString().isIn([
AuthProvider.EMAIL,
@@ -60,7 +57,7 @@ router.patch(
router.get(
"/me/organizations",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
usersController.getMyOrganizations
);
@@ -68,7 +65,7 @@ router.get(
router.get(
"/me/api-keys",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
usersController.getMyAPIKeys
);
@@ -76,7 +73,7 @@ router.get(
router.post(
"/me/api-keys",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
body("name").exists().isString().trim(),
body("expiresIn").isNumeric(),
@@ -87,7 +84,7 @@ router.post(
router.delete(
"/me/api-keys/:apiKeyDataId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
param("apiKeyDataId").exists().trim(),
validateRequest,
@@ -97,7 +94,7 @@ router.delete(
router.get(
"/me/sessions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
usersController.getMySessions
);
@@ -105,7 +102,7 @@ router.get(
router.delete(
"/me/sessions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
usersController.deleteMySessions
);

View File

@@ -9,17 +9,15 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_TOKEN,
MEMBER,
AuthMode
} from "../../variables";
import { workspaceController } from "../../controllers/v2";
router.post(
"/:workspaceId/secrets",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -37,7 +35,7 @@ router.post(
router.get(
"/:workspaceId/secrets",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_TOKEN],
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -53,7 +51,7 @@ router.get(
router.get(
"/:workspaceId/encrypted-key",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -67,7 +65,7 @@ router.get(
router.get(
"/:workspaceId/service-token-data",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -83,7 +81,7 @@ router.get( // new - TODO: rewire dashboard to this route
param("workspaceId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@@ -99,7 +97,7 @@ router.patch( // TODO - rewire dashboard to this route
body("role").exists().isString().trim().isIn([ADMIN, MEMBER]),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -118,7 +116,7 @@ router.delete( // TODO - rewire dashboard to this route
param("membershipId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -134,7 +132,7 @@ router.delete( // TODO - rewire dashboard to this route
router.patch(
"/:workspaceId/auto-capitalization",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

View File

@@ -5,11 +5,8 @@ import { body, param, query } from "express-validator";
import { secretsController } from "../../controllers/v3";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
MEMBER,
AuthMode,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
SECRET_PERSONAL,
@@ -25,10 +22,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
secretsController.getSecretsRaw
@@ -44,10 +40,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -73,10 +68,9 @@ router.post(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -102,10 +96,9 @@ router.patch(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -130,10 +123,9 @@ router.delete(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -156,10 +148,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -192,10 +183,9 @@ router.post(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -220,10 +210,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -250,10 +239,9 @@ router.patch(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@@ -278,10 +266,9 @@ router.delete(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({

View File

@@ -8,7 +8,7 @@ import {
import { workspacesController } from "../../controllers/v3";
import {
ADMIN,
AUTH_MODE_JWT,
AuthMode
} from "../../variables";
import { body, param } from "express-validator";
@@ -19,7 +19,7 @@ router.get(
param("workspaceId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -33,7 +33,7 @@ router.get( // allow admins to get all workspace secrets (part of blind indices
param("workspaceId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@@ -65,7 +65,7 @@ router.post( // allow admins to name all workspace secrets (part of blind indice
.withMessage("secretId must be a string"),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],

View File

@@ -1,8 +1,6 @@
import { Types } from "mongoose";
import {
AuthData,
AuthData
} from "../../interfaces/middleware";
declare module "express" {

View File

@@ -1,15 +1,15 @@
const CLI_USER_AGENT_NAME = "cli"
const K8_OPERATOR_AGENT_NAME = "k8-operator"
export const getChannelFromUserAgent = function (userAgent: string | undefined) {
import { UserAgentType } from "../ee/models"
export const getUserAgentType = function (userAgent: string | undefined) {
if (userAgent == undefined) {
return "other"
} else if (userAgent == CLI_USER_AGENT_NAME) {
return "cli"
} else if (userAgent == K8_OPERATOR_AGENT_NAME) {
return "k8-operator"
return UserAgentType.OTHER;
} else if (userAgent == UserAgentType.CLI) {
return UserAgentType.CLI;
} else if (userAgent == UserAgentType.K8_OPERATOR) {
return UserAgentType.K8_OPERATOR;
} else if (userAgent.toLowerCase().includes("mozilla")) {
return "web"
return UserAgentType.WEB;
} else {
return "other"
return UserAgentType.OTHER;
}
}

View File

@@ -1,25 +1,15 @@
import { Types } from "mongoose";
import {
Bot,
IServiceAccount,
IServiceTokenData,
IUser,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { validateUserClientForWorkspace } from "./user";
import {
BotNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for bot with id [botId] based
@@ -34,65 +24,24 @@ export const validateClientForBot = async ({
botId,
acceptedRoles,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
botId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
}) => {
const bot = await Bot.findById(botId);
if (!bot) throw BotNotFoundError();
if (
authData.authMode === AUTH_MODE_JWT &&
authData.authPayload instanceof User
) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: bot.workspace,
acceptedRoles,
});
return bot;
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: bot.workspace,
acceptedRoles,
});
return bot;
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for bot",
});
}
if (
authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
authData.authPayload instanceof ServiceAccount
) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: bot.workspace,
});
return bot;
}
if (
authData.authMode === AUTH_MODE_SERVICE_TOKEN &&
authData.authPayload instanceof ServiceTokenData
) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for bot",
});
}
if (
authData.authMode === AUTH_MODE_API_KEY &&
authData.authPayload instanceof User
) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: bot.workspace,
acceptedRoles,
});
return bot;
}
throw BotNotFoundError({
message: "Failed client authorization for bot",
});
};

View File

@@ -1,15 +1,9 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
Integration,
IntegrationAuth,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { validateUserClientForWorkspace } from "./user";
import { IntegrationService } from "../services";
import {
@@ -17,12 +11,8 @@ import {
IntegrationNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for integration with id [integrationId] based
@@ -39,10 +29,7 @@ export const validateClientForIntegration = async ({
integrationId,
acceptedRoles,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
integrationId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
}) => {
@@ -61,43 +48,19 @@ export const validateClientForIntegration = async ({
const accessToken = (await IntegrationService.getIntegrationAuthAccess({
integrationAuthId: integrationAuth._id,
})).accessToken;
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integration.workspace,
acceptedRoles,
});
return ({ integration, accessToken });
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: integration.workspace,
});
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: integration.workspace,
acceptedRoles,
});
return ({ integration, accessToken });
return ({ integration, accessToken });
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration",
});
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration",
});
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integration.workspace,
acceptedRoles,
});
return ({ integration, accessToken });
}
throw UnauthorizedRequestError({
message: "Failed client authorization for integration",
});
}

View File

@@ -1,27 +1,17 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
IWorkspace,
IntegrationAuth,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import {
IntegrationAuthNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import { IntegrationService } from "../services";
import { validateUserClientForWorkspace } from "./user";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for integration authorization with id [integrationAuthId] based
@@ -38,10 +28,7 @@ import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
acceptedRoles,
attachAccessToken,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
integrationAuthId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
attachAccessToken?: boolean;
@@ -66,44 +53,20 @@ import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
accessId = access.accessId;
}
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integrationAuth.workspace._id,
acceptedRoles,
});
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: integrationAuth.workspace._id,
acceptedRoles,
});
return ({ integrationAuth, accessToken, accessId });
return ({ integrationAuth, accessToken, accessId });
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration authorization",
});
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: integrationAuth.workspace._id,
});
return ({ integrationAuth, accessToken, accessId });
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration authorization",
});
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integrationAuth.workspace._id,
acceptedRoles,
});
return ({ integrationAuth, accessToken, accessId });
}
throw UnauthorizedRequestError({
message: "Failed client authorization for integration authorization",
});
}
export {

View File

@@ -15,12 +15,9 @@ import {
MembershipNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
import { auth } from "../routes/v1";
/**
* Validate authenticated clients for membership with id [membershipId] based
@@ -36,10 +33,7 @@ export const validateClientForMembership = async ({
membershipId,
acceptedRoles,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
membershipId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
}) => {
@@ -49,46 +43,22 @@ export const validateClientForMembership = async ({
if (!membership) throw MembershipNotFoundError({
message: "Failed to find membership",
});
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: membership.workspace,
acceptedRoles,
});
return membership;
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: membership.workspace,
});
return membership;
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: membership.workspace,
acceptedRoles,
});
return membership;
case ActorType.SERVICE:
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload as IServiceTokenData,
workspaceId: new Types.ObjectId(membership.workspace),
});
return membership;
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload,
workspaceId: new Types.ObjectId(membership.workspace),
});
return membership;
}
if (authData.authMode == AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: membership.workspace,
acceptedRoles,
});
return membership;
}
throw UnauthorizedRequestError({
message: "Failed client authorization for membership",
});
}

View File

@@ -1,12 +1,6 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
MembershipOrg,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import {
validateMembershipOrg,
@@ -15,12 +9,8 @@ import {
MembershipOrgNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for organization membership with id [membershipOrgId] based
@@ -37,10 +27,7 @@ export const validateClientForMembershipOrg = async ({
acceptedRoles,
acceptedStatuses,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
membershipOrgId: Types.ObjectId;
acceptedRoles: Array<"owner" | "admin" | "member">;
acceptedStatuses: Array<"invited" | "accepted">;
@@ -50,44 +37,20 @@ export const validateClientForMembershipOrg = async ({
if (!membershipOrg) throw MembershipOrgNotFoundError({
message: "Failed to find organization membership ",
});
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateMembershipOrg({
userId: authData.authPayload._id,
organizationId: membershipOrg.organization,
acceptedRoles,
acceptedStatuses,
});
return membershipOrg;
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
if (!authData.authPayload.organization.equals(membershipOrg.organization)) throw UnauthorizedRequestError({
message: "Failed service account client authorization for organization membership",
});
return membershipOrg;
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
throw UnauthorizedRequestError({
message: "Failed service account client authorization for organization membership",
});
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateMembershipOrg({
userId: authData.authPayload._id,
organizationId: membershipOrg.organization,
acceptedRoles,
acceptedStatuses,
});
return membershipOrg;
switch (authData.actor.type) {
case ActorType.USER:
await validateMembershipOrg({
userId: authData.authPayload._id,
organizationId: membershipOrg.organization,
acceptedRoles,
acceptedStatuses,
});
return membershipOrg;
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service account client authorization for organization membership",
});
}
throw UnauthorizedRequestError({
message: "Failed client authorization for organization membership",
});
}

View File

@@ -1,25 +1,15 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
Organization,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import {
OrganizationNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import { validateUserClientForOrganization } from "./user";
import { validateServiceAccountClientForOrganization } from "./serviceAccount";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate accepted clients for organization with id [organizationId]
@@ -33,10 +23,7 @@ export const validateClientForOrganization = async ({
acceptedRoles,
acceptedStatuses,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
organizationId: Types.ObjectId;
acceptedRoles: Array<"owner" | "admin" | "member">;
acceptedStatuses: Array<"invited" | "accepted">;
@@ -48,57 +35,20 @@ export const validateClientForOrganization = async ({
message: "Failed to find organization",
});
}
switch (authData.actor.type) {
case ActorType.USER:
const membershipOrg = await validateUserClientForOrganization({
user: authData.authPayload as IUser,
organization,
acceptedRoles,
acceptedStatuses,
});
if (
authData.authMode === AUTH_MODE_JWT &&
authData.authPayload instanceof User
) {
const membershipOrg = await validateUserClientForOrganization({
user: authData.authPayload,
organization,
acceptedRoles,
acceptedStatuses,
});
return { organization, membershipOrg };
return { organization, membershipOrg };
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for organization",
});
}
if (
authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
authData.authPayload instanceof ServiceAccount
) {
await validateServiceAccountClientForOrganization({
serviceAccount: authData.authPayload,
organization,
});
return { organization };
}
if (
authData.authMode === AUTH_MODE_SERVICE_TOKEN &&
authData.authPayload instanceof ServiceTokenData
) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for organization",
});
}
if (
authData.authMode === AUTH_MODE_API_KEY &&
authData.authPayload instanceof User
) {
const membershipOrg = await validateUserClientForOrganization({
user: authData.authPayload,
organization,
acceptedRoles,
acceptedStatuses,
});
return { organization, membershipOrg };
}
throw UnauthorizedRequestError({
message: "Failed client authorization for organization",
});
};

View File

@@ -2,25 +2,17 @@ import { Types } from "mongoose";
import {
ISecret,
Secret,
ServiceAccount,
ServiceTokenData,
User,
IUser,
IServiceTokenData,
} from "../models";
import { validateServiceAccountClientForSecrets, validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { validateUserClientForSecret, validateUserClientForSecrets } from "./user";
import { validateServiceTokenDataClientForSecrets, validateServiceTokenDataClientForWorkspace } from "./serviceTokenData";
import { AuthData } from "../interfaces/middleware";
import {
BadRequestError,
SecretNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for secrets with id [secretId] based
@@ -47,53 +39,26 @@ export const validateClientForSecret = async ({
if (!secret) throw SecretNotFoundError({
message: "Failed to find secret",
});
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForSecret({
user: authData.authPayload as IUser,
secret,
acceptedRoles,
requiredPermissions,
});
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForSecret({
user: authData.authPayload,
secret,
acceptedRoles,
requiredPermissions,
});
return secret;
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: secret.workspace,
environment: secret.environment,
requiredPermissions,
});
return secret;
case ActorType.SERVICE:
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload as IServiceTokenData,
workspaceId: secret.workspace,
environment: secret.environment,
});
return secret;
return secret;
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload,
workspaceId: secret.workspace,
environment: secret.environment,
});
return secret;
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForSecret({
user: authData.authPayload,
secret,
acceptedRoles,
requiredPermissions,
});
return secret;
}
throw UnauthorizedRequestError({
message: "Failed client authorization for secret",
});
}
/**
@@ -127,48 +92,23 @@ export const validateClientForSecrets = async ({
if (secrets.length != secretIds.length) {
throw BadRequestError({ message: "Failed to validate non-existent secrets" })
}
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForSecrets({
user: authData.authPayload,
secrets,
requiredPermissions,
});
return secrets;
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForSecrets({
serviceAccount: authData.authPayload,
secrets,
requiredPermissions,
});
return secrets;
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForSecrets({
user: authData.authPayload as IUser,
secrets,
requiredPermissions,
});
return secrets;
case ActorType.SERVICE:
await validateServiceTokenDataClientForSecrets({
serviceTokenData: authData.authPayload as IServiceTokenData,
secrets,
requiredPermissions,
});
return secrets;
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
await validateServiceTokenDataClientForSecrets({
serviceTokenData: authData.authPayload,
secrets,
requiredPermissions,
});
return secrets;
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForSecrets({
user: authData.authPayload,
secrets,
requiredPermissions,
});
return secrets;
}
throw UnauthorizedRequestError({
message: "Failed client authorization for secrets resource",
});
}

View File

@@ -4,12 +4,9 @@ import {
IOrganization,
ISecret,
IServiceAccount,
IServiceTokenData,
IUser,
ServiceAccount,
ServiceAccountWorkspacePermission,
ServiceTokenData,
User,
} from "../models";
import { validateUserClientForServiceAccount } from "./user";
import {
@@ -18,23 +15,18 @@ import {
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
export const validateClientForServiceAccount = async ({
authData,
serviceAccountId,
requiredPermissions,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
},
authData: AuthData;
serviceAccountId: Types.ObjectId;
requiredPermissions?: string[];
}) => {
@@ -46,45 +38,20 @@ export const validateClientForServiceAccount = async ({
});
}
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForServiceAccount({
user: authData.authPayload,
serviceAccount,
requiredPermissions,
});
return serviceAccount;
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForServiceAccount({
user: authData.authPayload as IUser,
serviceAccount,
requiredPermissions,
});
return serviceAccount;
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for service account resource",
});
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForServiceAccount({
serviceAccount: authData.authPayload,
targetServiceAccount: serviceAccount,
requiredPermissions,
});
return serviceAccount;
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for service account resource",
});
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForServiceAccount({
user: authData.authPayload,
serviceAccount,
requiredPermissions,
});
return serviceAccount;
}
throw UnauthorizedRequestError({
message: "Failed client authorization for service account resource",
});
}
/**

View File

@@ -1,22 +1,14 @@
import { Types } from "mongoose";
import {
ISecret,
IServiceAccount,
IServiceTokenData,
IUser,
ServiceAccount,
ServiceTokenData,
User
} from "../models";
import { ServiceTokenDataNotFoundError, UnauthorizedRequestError } from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN
} from "../variables";
import { validateUserClientForWorkspace } from "./user";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { ActorType } from "../ee/models";
import { AuthData } from "../interfaces/middleware";
/**
* Validate authenticated clients for service token with id [serviceTokenId] based
@@ -31,10 +23,7 @@ export const validateClientForServiceTokenData = async ({
serviceTokenDataId,
acceptedRoles
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
serviceTokenDataId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
}) => {
@@ -42,55 +31,24 @@ export const validateClientForServiceTokenData = async ({
.select("+encryptedKey +iv +tag")
.populate<{ user: IUser }>("user");
if (!serviceTokenData)
throw ServiceTokenDataNotFoundError({
message: "Failed to find service token data"
});
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: serviceTokenData.workspace,
acceptedRoles
});
return serviceTokenData;
}
if (
authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
authData.authPayload instanceof ServiceAccount
) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: serviceTokenData.workspace
});
return serviceTokenData;
}
if (
authData.authMode === AUTH_MODE_SERVICE_TOKEN &&
authData.authPayload instanceof ServiceTokenData
) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for service token data"
});
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: serviceTokenData.workspace,
acceptedRoles
});
return serviceTokenData;
}
throw UnauthorizedRequestError({
message: "Failed client authorization for service token data"
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({
message: "Failed to find service token data"
});
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: serviceTokenData.workspace,
acceptedRoles
});
return serviceTokenData;
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for service token data"
});
}
};
/**

View File

@@ -2,15 +2,14 @@ import net from "net";
import { Types } from "mongoose";
import {
SecretBlindIndexData,
ServiceAccount,
ServiceTokenData,
User,
IServiceTokenData,
IUser,
Workspace,
} from "../models";
import {
ActorType,
TrustedIP
} from "../ee/models";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { validateUserClientForWorkspace } from "./user";
import { validateServiceTokenDataClientForWorkspace } from "./serviceTokenData";
import {
@@ -18,12 +17,6 @@ import {
UnauthorizedRequestError,
WorkspaceNotFoundError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { BotService } from "../services";
import { AuthData } from "../interfaces/middleware";
import { extractIPDetails } from "../utils/ip";
@@ -85,89 +78,59 @@ export const validateClientForWorkspace = async ({
});
}
switch (authData.actor.type) {
case ActorType.USER:
const membership = await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId,
environment,
acceptedRoles,
requiredPermissions,
});
return ({ membership, workspace });
case ActorType.SERVICE:
if (checkIPAllowlist) {
const trustedIps = await TrustedIP.find({
workspace: workspaceId
});
if (trustedIps.length > 0) {
// case: check the IP address of the inbound request against trusted IPs
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
const membership = await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId,
environment,
acceptedRoles,
requiredPermissions,
});
const blockList = new net.BlockList();
return ({ membership, workspace });
}
for (const trustedIp of trustedIps) {
if (trustedIp.prefix !== undefined) {
blockList.addSubnet(
trustedIp.ipAddress,
trustedIp.prefix,
trustedIp.type
);
} else {
blockList.addAddress(
trustedIp.ipAddress,
trustedIp.type
);
}
}
const { type } = extractIPDetails(authData.ipAddress);
const check = blockList.check(authData.ipAddress, type);
if (!check) throw UnauthorizedRequestError({
message: "Failed workspace authorization"
});
}
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId,
environment,
requiredPermissions,
});
return {};
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
if (checkIPAllowlist) {
const trustedIps = await TrustedIP.find({
workspace: workspaceId
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload as IServiceTokenData,
workspaceId,
environment,
requiredPermissions,
});
if (trustedIps.length > 0) {
// case: check the IP address of the inbound request against trusted IPs
const blockList = new net.BlockList();
for (const trustedIp of trustedIps) {
if (trustedIp.prefix !== undefined) {
blockList.addSubnet(
trustedIp.ipAddress,
trustedIp.prefix,
trustedIp.type
);
} else {
blockList.addAddress(
trustedIp.ipAddress,
trustedIp.type
);
}
}
const { type } = extractIPDetails(authData.authIP);
const check = blockList.check(authData.authIP, type);
if (!check) throw UnauthorizedRequestError({
message: "Failed workspace authorization"
});
}
}
await validateServiceTokenDataClientForWorkspace({
serviceTokenData: authData.authPayload,
workspaceId,
environment,
requiredPermissions,
});
return {};
return {};
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
const membership = await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId,
environment,
acceptedRoles,
requiredPermissions,
});
return ({ membership, workspace });
}
throw UnauthorizedRequestError({
message: "Failed client authorization for workspace",
});
}

View File

@@ -1,4 +1,5 @@
export const AUTH_MODE_JWT = "jwt";
export const AUTH_MODE_SERVICE_ACCOUNT = "serviceAccount";
export const AUTH_MODE_SERVICE_TOKEN = "serviceToken";
export const AUTH_MODE_API_KEY = "apiKey"; // TODO: deprecate
export enum AuthMode {
JWT = "jwt",
SERVICE_TOKEN = "serviceToken",
API_KEY = "apiKey"
}

View File

@@ -1,3 +1,4 @@
// TODO: deprecate in favor of new audit logs
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import Image from "next/image";

View File

@@ -1,3 +1,5 @@
// TODO: deprecate in favor of new audit logs
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
import React, { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -0,0 +1,16 @@
import { EventType, UserAgentType } from "./enums";
export const eventToNameMap: { [K in EventType]: string } = {
[EventType.GET_SECRETS]: "Get Secrets",
[EventType.GET_SECRET]: "Get Secret",
[EventType.CREATE_SECRET]: "Create Secret",
[EventType.UPDATE_SECRET]: "Update Secret",
[EventType.DELETE_SECRET]: "Delete Secret",
};
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
[UserAgentType.WEB]: "Web",
[UserAgentType.CLI]: "CLI",
[UserAgentType.K8_OPERATOR]: "K8s operator",
[UserAgentType.OTHER]: "Other",
};

View File

@@ -0,0 +1,19 @@
export enum ActorType {
USER = "user",
SERVICE = "service"
}
export enum UserAgentType {
WEB = "web",
CLI = "cli",
K8_OPERATOR = "k8-operator",
OTHER = "other"
}
export enum EventType {
GET_SECRETS = "get-secrets",
GET_SECRET = "get-secret",
CREATE_SECRET = "create-secret",
UPDATE_SECRET = "update-secret",
DELETE_SECRET = "delete-secret"
}

View File

@@ -0,0 +1 @@
export * from "./queries";

View File

@@ -0,0 +1,53 @@
import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import {
AuditLog,
Actor
} from "./types";
import { EventType, UserAgentType } from "./enums";
export const workspaceKeys = {
getAuditLogs: (workspaceId: string, filters: {
eventType?: EventType;
userAgentType?: UserAgentType;
actor?: string;
}) => [{ workspaceId, filters }, "audit-logs"] as const,
getAuditLogActorFilterOpts: (workspaceId: string) => [{ workspaceId }, "audit-log-actor-filters"] as const
}
export const useGetAuditLogs = (workspaceId: string, filters: {
eventType?: EventType;
userAgentType?: UserAgentType;
actor?: string;
}) => {
return useQuery({
queryKey: workspaceKeys.getAuditLogs(workspaceId, filters),
queryFn: async () => {
const params = new URLSearchParams();
if (filters.eventType) {
params.append("eventType", filters.eventType);
}
if (filters.userAgentType) {
params.append("userAgentType", filters.userAgentType);
}
if (filters.actor) {
params.append("actor", filters.actor);
}
const { data } = await apiRequest.get<{ auditLogs: AuditLog[] }>(`/api/v1/workspace/${workspaceId}/audit-logs`, { params });
return data.auditLogs;
}
});
}
export const useGetAuditLogActorFilterOpts = (workspaceId: string) => {
return useQuery({
queryKey: workspaceKeys.getAuditLogActorFilterOpts(workspaceId),
queryFn: async () => {
const { data } = await apiRequest.get<{ actors: Actor[] }>(`/api/v1/workspace/${workspaceId}/audit-logs/filters/actors`);
return data.actors;
}
});
}

View File

@@ -0,0 +1,103 @@
import {
ActorType,
EventType,
UserAgentType
} from "./enums";
interface UserActorMetadata {
userId: string;
email: string;
}
interface ServiceActorMetadata {
serviceId: string;
name: string;
}
interface UserActor {
type: ActorType.USER;
metadata: UserActorMetadata;
}
export interface ServiceActor {
type: ActorType.SERVICE;
metadata: ServiceActorMetadata;
}
export type Actor =
| UserActor
| ServiceActor;
interface GetSecretsEvent {
type: EventType.GET_SECRETS;
metadata: {
environment: string;
secretPath: string;
numberOfSecrets: number;
};
}
interface GetSecretEvent {
type: EventType.GET_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
};
}
interface CreateSecretEvent {
type: EventType.CREATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
interface UpdateSecretEvent {
type: EventType.UPDATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
interface DeleteSecretEvent {
type: EventType.DELETE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
| CreateSecretEvent
| UpdateSecretEvent
| DeleteSecretEvent;
export type AuditLog = {
_id: string;
actor: Actor;
organization: string;
workspace: string;
ipAddress: string;
event: Event;
userAgent: string;
userAgentType: UserAgentType;
createdAt: string;
updatedAt: string;
}

View File

@@ -15,6 +15,7 @@ export * from "./ssoConfig";
export * from "./subscriptions";
export * from "./tags";
export * from "./trustedIps";
export * from "./auditLogs";
export * from "./users";
export * from "./webhooks";
export * from "./workspace";

View File

@@ -3,6 +3,7 @@ export type SubscriptionPlan = {
membersUsed: number;
memberLimit: number;
auditLogs: boolean;
auditLogsRetentionDays: number;
customAlerts: boolean;
customRateLimits: boolean;
pitRecovery: boolean;

View File

@@ -28,7 +28,8 @@ export const workspaceKeys = {
getWorkspaceAuthorization: (workspaceId: string) => [{ workspaceId }, "workspace-authorizations"],
getWorkspaceIntegrations: (workspaceId: string) => [{ workspaceId }, "workspace-integrations"],
getAllUserWorkspace: ["workspaces"] as const,
getUserWsEnvironments: (workspaceId: string) => ["workspace-env", { workspaceId }] as const
getUserWsEnvironments: (workspaceId: string) => ["workspace-env", { workspaceId }] as const,
getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const
};
const fetchWorkspaceById = async (workspaceId: string) => {
@@ -259,3 +260,4 @@ export const useDeleteWsEnvironment = () => {
}
});
};

View File

@@ -53,4 +53,4 @@ export type UpdateEnvironmentDTO = {
environmentName: string;
};
export type DeleteEnvironmentDTO = { workspaceID: string; environmentSlug: string };
export type DeleteEnvironmentDTO = { workspaceID: string; environmentSlug: string };

View File

@@ -483,6 +483,18 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem>
</a>
</Link>
<Link href={`/project/${currentWorkspace?._id}/logs`} passHref>
<a>
<MenuItem
isSelected={
router.asPath === `/project/${currentWorkspace?._id}/logs`
}
icon="system-outline-168-view-headline"
>
Audit Logs V2
</MenuItem>
</a>
</Link>
<Link href={`/project/${currentWorkspace?._id}/audit-logs`} passHref>
<a>
<MenuItem

View File

@@ -10,7 +10,7 @@ import {
Input,
Select,
SelectItem
} from "../../../components/v2";
} from "@app/components/v2";
import {
useGetIntegrationAuthApps,
useGetIntegrationAuthById

View File

@@ -0,0 +1,23 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import { LogsPage } from "@app/views/Project/LogsPage";
const Logs = () => {
const { t } = useTranslation();
return (
<div className="h-full bg-bunker-800">
<Head>
<title>{t("common.head-title", { title: t("billing.title") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<LogsPage />
</div>
);
}
export default Logs;
Logs.requireAuth = true;

View File

@@ -0,0 +1,17 @@
import {
LogsSection
} from "./components";
export const LogsPage = () => {
return (
<div className="flex justify-center bg-bunker-800 text-white w-full h-full">
<div className="max-w-7xl px-6 w-full">
<div className="my-6">
<p className="text-3xl font-semibold text-gray-200">Audit Logs</p>
<div />
</div>
<LogsSection />
</div>
</div>
);
}

View File

@@ -0,0 +1,147 @@
import { Control, Controller, UseFormReset } from "react-hook-form";
import {
FormControl,
Select,
SelectItem,
Button
} from "@app/components/v2";
import { eventToNameMap, userAgentTTypeoNameMap } from "~/hooks/api/auditLogs/constants";
import { useWorkspace } from "@app/context";
import { useGetAuditLogActorFilterOpts } from "@app/hooks/api";
import { Actor } from "~/hooks/api/auditLogs/types";
import { ActorType } from "~/hooks/api/auditLogs/enums";
import { faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AuditLogFilterFormData } from "./LogsSection";
const eventTypes = Object.entries(eventToNameMap).map(([value, label]) => ({ label, value }));
const userAgentTypes = Object.entries(userAgentTTypeoNameMap).map(([value, label]) => ({ label, value }));
type Props = {
control: Control<AuditLogFilterFormData>;
reset: UseFormReset<AuditLogFilterFormData>;
}
export const LogsFilter = ({
control,
reset
}: Props) => {
const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useGetAuditLogActorFilterOpts(currentWorkspace?._id ?? "");
const renderActorSelectItem = (actor: Actor) => {
switch (actor.type) {
case ActorType.USER:
return (
<SelectItem value={`${actor.type}-${actor.metadata.userId}`} key={`user-actor-filter-${actor.metadata.userId}`}>
{actor.metadata.email}
</SelectItem>
);
case ActorType.SERVICE:
return (
<SelectItem value={`${actor.type}-${actor.metadata.serviceId}`} key={`service-actor-filter-${actor.metadata.serviceId}`}>
{actor.metadata.name}
</SelectItem>
);
}
}
return (
<div className="flex justify-between items-center">
<div className="flex items-center">
<div className="w-40 mr-4">
<Controller
control={control}
name="eventType"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Event"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{eventTypes.map(({ label, value }) => (
<SelectItem value={String(value || "")} key={label}>
{label}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
</div>
{!isLoading && data && data.length > 0 && (
<div className="w-40 mr-4">
<Controller
control={control}
name="actor"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Actor"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{data.map((actor) => renderActorSelectItem(actor))}
</Select>
</FormControl>
)}
/>
</div>
)}
<div className="w-40">
<Controller
control={control}
name="userAgentType"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Source"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{userAgentTypes.map(({ label, value }) => (
<SelectItem value={String(value || "")} key={label}>
{label}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
</div>
</div>
<div>
<Button
isLoading={false}
colorSchema="primary"
variant="outline_bg"
type="submit"
leftIcon={<FontAwesomeIcon icon={faFilterCircleXmark} className="mr-2" />}
onClick={() => reset({
eventType: "",
actor: "",
userAgentType: ""
})}
>
Clear filters
</Button>
</div>
</div>
);
}

View File

@@ -0,0 +1,49 @@
import { useForm } from "react-hook-form";
import { LogsFilter } from "./LogsFilter";
import { LogsTable } from "./LogsTable";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { EventType, UserAgentType } from "~/hooks/api/auditLogs/enums";
const schema = yup.object({
eventType: yup.string()
.oneOf(Object.values(EventType), 'Invalid event type'),
actor: yup.string(),
userAgentType: yup.string()
.oneOf(Object.values(UserAgentType), 'Invalid user agent type'),
}).required();
export type AuditLogFilterFormData = yup.InferType<typeof schema>;
export const LogsSection = () => {
const {
control,
reset,
watch,
} = useForm<AuditLogFilterFormData>({
resolver: yupResolver(schema)
});
const eventType = watch("eventType") as EventType | undefined;
const userAgentType = watch("userAgentType") as UserAgentType | undefined;
const actor = watch("actor") as string | undefined;
return (
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
<div className="flex items-center mb-8">
<h2 className="text-xl font-semibold flex-1 text-white">
Audit Logs
</h2>
</div>
<LogsFilter
control={control}
reset={reset}
/>
<LogsTable
eventType={eventType}
userAgentType={userAgentType}
actor={actor}
/>
</div>
);
}

View File

@@ -0,0 +1,70 @@
import { faFile } from "@fortawesome/free-solid-svg-icons";
import {
EmptyState,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
THead,
Tr
} from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { useGetAuditLogs } from "@app/hooks/api";
import { LogsTableRow } from "./LogsTableRow";
import { EventType, UserAgentType } from "~/hooks/api/auditLogs/enums";
type Props = {
eventType: EventType | undefined;
userAgentType: UserAgentType | undefined;
actor: string | undefined;
}
export const LogsTable = ({
eventType,
userAgentType,
actor
}: Props) => {
const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useGetAuditLogs(currentWorkspace?._id ?? "", {
eventType,
userAgentType,
actor
});
return (
<TableContainer>
<Table>
<THead>
<Tr>
<Th>Timestamp</Th>
<Th>Event</Th>
<Th>Actor</Th>
<Th>Source</Th>
<Th>Metadata</Th>
</Tr>
</THead>
<TBody>
{!isLoading && data && data.map((auditLog) => (
<LogsTableRow
auditLog={auditLog}
key={`audit-log-${auditLog._id}`}
/>
))}
{isLoading && <TableSkeleton innerKey="logs-table" columns={5} key="logs" />}
{!isLoading && data && data.length === 0 && (
<Tr>
<Td colSpan={5}>
<EmptyState
title="No audit logs on file"
icon={faFile}
/>
</Td>
</Tr>
)}
</TBody>
</Table>
</TableContainer>
);
}

View File

@@ -0,0 +1,114 @@
import { AuditLog, Actor, Event } from "~/hooks/api/auditLogs/types";
import { ActorType, EventType } from "~/hooks/api/auditLogs/enums";
import { eventToNameMap, userAgentTTypeoNameMap } from "~/hooks/api/auditLogs/constants";
import {
Td,
Tr
} from "@app/components/v2";
type Props = {
auditLog: AuditLog
}
export const LogsTableRow = ({
auditLog
}: Props) => {
const renderActor = (actor: Actor) => {
switch (actor.type) {
case ActorType.USER:
return (
<Td>
<p>{actor.metadata.email}</p>
<p>User</p>
</Td>
);
case ActorType.SERVICE:
return (
<Td>
<p>{`${actor.metadata.name}`}</p>
<p>Service token</p>
</Td>
);
}
}
const renderMetadata = (event: Event) => {
switch (event.type) {
case EventType.GET_SECRETS:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Path: ${event.metadata.secretPath}`}</p>
<p>{`# Secrets: ${event.metadata.numberOfSecrets}`}</p>
</Td>
);
case EventType.GET_SECRET:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Path: ${event.metadata.secretPath}`}</p>
<p>{`Secret: ${event.metadata.secretKey}`}</p>
</Td>
);
case EventType.CREATE_SECRET:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Path: ${event.metadata.secretPath}`}</p>
<p>{`Secret: ${event.metadata.secretKey}`}</p>
</Td>
);
case EventType.UPDATE_SECRET:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Path: ${event.metadata.secretPath}`}</p>
<p>{`Secret: ${event.metadata.secretKey}`}</p>
</Td>
);
case EventType.DELETE_SECRET:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Path: ${event.metadata.secretPath}`}</p>
<p>{`Secret: ${event.metadata.secretKey}`}</p>
</Td>
);
default:
return (
<Td>Test</Td>
);
}
}
const formatDate = (dateToFormat: string) => {
const date = new Date(dateToFormat);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
let hours = date.getHours();
const minutes = String(date.getMinutes()).padStart(2, '0');
// convert from 24h to 12h format
const period = hours >= 12 ? 'PM' : 'AM';
hours = hours % 12;
hours = hours ? hours : 12; // the hour '0' should be '12'
const formattedDate = `${day}-${month}-${year} at ${hours}:${minutes} ${period}`;
return formattedDate;
}
return (
<Tr className={`log-${auditLog._id} h-10`}>
<Td>{formatDate(auditLog.createdAt)}</Td>
<Td>{`${eventToNameMap[auditLog.event.type]}`}</Td>
{renderActor(auditLog.actor)}
<Td>
<p>{userAgentTTypeoNameMap[auditLog.userAgentType]}</p>
<p>{auditLog.ipAddress}</p>
</Td>
{renderMetadata(auditLog.event)}
</Tr>
);
}

View File

@@ -0,0 +1 @@
export { LogsSection } from "./LogsSection";

View File

@@ -0,0 +1 @@
export { LogsPage } from "./LogsPage";