mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 10:35:17 +00:00
Fix merge conflicts, bring oidc auth up to speed with identity ui changes
This commit is contained in:
@@ -25,10 +25,10 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
.optional()
|
||||
.nullable()
|
||||
.transform((val) => (val ? removeTrailingSlash(val) : val)),
|
||||
approvers: z.string().array().min(1),
|
||||
approverUserIds: z.string().array().min(1),
|
||||
approvals: z.number().min(1).default(1)
|
||||
})
|
||||
.refine((data) => data.approvals <= data.approvers.length, {
|
||||
.refine((data) => data.approvals <= data.approverUserIds.length, {
|
||||
path: ["approvals"],
|
||||
message: "The number of approvals should be lower than the number of approvers."
|
||||
}),
|
||||
@@ -66,7 +66,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
body: z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
approvers: z.string().array().min(1),
|
||||
approverUserIds: z.string().array().min(1),
|
||||
approvals: z.number().min(1).default(1),
|
||||
secretPath: z
|
||||
.string()
|
||||
@@ -74,7 +74,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
.nullable()
|
||||
.transform((val) => (val ? removeTrailingSlash(val) : val))
|
||||
})
|
||||
.refine((data) => data.approvals <= data.approvers.length, {
|
||||
.refine((data) => data.approvals <= data.approverUserIds.length, {
|
||||
path: ["approvals"],
|
||||
message: "The number of approvals should be lower than the number of approvers."
|
||||
}),
|
||||
@@ -139,7 +139,15 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array()
|
||||
approvals: sapPubSchema
|
||||
.extend({
|
||||
userApprovers: z
|
||||
.object({
|
||||
userId: z.string()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -170,7 +178,11 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional()
|
||||
policy: sapPubSchema
|
||||
.extend({
|
||||
userApprovers: z.object({ userId: z.string() }).array()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
SecretApprovalRequestsSecretsSchema,
|
||||
SecretsSchema,
|
||||
SecretTagsSchema,
|
||||
SecretVersionsSchema
|
||||
SecretVersionsSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||
@@ -14,6 +15,15 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
const approvalRequestUser = z.object({ userId: z.string() }).merge(
|
||||
UsersSchema.pick({
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
username: true
|
||||
})
|
||||
);
|
||||
|
||||
export const registerSecretApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
@@ -41,9 +51,10 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
approvers: z.string().array(),
|
||||
secretPath: z.string().optional().nullable()
|
||||
}),
|
||||
committerUser: approvalRequestUser,
|
||||
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
||||
environment: z.string(),
|
||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
||||
reviewers: z.object({ userId: z.string(), status: z.string() }).array(),
|
||||
approvers: z.string().array()
|
||||
}).array()
|
||||
})
|
||||
@@ -195,7 +206,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
|
||||
// eslint-disable-next-line
|
||||
metadata: {
|
||||
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeBy as string,
|
||||
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangedByUserId as string,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
// eslint-disable-next-line
|
||||
@@ -216,6 +227,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
})
|
||||
.array()
|
||||
.optional();
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:id",
|
||||
@@ -235,12 +247,13 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
approvals: z.number(),
|
||||
approvers: z.string().array(),
|
||||
approvers: approvalRequestUser.array(),
|
||||
secretPath: z.string().optional().nullable()
|
||||
}),
|
||||
environment: z.string(),
|
||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
||||
approvers: z.string().array(),
|
||||
statusChangedByUser: approvalRequestUser.optional(),
|
||||
committerUser: approvalRequestUser,
|
||||
reviewers: approvalRequestUser.extend({ status: z.string() }).array(),
|
||||
secretPath: z.string(),
|
||||
commits: SecretApprovalRequestsSecretsSchema.omit({ secretBlindIndex: true })
|
||||
.merge(
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, stripUndefinedInWhere } from "@app/lib/knex";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
|
||||
|
||||
@@ -55,13 +56,34 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
||||
|
||||
// delete all audit log that have expired
|
||||
const pruneAuditLog = async (tx?: Knex) => {
|
||||
try {
|
||||
const today = new Date();
|
||||
const docs = await (tx || db)(TableName.AuditLog).where("expiresAt", "<", today).del();
|
||||
return docs;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "PruneAuditLog" });
|
||||
}
|
||||
const AUDIT_LOG_PRUNE_BATCH_SIZE = 10000;
|
||||
const MAX_RETRY_ON_FAILURE = 3;
|
||||
|
||||
const today = new Date();
|
||||
let deletedAuditLogIds: { id: string }[] = [];
|
||||
let numberOfRetryOnFailure = 0;
|
||||
|
||||
do {
|
||||
try {
|
||||
const findExpiredLogSubQuery = (tx || db)(TableName.AuditLog)
|
||||
.where("expiresAt", "<", today)
|
||||
.select("id")
|
||||
.limit(AUDIT_LOG_PRUNE_BATCH_SIZE);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
deletedAuditLogIds = await (tx || db)(TableName.AuditLog)
|
||||
.whereIn("id", findExpiredLogSubQuery)
|
||||
.del()
|
||||
.returning("id");
|
||||
numberOfRetryOnFailure = 0; // reset
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 100); // time to breathe for db
|
||||
});
|
||||
} catch (error) {
|
||||
numberOfRetryOnFailure += 1;
|
||||
logger.error(error, "Failed to delete audit log on pruning");
|
||||
}
|
||||
} while (deletedAuditLogIds.length > 0 && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE);
|
||||
};
|
||||
|
||||
return { ...auditLogOrm, pruneAuditLog, find };
|
||||
|
||||
@@ -66,6 +66,13 @@ export enum EventType {
|
||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
||||
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
||||
REVOKE_IDENTITY_UNIVERSAL_AUTH = "revoke-identity-universal-auth",
|
||||
CREATE_TOKEN_IDENTITY_TOKEN_AUTH = "create-token-identity-token-auth",
|
||||
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
|
||||
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
|
||||
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
|
||||
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
|
||||
GET_IDENTITY_TOKEN_AUTH = "get-identity-token-auth",
|
||||
REVOKE_IDENTITY_TOKEN_AUTH = "revoke-identity-token-auth",
|
||||
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
|
||||
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
|
||||
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
|
||||
@@ -452,6 +459,66 @@ interface DeleteIdentityUniversalAuthEvent {
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateTokenIdentityTokenAuthEvent {
|
||||
type: EventType.CREATE_TOKEN_IDENTITY_TOKEN_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
identityAccessTokenId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateTokenIdentityTokenAuthEvent {
|
||||
type: EventType.UPDATE_TOKEN_IDENTITY_TOKEN_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
tokenId: string;
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetTokensIdentityTokenAuthEvent {
|
||||
type: EventType.GET_TOKENS_IDENTITY_TOKEN_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AddIdentityTokenAuthEvent {
|
||||
type: EventType.ADD_IDENTITY_TOKEN_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateIdentityTokenAuthEvent {
|
||||
type: EventType.UPDATE_IDENTITY_TOKEN_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
accessTokenTTL?: number;
|
||||
accessTokenMaxTTL?: number;
|
||||
accessTokenNumUsesLimit?: number;
|
||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetIdentityTokenAuthEvent {
|
||||
type: EventType.GET_IDENTITY_TOKEN_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteIdentityTokenAuthEvent {
|
||||
type: EventType.REVOKE_IDENTITY_TOKEN_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface LoginIdentityKubernetesAuthEvent {
|
||||
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH;
|
||||
metadata: {
|
||||
@@ -833,7 +900,6 @@ interface CreateWebhookEvent {
|
||||
webhookId: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
webhookUrl: string;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
}
|
||||
@@ -844,7 +910,6 @@ interface UpdateWebhookStatusEvent {
|
||||
webhookId: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
webhookUrl: string;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
}
|
||||
@@ -855,7 +920,6 @@ interface DeleteWebhookEvent {
|
||||
webhookId: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
webhookUrl: string;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
}
|
||||
@@ -1116,6 +1180,13 @@ export type Event =
|
||||
| UpdateIdentityUniversalAuthEvent
|
||||
| DeleteIdentityUniversalAuthEvent
|
||||
| GetIdentityUniversalAuthEvent
|
||||
| CreateTokenIdentityTokenAuthEvent
|
||||
| UpdateTokenIdentityTokenAuthEvent
|
||||
| GetTokensIdentityTokenAuthEvent
|
||||
| AddIdentityTokenAuthEvent
|
||||
| UpdateIdentityTokenAuthEvent
|
||||
| GetIdentityTokenAuthEvent
|
||||
| DeleteIdentityTokenAuthEvent
|
||||
| LoginIdentityKubernetesAuthEvent
|
||||
| DeleteIdentityKubernetesAuthEvent
|
||||
| AddIdentityKubernetesAuthEvent
|
||||
|
||||
@@ -3,7 +3,8 @@ import { z } from "zod";
|
||||
export enum SqlProviders {
|
||||
Postgres = "postgres",
|
||||
MySQL = "mysql2",
|
||||
Oracle = "oracledb"
|
||||
Oracle = "oracledb",
|
||||
MsSQL = "mssql"
|
||||
}
|
||||
|
||||
export const DynamicSecretSqlDBSchema = z.object({
|
||||
|
||||
@@ -34,6 +34,7 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal
|
||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||
@@ -417,6 +418,13 @@ export const ldapConfigServiceFactory = ({
|
||||
}: TLdapLoginDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await getServerCfg();
|
||||
|
||||
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.LDAP)) {
|
||||
throw new BadRequestError({
|
||||
message: "Login with LDAP is disabled by administrator."
|
||||
});
|
||||
}
|
||||
|
||||
let userAlias = await userAliasDAL.findOne({
|
||||
externalId,
|
||||
orgId,
|
||||
@@ -473,7 +481,7 @@ export const ldapConfigServiceFactory = ({
|
||||
|
||||
userAlias = await userDAL.transaction(async (tx) => {
|
||||
let newUser: TUsers | undefined;
|
||||
if (serverCfg.trustSamlEmails) {
|
||||
if (serverCfg.trustLdapEmails) {
|
||||
newUser = await userDAL.findOne(
|
||||
{
|
||||
email,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// TODO(akhilmhdh): With tony find out the api structure and fill it here
|
||||
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -200,13 +201,13 @@ export const licenseServiceFactory = ({
|
||||
await licenseServerCloudApi.request.delete(`/api/license-server/v1/customers/${customerId}`);
|
||||
};
|
||||
|
||||
const updateSubscriptionOrgMemberCount = async (orgId: string) => {
|
||||
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
|
||||
if (instanceType === InstanceType.Cloud) {
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
if (!org) throw new BadRequestError({ message: "Org not found" });
|
||||
|
||||
const quantity = await licenseDAL.countOfOrgMembers(orgId);
|
||||
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(orgId);
|
||||
const quantity = await licenseDAL.countOfOrgMembers(orgId, tx);
|
||||
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(orgId, tx);
|
||||
if (org?.customerId) {
|
||||
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
|
||||
quantity,
|
||||
@@ -215,8 +216,8 @@ export const licenseServiceFactory = ({
|
||||
}
|
||||
await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId));
|
||||
} else if (instanceType === InstanceType.EnterpriseOnPrem) {
|
||||
const usedSeats = await licenseDAL.countOfOrgMembers(null);
|
||||
const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null);
|
||||
const usedSeats = await licenseDAL.countOfOrgMembers(null, tx);
|
||||
const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null, tx);
|
||||
await licenseServerOnPremApi.request.patch(`/api/license/v1/license`, {
|
||||
usedSeats,
|
||||
usedIdentitySeats
|
||||
|
||||
@@ -26,6 +26,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||
@@ -157,6 +158,13 @@ export const oidcConfigServiceFactory = ({
|
||||
|
||||
const oidcLogin = async ({ externalId, email, firstName, lastName, orgId, callbackPort }: TOidcLoginDTO) => {
|
||||
const serverCfg = await getServerCfg();
|
||||
|
||||
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.OIDC)) {
|
||||
throw new BadRequestError({
|
||||
message: "Login with OIDC is disabled by administrator."
|
||||
});
|
||||
}
|
||||
|
||||
const appCfg = getConfig();
|
||||
const userAlias = await userAliasDAL.findOne({
|
||||
externalId,
|
||||
|
||||
@@ -28,6 +28,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||
@@ -335,6 +336,13 @@ export const samlConfigServiceFactory = ({
|
||||
}: TSamlLoginDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await getServerCfg();
|
||||
|
||||
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.SAML)) {
|
||||
throw new BadRequestError({
|
||||
message: "Login with SAML is disabled by administrator."
|
||||
});
|
||||
}
|
||||
|
||||
const userAlias = await userAliasDAL.findOne({
|
||||
externalId,
|
||||
orgId,
|
||||
|
||||
@@ -1,49 +1,59 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName, TSecretApprovalPolicies } from "@app/db/schemas";
|
||||
import { SecretApprovalPoliciesSchema, TableName, TSecretApprovalPolicies } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex";
|
||||
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
|
||||
|
||||
export type TSecretApprovalPolicyDALFactory = ReturnType<typeof secretApprovalPolicyDALFactory>;
|
||||
|
||||
export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
const secretApprovalPolicyOrm = ormify(db, TableName.SecretApprovalPolicy);
|
||||
|
||||
const sapFindQuery = (tx: Knex, filter: TFindFilter<TSecretApprovalPolicies>) =>
|
||||
const secretApprovalPolicyFindQuery = (tx: Knex, filter: TFindFilter<TSecretApprovalPolicies>) =>
|
||||
tx(TableName.SecretApprovalPolicy)
|
||||
// eslint-disable-next-line
|
||||
.where(buildFindFilter(filter))
|
||||
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||
.join(
|
||||
.leftJoin(
|
||||
TableName.SecretApprovalPolicyApprover,
|
||||
`${TableName.SecretApprovalPolicy}.id`,
|
||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||
)
|
||||
.select(tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover))
|
||||
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
||||
.select(tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover))
|
||||
.select(
|
||||
tx.ref("name").withSchema(TableName.Environment).as("envName"),
|
||||
tx.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||
tx.ref("id").withSchema(TableName.Environment).as("envId"),
|
||||
tx.ref("projectId").withSchema(TableName.Environment)
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretApprovalPolicy))
|
||||
.orderBy("createdAt", "asc");
|
||||
|
||||
const findById = async (id: string, tx?: Knex) => {
|
||||
try {
|
||||
const doc = await sapFindQuery(tx || db.replicaNode(), {
|
||||
const doc = await secretApprovalPolicyFindQuery(tx || db.replicaNode(), {
|
||||
[`${TableName.SecretApprovalPolicy}.id` as "id"]: id
|
||||
});
|
||||
const formatedDoc = mergeOneToManyRelation(
|
||||
doc,
|
||||
"id",
|
||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
...el,
|
||||
envId,
|
||||
environment: { id: envId, name, slug }
|
||||
const formatedDoc = sqlNestRelationships({
|
||||
data: doc,
|
||||
key: "id",
|
||||
parentMapper: (data) => ({
|
||||
environment: { id: data.envId, name: data.envName, slug: data.envSlug },
|
||||
projectId: data.projectId,
|
||||
...SecretApprovalPoliciesSchema.parse(data)
|
||||
}),
|
||||
({ approverId }) => approverId,
|
||||
"approvers"
|
||||
);
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "approverUserId",
|
||||
label: "userApprovers" as const,
|
||||
mapper: ({ approverUserId }) => ({
|
||||
userId: approverUserId
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return formatedDoc?.[0];
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindById" });
|
||||
@@ -52,18 +62,25 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
|
||||
const find = async (filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await sapFindQuery(tx || db.replicaNode(), filter);
|
||||
const formatedDoc = mergeOneToManyRelation(
|
||||
docs,
|
||||
"id",
|
||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
...el,
|
||||
envId,
|
||||
environment: { id: envId, name, slug }
|
||||
const docs = await secretApprovalPolicyFindQuery(tx || db.replicaNode(), filter);
|
||||
const formatedDoc = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: (data) => ({
|
||||
environment: { id: data.envId, name: data.envName, slug: data.envSlug },
|
||||
projectId: data.projectId,
|
||||
...SecretApprovalPoliciesSchema.parse(data)
|
||||
}),
|
||||
({ approverId }) => approverId,
|
||||
"approvers"
|
||||
);
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "approverUserId",
|
||||
label: "userApprovers" as const,
|
||||
mapper: ({ approverUserId }) => ({
|
||||
userId: approverUserId
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
return formatedDoc;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find" });
|
||||
|
||||
@@ -7,7 +7,6 @@ import { BadRequestError } from "@app/lib/errors";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { containsGlobPatterns } from "@app/lib/picomatch";
|
||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
|
||||
import { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
|
||||
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
||||
@@ -29,7 +28,6 @@ type TSecretApprovalPolicyServiceFactoryDep = {
|
||||
secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory;
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
||||
};
|
||||
|
||||
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
||||
@@ -38,8 +36,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
secretApprovalPolicyDAL,
|
||||
permissionService,
|
||||
secretApprovalPolicyApproverDAL,
|
||||
projectEnvDAL,
|
||||
projectMembershipDAL
|
||||
projectEnvDAL
|
||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||
const createSecretApprovalPolicy = async ({
|
||||
name,
|
||||
@@ -48,12 +45,12 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
approvals,
|
||||
approvers,
|
||||
approverUserIds,
|
||||
projectId,
|
||||
secretPath,
|
||||
environment
|
||||
}: TCreateSapDTO) => {
|
||||
if (approvals > approvers.length)
|
||||
if (approvals > approverUserIds.length)
|
||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
@@ -70,13 +67,6 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
|
||||
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
||||
|
||||
const secretApprovers = await projectMembershipDAL.find({
|
||||
projectId,
|
||||
$in: { id: approvers }
|
||||
});
|
||||
if (secretApprovers.length !== approvers.length)
|
||||
throw new BadRequestError({ message: "Approver not found in project" });
|
||||
|
||||
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
||||
const doc = await secretApprovalPolicyDAL.create(
|
||||
{
|
||||
@@ -88,8 +78,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
await secretApprovalPolicyApproverDAL.insertMany(
|
||||
secretApprovers.map(({ id }) => ({
|
||||
approverId: id,
|
||||
approverUserIds.map((approverUserId) => ({
|
||||
approverUserId,
|
||||
policyId: doc.id
|
||||
})),
|
||||
tx
|
||||
@@ -100,7 +90,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
};
|
||||
|
||||
const updateSecretApprovalPolicy = async ({
|
||||
approvers,
|
||||
approverUserIds,
|
||||
secretPath,
|
||||
name,
|
||||
actorId,
|
||||
@@ -132,22 +122,11 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
if (approvers) {
|
||||
const secretApprovers = await projectMembershipDAL.find(
|
||||
{
|
||||
projectId: secretApprovalPolicy.projectId,
|
||||
$in: { id: approvers }
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
if (secretApprovers.length !== approvers.length)
|
||||
throw new BadRequestError({ message: "Approver not found in project" });
|
||||
if (doc.approvals > secretApprovers.length)
|
||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||
if (approverUserIds) {
|
||||
await secretApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
||||
await secretApprovalPolicyApproverDAL.insertMany(
|
||||
secretApprovers.map(({ id }) => ({
|
||||
approverId: id,
|
||||
approverUserIds.map((approverUserId) => ({
|
||||
approverUserId,
|
||||
policyId: doc.id
|
||||
})),
|
||||
tx
|
||||
|
||||
@@ -4,7 +4,7 @@ export type TCreateSapDTO = {
|
||||
approvals: number;
|
||||
secretPath?: string | null;
|
||||
environment: string;
|
||||
approvers: string[];
|
||||
approverUserIds: string[];
|
||||
projectId: string;
|
||||
name: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
@@ -13,7 +13,7 @@ export type TUpdateSapDTO = {
|
||||
secretPolicyId: string;
|
||||
approvals?: number;
|
||||
secretPath?: string | null;
|
||||
approvers: string[];
|
||||
approverUserIds: string[];
|
||||
name?: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
SecretApprovalRequestsSchema,
|
||||
TableName,
|
||||
TSecretApprovalRequests,
|
||||
TSecretApprovalRequestsSecrets
|
||||
TSecretApprovalRequestsSecrets,
|
||||
TUsers
|
||||
} from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols, sqlNestRelationships, stripUndefinedInWhere, TFindFilter } from "@app/lib/knex";
|
||||
@@ -16,7 +17,7 @@ export type TSecretApprovalRequestDALFactory = ReturnType<typeof secretApprovalR
|
||||
|
||||
type TFindQueryFilter = {
|
||||
projectId: string;
|
||||
membershipId: string;
|
||||
userId: string;
|
||||
status?: RequestState;
|
||||
environment?: string;
|
||||
committer?: string;
|
||||
@@ -37,27 +38,63 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
`${TableName.SecretApprovalRequest}.policyId`,
|
||||
`${TableName.SecretApprovalPolicy}.id`
|
||||
)
|
||||
.leftJoin<TUsers>(
|
||||
db(TableName.Users).as("statusChangedByUser"),
|
||||
`${TableName.SecretApprovalRequest}.statusChangedByUserId`,
|
||||
`statusChangedByUser.id`
|
||||
)
|
||||
.join<TUsers>(
|
||||
db(TableName.Users).as("committerUser"),
|
||||
`${TableName.SecretApprovalRequest}.committerUserId`,
|
||||
`committerUser.id`
|
||||
)
|
||||
.join(
|
||||
TableName.SecretApprovalPolicyApprover,
|
||||
`${TableName.SecretApprovalPolicy}.id`,
|
||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||
)
|
||||
.join<TUsers>(
|
||||
db(TableName.Users).as("secretApprovalPolicyApproverUser"),
|
||||
`${TableName.SecretApprovalPolicyApprover}.approverUserId`,
|
||||
"secretApprovalPolicyApproverUser.id"
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.SecretApprovalRequestReviewer,
|
||||
`${TableName.SecretApprovalRequest}.id`,
|
||||
`${TableName.SecretApprovalRequestReviewer}.requestId`
|
||||
)
|
||||
.leftJoin<TUsers>(
|
||||
db(TableName.Users).as("secretApprovalReviewerUser"),
|
||||
`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`,
|
||||
`secretApprovalReviewerUser.id`
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||
.select(
|
||||
tx.ref("member").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
||||
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
|
||||
tx.ref("email").withSchema("secretApprovalPolicyApproverUser").as("approverEmail"),
|
||||
tx.ref("username").withSchema("secretApprovalPolicyApproverUser").as("approverUsername"),
|
||||
tx.ref("firstName").withSchema("secretApprovalPolicyApproverUser").as("approverFirstName"),
|
||||
tx.ref("lastName").withSchema("secretApprovalPolicyApproverUser").as("approverLastName"),
|
||||
tx.ref("email").withSchema("statusChangedByUser").as("statusChangedByUserEmail"),
|
||||
tx.ref("username").withSchema("statusChangedByUser").as("statusChangedByUserUsername"),
|
||||
tx.ref("firstName").withSchema("statusChangedByUser").as("statusChangedByUserFirstName"),
|
||||
tx.ref("lastName").withSchema("statusChangedByUser").as("statusChangedByUserLastName"),
|
||||
tx.ref("email").withSchema("committerUser").as("committerUserEmail"),
|
||||
tx.ref("username").withSchema("committerUser").as("committerUserUsername"),
|
||||
tx.ref("firstName").withSchema("committerUser").as("committerUserFirstName"),
|
||||
tx.ref("lastName").withSchema("committerUser").as("committerUserLastName"),
|
||||
tx.ref("reviewerUserId").withSchema(TableName.SecretApprovalRequestReviewer),
|
||||
tx.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
||||
tx.ref("email").withSchema("secretApprovalReviewerUser").as("reviewerEmail"),
|
||||
tx.ref("username").withSchema("secretApprovalReviewerUser").as("reviewerUsername"),
|
||||
tx.ref("firstName").withSchema("secretApprovalReviewerUser").as("reviewerFirstName"),
|
||||
tx.ref("lastName").withSchema("secretApprovalReviewerUser").as("reviewerLastName"),
|
||||
tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||
tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
||||
tx.ref("projectId").withSchema(TableName.Environment),
|
||||
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||
tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
|
||||
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals")
|
||||
);
|
||||
|
||||
const findById = async (id: string, tx?: Knex) => {
|
||||
@@ -71,6 +108,22 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
...SecretApprovalRequestsSchema.parse(el),
|
||||
projectId: el.projectId,
|
||||
environment: el.environment,
|
||||
statusChangedByUser: el.statusChangedByUserId
|
||||
? {
|
||||
userId: el.statusChangedByUserId,
|
||||
email: el.statusChangedByUserEmail,
|
||||
firstName: el.statusChangedByUserFirstName,
|
||||
lastName: el.statusChangedByUserLastName,
|
||||
username: el.statusChangedByUserUsername
|
||||
}
|
||||
: undefined,
|
||||
committerUser: {
|
||||
userId: el.committerUserId,
|
||||
email: el.committerUserEmail,
|
||||
firstName: el.committerUserFirstName,
|
||||
lastName: el.committerUserLastName,
|
||||
username: el.committerUserUsername
|
||||
},
|
||||
policy: {
|
||||
id: el.policyId,
|
||||
name: el.policyName,
|
||||
@@ -80,11 +133,34 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "reviewerMemberId",
|
||||
key: "reviewerUserId",
|
||||
label: "reviewers" as const,
|
||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
||||
mapper: ({
|
||||
reviewerUserId: userId,
|
||||
reviewerStatus: status,
|
||||
reviewerEmail: email,
|
||||
reviewerLastName: lastName,
|
||||
reviewerUsername: username,
|
||||
reviewerFirstName: firstName
|
||||
}) => (userId ? { userId, status, email, firstName, lastName, username } : undefined)
|
||||
},
|
||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
||||
{
|
||||
key: "approverUserId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({
|
||||
approverUserId,
|
||||
approverEmail: email,
|
||||
approverUsername: username,
|
||||
approverLastName: lastName,
|
||||
approverFirstName: firstName
|
||||
}) => ({
|
||||
userId: approverUserId,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
username
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
if (!formatedDoc?.[0]) return;
|
||||
@@ -97,7 +173,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findProjectRequestCount = async (projectId: string, membershipId: string, tx?: Knex) => {
|
||||
const findProjectRequestCount = async (projectId: string, userId: string, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)
|
||||
.with(
|
||||
@@ -114,8 +190,8 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
.andWhere(
|
||||
(bd) =>
|
||||
void bd
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, userId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, userId)
|
||||
)
|
||||
.select("status", `${TableName.SecretApprovalRequest}.id`)
|
||||
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
|
||||
@@ -142,7 +218,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
const findByProjectId = async (
|
||||
{ status, limit = 20, offset = 0, projectId, committer, environment, membershipId }: TFindQueryFilter,
|
||||
{ status, limit = 20, offset = 0, projectId, committer, environment, userId }: TFindQueryFilter,
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
@@ -161,6 +237,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
`${TableName.SecretApprovalPolicy}.id`,
|
||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||
)
|
||||
.join<TUsers>(
|
||||
db(TableName.Users).as("committerUser"),
|
||||
`${TableName.SecretApprovalRequest}.committerUserId`,
|
||||
`committerUser.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.SecretApprovalRequestReviewer,
|
||||
`${TableName.SecretApprovalRequest}.id`,
|
||||
@@ -176,20 +257,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
projectId,
|
||||
[`${TableName.Environment}.slug` as "slug"]: environment,
|
||||
[`${TableName.SecretApprovalRequest}.status`]: status,
|
||||
committerId: committer
|
||||
committerUserId: committer
|
||||
})
|
||||
)
|
||||
.andWhere(
|
||||
(bd) =>
|
||||
void bd
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, userId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, userId)
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||
.select(
|
||||
db.ref("projectId").withSchema(TableName.Environment),
|
||||
db.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||
db.ref("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
||||
db.ref("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerId"),
|
||||
db.ref("reviewerUserId").withSchema(TableName.SecretApprovalRequestReviewer),
|
||||
db.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
||||
db.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||
db.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
||||
@@ -201,7 +283,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
),
|
||||
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||
db.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
|
||||
db.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
|
||||
db.ref("email").withSchema("committerUser").as("committerUserEmail"),
|
||||
db.ref("username").withSchema("committerUser").as("committerUserUsername"),
|
||||
db.ref("firstName").withSchema("committerUser").as("committerUserFirstName"),
|
||||
db.ref("lastName").withSchema("committerUser").as("committerUserLastName")
|
||||
)
|
||||
.orderBy("createdAt", "desc");
|
||||
|
||||
@@ -223,18 +309,26 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
name: el.policyName,
|
||||
approvals: el.policyApprovals,
|
||||
secretPath: el.policySecretPath
|
||||
},
|
||||
committerUser: {
|
||||
userId: el.committerUserId,
|
||||
email: el.committerUserEmail,
|
||||
firstName: el.committerUserFirstName,
|
||||
lastName: el.committerUserLastName,
|
||||
username: el.committerUserUsername
|
||||
}
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "reviewerMemberId",
|
||||
key: "reviewerId",
|
||||
label: "reviewers" as const,
|
||||
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined)
|
||||
mapper: ({ reviewerUserId, reviewerStatus: s }) =>
|
||||
reviewerUserId ? { userId: reviewerUserId, status: s } : undefined
|
||||
},
|
||||
{
|
||||
key: "approverId",
|
||||
key: "approverUserId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverId }) => approverId
|
||||
mapper: ({ approverUserId }) => approverUserId
|
||||
},
|
||||
{
|
||||
key: "commitId",
|
||||
|
||||
+24
-30
@@ -87,7 +87,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
await permissionService.getProjectPermission(
|
||||
actor as ActorType.USER,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -95,7 +95,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id);
|
||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId);
|
||||
return count;
|
||||
};
|
||||
|
||||
@@ -113,19 +113,13 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
}: TListApprovalsDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
await permissionService.getProjectPermission(actor, actorId, projectId, actorAuthMethod, actorOrgId);
|
||||
const approvals = await secretApprovalRequestDAL.findByProjectId({
|
||||
projectId,
|
||||
committer,
|
||||
environment,
|
||||
status,
|
||||
membershipId: membership.id,
|
||||
userId: actorId,
|
||||
limit,
|
||||
offset
|
||||
});
|
||||
@@ -145,7 +139,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
@@ -154,8 +148,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||
) {
|
||||
throw new UnauthorizedError({ message: "User has no access" });
|
||||
}
|
||||
@@ -180,7 +174,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
@@ -189,8 +183,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||
) {
|
||||
throw new UnauthorizedError({ message: "User has no access" });
|
||||
}
|
||||
@@ -198,7 +192,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const review = await secretApprovalRequestReviewerDAL.findOne(
|
||||
{
|
||||
requestId: secretApprovalRequest.id,
|
||||
member: membership.id
|
||||
reviewerUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -207,7 +201,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
{
|
||||
status,
|
||||
requestId: secretApprovalRequest.id,
|
||||
member: membership.id
|
||||
reviewerUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -230,7 +224,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
@@ -239,8 +233,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||
) {
|
||||
throw new UnauthorizedError({ message: "User has no access" });
|
||||
}
|
||||
@@ -253,7 +247,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
|
||||
const updatedRequest = await secretApprovalRequestDAL.updateById(secretApprovalRequest.id, {
|
||||
status,
|
||||
statusChangeBy: membership.id
|
||||
statusChangedByUserId: actorId
|
||||
});
|
||||
return { ...secretApprovalRequest, ...updatedRequest };
|
||||
};
|
||||
@@ -270,7 +264,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy, folderId, projectId } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -280,19 +274,19 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||
) {
|
||||
throw new UnauthorizedError({ message: "User has no access" });
|
||||
}
|
||||
const reviewers = secretApprovalRequest.reviewers.reduce<Record<string, ApprovalStatus>>(
|
||||
(prev, curr) => ({ ...prev, [curr.member.toString()]: curr.status as ApprovalStatus }),
|
||||
(prev, curr) => ({ ...prev, [curr.userId.toString()]: curr.status as ApprovalStatus }),
|
||||
{}
|
||||
);
|
||||
const hasMinApproval =
|
||||
secretApprovalRequest.policy.approvals <=
|
||||
secretApprovalRequest.policy.approvers.filter(
|
||||
(approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED
|
||||
({ userId: approverId }) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED
|
||||
).length;
|
||||
|
||||
if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
|
||||
@@ -472,7 +466,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
conflicts: JSON.stringify(conflicts),
|
||||
hasMerged: true,
|
||||
status: RequestState.Closed,
|
||||
statusChangeBy: membership.id
|
||||
statusChangedByUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -509,7 +503,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
}: TGenerateSecretApprovalRequestDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { permission, membership } = await permissionService.getProjectPermission(
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -663,7 +657,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
policyId: policy.id,
|
||||
status: "open",
|
||||
hasMerged: false,
|
||||
committerId: membership.id
|
||||
committerUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
@@ -11,7 +11,6 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { QueueName, TQueueServiceFactory } from "@app/queue";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
|
||||
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
|
||||
@@ -46,7 +45,6 @@ type TSecretReplicationServiceFactoryDep = {
|
||||
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
||||
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret" | "find">;
|
||||
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "create" | "transaction">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findOne">;
|
||||
secretApprovalRequestSecretDAL: Pick<
|
||||
TSecretApprovalRequestSecretDALFactory,
|
||||
"insertMany" | "insertApprovalSecretTags"
|
||||
@@ -92,7 +90,6 @@ export const secretReplicationServiceFactory = ({
|
||||
secretApprovalRequestSecretDAL,
|
||||
secretApprovalRequestDAL,
|
||||
secretQueueService,
|
||||
projectMembershipDAL,
|
||||
projectBotService
|
||||
}: TSecretReplicationServiceFactoryDep) => {
|
||||
const getReplicatedSecrets = (
|
||||
@@ -297,12 +294,6 @@ export const secretReplicationServiceFactory = ({
|
||||
);
|
||||
// this means it should be a approval request rather than direct replication
|
||||
if (policy && actor === ActorType.USER) {
|
||||
const membership = await projectMembershipDAL.findOne({ projectId, userId: actorId });
|
||||
if (!membership) {
|
||||
logger.error("Project membership not found in %s for user %s", projectId, actorId);
|
||||
return;
|
||||
}
|
||||
|
||||
const localSecretsLatestVersions = destinationLocalSecrets.map(({ id }) => id);
|
||||
const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(
|
||||
destinationReplicationFolderId,
|
||||
@@ -316,7 +307,7 @@ export const secretReplicationServiceFactory = ({
|
||||
policyId: policy.id,
|
||||
status: "open",
|
||||
hasMerged: false,
|
||||
committerId: membership.id,
|
||||
committerUserId: actorId,
|
||||
isReplicated: true
|
||||
},
|
||||
tx
|
||||
|
||||
+1
-1
@@ -331,7 +331,7 @@ export const secretRotationQueueFactory = ({
|
||||
|
||||
logger.info("Finished rotating: rotation id: ", rotationId);
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
logger.error(error, "Failed to execute secret rotation");
|
||||
if (error instanceof DisableRotationErrors) {
|
||||
if (job.id) {
|
||||
await queue.stopRepeatableJobByJobId(QueueName.SecretRotation, job.id);
|
||||
|
||||
@@ -133,7 +133,7 @@ export const secretRotationServiceFactory = ({
|
||||
creds: []
|
||||
};
|
||||
const encData = infisicalSymmetricEncypt(JSON.stringify(unencryptedData));
|
||||
const secretRotation = secretRotationDAL.transaction(async (tx) => {
|
||||
const secretRotation = await secretRotationDAL.transaction(async (tx) => {
|
||||
const doc = await secretRotationDAL.create(
|
||||
{
|
||||
provider,
|
||||
@@ -148,13 +148,13 @@ export const secretRotationServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
await secretRotationQueue.addToQueue(doc.id, doc.interval);
|
||||
const outputSecretMapping = await secretRotationDAL.secretOutputInsertMany(
|
||||
Object.entries(outputs).map(([key, secretId]) => ({ key, secretId, rotationId: doc.id })),
|
||||
tx
|
||||
);
|
||||
return { ...doc, outputs: outputSecretMapping, environment: folder.environment };
|
||||
});
|
||||
await secretRotationQueue.addToQueue(secretRotation.id, secretRotation.interval);
|
||||
return secretRotation;
|
||||
};
|
||||
|
||||
@@ -212,9 +212,9 @@ export const secretRotationServiceFactory = ({
|
||||
);
|
||||
const deletedDoc = await secretRotationDAL.transaction(async (tx) => {
|
||||
const strat = await secretRotationDAL.deleteById(rotationId, tx);
|
||||
await secretRotationQueue.removeFromQueue(strat.id, strat.interval);
|
||||
return strat;
|
||||
});
|
||||
await secretRotationQueue.removeFromQueue(deletedDoc.id, deletedDoc.interval);
|
||||
return { ...doc, ...deletedDoc };
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user