Compare commits

..

1 Commits

Author SHA1 Message Date
Tuan Dang
f8ad8de4c5 Make progress on restyling 2024-03-15 12:31:59 -07:00
813 changed files with 8657 additions and 23368 deletions

View File

@@ -3,6 +3,9 @@
# THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION
ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218
# Required
DB_CONNECTION_URI=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
# JWT
# Required secrets to sign JWT tokens
# THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION
@@ -13,9 +16,6 @@ POSTGRES_PASSWORD=infisical
POSTGRES_USER=infisical
POSTGRES_DB=infisical
# Required
DB_CONNECTION_URI=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
# Redis
REDIS_URL=redis://redis:6379

View File

@@ -41,7 +41,6 @@ jobs:
load: true
context: backend
tags: infisical/infisical:test
platforms: linux/amd64,linux/arm64
- name: ⏻ Spawn backend container and dependencies
run: |
docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull
@@ -93,7 +92,6 @@ jobs:
project: 64mmf0n610
context: frontend
tags: infisical/frontend:test
platforms: linux/amd64,linux/arm64
build-args: |
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
NEXT_INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }}

View File

@@ -1,140 +0,0 @@
name: Deployment pipeline
on: [workflow_dispatch]
permissions:
id-token: write
contents: read
jobs:
infisical-image:
name: Build backend image
runs-on: ubuntu-latest
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: 📦 Install dependencies to test all dependencies
run: npm ci --only-production
working-directory: backend
- name: Save commit hashes for tag
id: commit
uses: pr-mpt/actions-commit-hash@v2
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: 🏗️ Build backend and push to docker hub
uses: depot/build-push-action@v1
with:
project: 64mmf0n610
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
push: true
context: .
file: Dockerfile.standalone-infisical
tags: |
infisical/staging_infisical:${{ steps.commit.outputs.short }}
infisical/staging_infisical:latest
platforms: linux/amd64,linux/arm64
build-args: |
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
INFISICAL_PLATFORM_VERSION=${{ steps.commit.outputs.short }}
gamma-deployment:
name: Deploy to gamma
runs-on: ubuntu-latest
needs: [infisical-image]
environment:
name: Gamma
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: "20"
- name: Change directory to backend and install dependencies
env:
DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }}
run: |
cd backend
npm install
npm run migration:latest
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
audience: sts.amazonaws.com
aws-region: us-east-1
role-to-assume: arn:aws:iam::905418227878:role/deploy-new-ecs-img
- name: Save commit hashes for tag
id: commit
uses: pr-mpt/actions-commit-hash@v2
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
- name: Render Amazon ECS task definition
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: infisical-prod-platform
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
environment-variables: "LOG_LEVEL=info"
- name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: infisical-prod-platform
cluster: infisical-prod-platform
wait-for-service-stability: true
production-postgres-deployment:
name: Deploy to production
runs-on: ubuntu-latest
needs: [gamma-deployment]
environment:
name: Production
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: "20"
- name: Change directory to backend and install dependencies
env:
DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }}
run: |
cd backend
npm install
npm run migration:latest
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
audience: sts.amazonaws.com
aws-region: us-east-1
role-to-assume: arn:aws:iam::381492033652:role/gha-make-prod-deployment
- name: Save commit hashes for tag
id: commit
uses: pr-mpt/actions-commit-hash@v2
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
- name: Render Amazon ECS task definition
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: infisical-prod-platform
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
environment-variables: "LOG_LEVEL=info"
- name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: infisical-prod-platform
cluster: infisical-prod-platform
wait-for-service-stability: true

View File

@@ -0,0 +1,120 @@
name: Build, Publish and Deploy to Gamma
on: [workflow_dispatch]
jobs:
infisical-image:
name: Build backend image
runs-on: ubuntu-latest
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: 📦 Install dependencies to test all dependencies
run: npm ci --only-production
working-directory: backend
# - name: 🧪 Run tests
# run: npm run test:ci
# working-directory: backend
- name: Save commit hashes for tag
id: commit
uses: pr-mpt/actions-commit-hash@v2
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: 📦 Build backend and export to Docker
uses: depot/build-push-action@v1
with:
project: 64mmf0n610
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
load: true
context: .
file: Dockerfile.standalone-infisical
tags: infisical/infisical:test
# - name: ⏻ Spawn backend container and dependencies
# run: |
# docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull
# - name: 🧪 Test backend image
# run: |
# ./.github/resources/healthcheck.sh infisical-backend-test
# - name: ⏻ Shut down backend container and dependencies
# run: |
# docker compose -f .github/resources/docker-compose.be-test.yml down
- name: 🏗️ Build backend and push
uses: depot/build-push-action@v1
with:
project: 64mmf0n610
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
push: true
context: .
file: Dockerfile.standalone-infisical
tags: |
infisical/staging_infisical:${{ steps.commit.outputs.short }}
infisical/staging_infisical:latest
platforms: linux/amd64,linux/arm64
build-args: |
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }}
postgres-migration:
name: Run latest migration files
runs-on: ubuntu-latest
needs: [infisical-image]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: "20"
- name: Change directory to backend and install dependencies
env:
DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }}
run: |
cd backend
npm install
npm run migration:latest
# - name: Run postgres DB migration files
# env:
# DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }}
# run: npm run migration:latest
gamma-deployment:
name: Deploy to gamma
runs-on: ubuntu-latest
needs: [postgres-migration]
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.10.0
- name: Install infisical helm chart
run: |
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
helm repo update
- name: Install kubectl
uses: azure/setup-kubectl@v3
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Save DigitalOcean kubeconfig with short-lived credentials
run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 infisical-gamma-postgres
- name: switch to gamma namespace
run: kubectl config set-context --current --namespace=gamma
- name: test kubectl
run: kubectl get ingress
- name: Download helm values to file and upgrade gamma deploy
run: |
wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml
helm upgrade infisical infisical-helm-charts/infisical-standalone --values values.yaml --wait --install
if [[ $(helm status infisical) == *"FAILED"* ]]; then
echo "Helm upgrade failed"
exit 1
else
echo "Helm upgrade was successful"
fi

View File

@@ -118,6 +118,9 @@ WORKDIR /backend
ENV TELEMETRY_ENABLED true
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
CMD node healthcheck.js
EXPOSE 8080
EXPOSE 443

View File

@@ -10,8 +10,7 @@
<a href="https://infisical.com/">Infisical Cloud</a> |
<a href="https://infisical.com/docs/self-hosting/overview">Self-Hosting</a> |
<a href="https://infisical.com/docs/documentation/getting-started/introduction">Docs</a> |
<a href="https://www.infisical.com">Website</a> |
<a href="https://infisical.com/careers">Hiring (Remote/SF)</a>
<a href="https://www.infisical.com">Website</a>
</h4>
<p align="center">

View File

@@ -10,7 +10,7 @@ import { seedData1 } from "@app/db/seed-data";
import { initEnvConfig } from "@app/lib/config/env";
import { initLogger } from "@app/lib/logger";
import { main } from "@app/server/app";
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
import { AuthTokenType } from "@app/services/auth/auth-type";
import { mockQueue } from "./mocks/queue";
import { mockSmtpServer } from "./mocks/smtp";
@@ -52,8 +52,6 @@ export default {
authTokenType: AuthTokenType.ACCESS_TOKEN,
userId: seedData1.id,
tokenVersionId: seedData1.token.id,
authMethod: AuthMethod.EMAIL,
organizationId: seedData1.organization.id,
accessVersion: 1
},
cfg.AUTH_SECRET,

View File

@@ -103,15 +103,11 @@ export const ${dalName} = (db: TDbClient) => {
`import { z } from "zod";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { readLimit } from "@app/server/config/rateLimiter";
export const register${pascalCase}Router = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({}),
response: {

View File

@@ -7,10 +7,10 @@ const prompt = promptSync({ sigint: true });
const migrationName = prompt("Enter name for migration: ");
// Remove spaces from migration name and replace with hyphens
const formattedMigrationName = migrationName.replace(/\s+/g, "-");
execSync(
`npx knex migrate:make --knexfile ${path.join(__dirname, "../src/db/knexfile.ts")} -x ts ${formattedMigrationName}`,
`npx knex migrate:make --knexfile ${path.join(
__dirname,
"../src/db/knexfile.ts"
)} -x ts ${migrationName}`,
{ stdio: "inherit" }
);

View File

@@ -3,13 +3,9 @@ import "fastify";
import { TUsers } from "@app/db/schemas";
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
import { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
@@ -23,7 +19,7 @@ import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
import { TAuthLoginFactory } from "@app/services/auth/auth-login-service";
import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { ActorType } from "@app/services/auth/auth-type";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
@@ -63,10 +59,9 @@ declare module "fastify" {
// identity injection. depending on which kinda of token the information is filled in auth
auth: TAuthMode;
permission: {
authMethod: ActorAuthMethod;
type: ActorType;
id: string;
orgId: string;
orgId?: string;
};
// passport data
passportUser: {
@@ -121,10 +116,6 @@ declare module "fastify" {
trustedIp: TTrustedIpServiceFactory;
secretBlindIndex: TSecretBlindIndexServiceFactory;
telemetry: TTelemetryServiceFactory;
dynamicSecret: TDynamicSecretServiceFactory;
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer

View File

@@ -17,12 +17,6 @@ import {
TBackupPrivateKey,
TBackupPrivateKeyInsert,
TBackupPrivateKeyUpdate,
TDynamicSecretLeases,
TDynamicSecretLeasesInsert,
TDynamicSecretLeasesUpdate,
TDynamicSecrets,
TDynamicSecretsInsert,
TDynamicSecretsUpdate,
TGitAppInstallSessions,
TGitAppInstallSessionsInsert,
TGitAppInstallSessionsUpdate,
@@ -38,9 +32,6 @@ import {
TIdentityOrgMemberships,
TIdentityOrgMembershipsInsert,
TIdentityOrgMembershipsUpdate,
TIdentityProjectAdditionalPrivilege,
TIdentityProjectAdditionalPrivilegeInsert,
TIdentityProjectAdditionalPrivilegeUpdate,
TIdentityProjectMembershipRole,
TIdentityProjectMembershipRoleInsert,
TIdentityProjectMembershipRoleUpdate,
@@ -95,9 +86,6 @@ import {
TProjects,
TProjectsInsert,
TProjectsUpdate,
TProjectUserAdditionalPrivilege,
TProjectUserAdditionalPrivilegeInsert,
TProjectUserAdditionalPrivilegeUpdate,
TProjectUserMembershipRoles,
TProjectUserMembershipRolesInsert,
TProjectUserMembershipRolesUpdate,
@@ -245,11 +233,6 @@ declare module "knex/types/tables" {
TProjectUserMembershipRolesUpdate
>;
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
TProjectUserAdditionalPrivilege,
TProjectUserAdditionalPrivilegeInsert,
TProjectUserAdditionalPrivilegeUpdate
>;
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
@@ -305,11 +288,6 @@ declare module "knex/types/tables" {
TIdentityProjectMembershipRoleInsert,
TIdentityProjectMembershipRoleUpdate
>;
[TableName.IdentityProjectAdditionalPrivilege]: Knex.CompositeTableType<
TIdentityProjectAdditionalPrivilege,
TIdentityProjectAdditionalPrivilegeInsert,
TIdentityProjectAdditionalPrivilegeUpdate
>;
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
TSecretApprovalPolicies,
@@ -362,12 +340,6 @@ declare module "knex/types/tables" {
TSecretSnapshotFoldersInsert,
TSecretSnapshotFoldersUpdate
>;
[TableName.DynamicSecret]: Knex.CompositeTableType<TDynamicSecrets, TDynamicSecretsInsert, TDynamicSecretsUpdate>;
[TableName.DynamicSecretLease]: Knex.CompositeTableType<
TDynamicSecretLeases,
TDynamicSecretLeasesInsert,
TDynamicSecretLeasesUpdate
>;
[TableName.SamlConfig]: Knex.CompositeTableType<TSamlConfigs, TSamlConfigsInsert, TSamlConfigsUpdate>;
[TableName.LdapConfig]: Knex.CompositeTableType<TLdapConfigs, TLdapConfigsInsert, TLdapConfigsUpdate>;
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;

View File

@@ -1,58 +0,0 @@
import { Knex } from "knex";
import { SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const doesTableExist = await knex.schema.hasTable(TableName.DynamicSecret);
if (!doesTableExist) {
await knex.schema.createTable(TableName.DynamicSecret, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.integer("version").notNullable();
t.string("type").notNullable();
t.string("defaultTTL").notNullable();
t.string("maxTTL");
t.string("inputIV").notNullable();
t.text("inputCiphertext").notNullable();
t.string("inputTag").notNullable();
t.string("algorithm").notNullable().defaultTo(SecretEncryptionAlgo.AES_256_GCM);
t.string("keyEncoding").notNullable().defaultTo(SecretKeyEncoding.UTF8);
t.uuid("folderId").notNullable();
// for background process communication
t.string("status");
t.string("statusDetails");
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
t.unique(["name", "folderId"]);
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.DynamicSecret);
const doesTableDynamicSecretLease = await knex.schema.hasTable(TableName.DynamicSecretLease);
if (!doesTableDynamicSecretLease) {
await knex.schema.createTable(TableName.DynamicSecretLease, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").notNullable();
t.string("externalEntityId").notNullable();
t.datetime("expireAt").notNullable();
// for background process communication
t.string("status");
t.string("statusDetails");
t.uuid("dynamicSecretId").notNullable();
t.foreign("dynamicSecretId").references("id").inTable(TableName.DynamicSecret).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.DynamicSecretLease);
}
export async function down(knex: Knex): Promise<void> {
await dropOnUpdateTrigger(knex, TableName.DynamicSecretLease);
await knex.schema.dropTableIfExists(TableName.DynamicSecretLease);
await dropOnUpdateTrigger(knex, TableName.DynamicSecret);
await knex.schema.dropTableIfExists(TableName.DynamicSecret);
}

View File

@@ -1,29 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.ProjectUserAdditionalPrivilege))) {
await knex.schema.createTable(TableName.ProjectUserAdditionalPrivilege, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("slug", 60).notNullable();
t.uuid("projectMembershipId").notNullable();
t.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
t.boolean("isTemporary").notNullable().defaultTo(false);
t.string("temporaryMode");
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
t.datetime("temporaryAccessStartTime");
t.datetime("temporaryAccessEndTime");
t.jsonb("permissions").notNullable();
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.ProjectUserAdditionalPrivilege);
}
export async function down(knex: Knex): Promise<void> {
await dropOnUpdateTrigger(knex, TableName.ProjectUserAdditionalPrivilege);
await knex.schema.dropTableIfExists(TableName.ProjectUserAdditionalPrivilege);
}

View File

@@ -1,32 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IdentityProjectAdditionalPrivilege))) {
await knex.schema.createTable(TableName.IdentityProjectAdditionalPrivilege, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("slug", 60).notNullable();
t.uuid("projectMembershipId").notNullable();
t.foreign("projectMembershipId")
.references("id")
.inTable(TableName.IdentityProjectMembership)
.onDelete("CASCADE");
t.boolean("isTemporary").notNullable().defaultTo(false);
t.string("temporaryMode");
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
t.datetime("temporaryAccessStartTime");
t.datetime("temporaryAccessEndTime");
t.jsonb("permissions").notNullable();
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.IdentityProjectAdditionalPrivilege);
}
export async function down(knex: Knex): Promise<void> {
await dropOnUpdateTrigger(knex, TableName.IdentityProjectAdditionalPrivilege);
await knex.schema.dropTableIfExists(TableName.IdentityProjectAdditionalPrivilege);
}

View File

@@ -1,111 +0,0 @@
import { Knex } from "knex";
import { z } from "zod";
import { TableName, TOrgMemberships } from "../schemas";
const validateOrgMembership = (membershipToValidate: TOrgMemberships, firstMembership: TOrgMemberships) => {
const firstOrgId = firstMembership.orgId;
const firstUserId = firstMembership.userId;
if (membershipToValidate.id === firstMembership.id) {
return;
}
if (membershipToValidate.inviteEmail !== firstMembership.inviteEmail) {
throw new Error(`Invite emails are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.orgId !== firstMembership.orgId) {
throw new Error(`OrgIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.role !== firstMembership.role) {
throw new Error(`Roles are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.roleId !== firstMembership.roleId) {
throw new Error(`RoleIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.status !== firstMembership.status) {
throw new Error(`Statuses are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.userId !== firstMembership.userId) {
throw new Error(`UserIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
};
export async function up(knex: Knex): Promise<void> {
const RowSchema = z.object({
userId: z.string(),
orgId: z.string(),
cnt: z.string()
});
// Transactional find and delete duplicate rows
await knex.transaction(async (tx) => {
const duplicateRows = await tx(TableName.OrgMembership)
.select("userId", "orgId") // Select the userId and orgId so we can group by them
.count("* as cnt") // Count the number of rows for each userId and orgId, so we can make sure there are more than 1 row (a duplicate)
.groupBy("userId", "orgId")
.havingRaw("count(*) > ?", [1]); // Using havingRaw for direct SQL expressions
// Parse the rows to ensure they are in the correct format, and for type safety
const parsedRows = RowSchema.array().parse(duplicateRows);
// For each of the duplicate rows, loop through and find the actual memberships to delete
for (const row of parsedRows) {
const count = Number(row.cnt);
// An extra check to ensure that the count is actually a number, and the number is greater than 2
if (typeof count !== "number" || count < 2) {
// eslint-disable-next-line no-continue
continue;
}
// Find all the organization memberships that have the same userId and orgId
// eslint-disable-next-line no-await-in-loop
const rowsToDelete = await tx(TableName.OrgMembership).where({
userId: row.userId,
orgId: row.orgId
});
// Ensure that all the rows have exactly the same value, except id, createdAt, updatedAt
for (const rowToDelete of rowsToDelete) {
validateOrgMembership(rowToDelete, rowsToDelete[0]);
}
// Find the row with the latest createdAt, which we will keep
let lowestCreatedAt: number | null = null;
let latestCreatedRow: TOrgMemberships | null = null;
for (const rowToDelete of rowsToDelete) {
if (lowestCreatedAt === null || rowToDelete.createdAt.getTime() < lowestCreatedAt) {
lowestCreatedAt = rowToDelete.createdAt.getTime();
latestCreatedRow = rowToDelete;
}
}
if (!latestCreatedRow) {
throw new Error("Failed to find last created membership");
}
// Filter out the latest row from the rows to delete
const membershipIdsToDelete = rowsToDelete.map((r) => r.id).filter((id) => id !== latestCreatedRow!.id);
// eslint-disable-next-line no-await-in-loop
const numberOfRowsDeleted = await tx(TableName.OrgMembership).whereIn("id", membershipIdsToDelete).delete();
// eslint-disable-next-line no-console
console.log(
`Deleted ${numberOfRowsDeleted} duplicate organization memberships for ${row.userId} and ${row.orgId}`
);
}
});
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
table.unique(["userId", "orgId"]);
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
table.dropUnique(["userId", "orgId"]);
});
}

View File

@@ -1,24 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const DynamicSecretLeasesSchema = z.object({
id: z.string().uuid(),
version: z.number(),
externalEntityId: z.string(),
expireAt: z.date(),
status: z.string().nullable().optional(),
statusDetails: z.string().nullable().optional(),
dynamicSecretId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TDynamicSecretLeases = z.infer<typeof DynamicSecretLeasesSchema>;
export type TDynamicSecretLeasesInsert = Omit<z.input<typeof DynamicSecretLeasesSchema>, TImmutableDBKeys>;
export type TDynamicSecretLeasesUpdate = Partial<Omit<z.input<typeof DynamicSecretLeasesSchema>, TImmutableDBKeys>>;

View File

@@ -1,31 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const DynamicSecretsSchema = z.object({
id: z.string().uuid(),
name: z.string(),
version: z.number(),
type: z.string(),
defaultTTL: z.string(),
maxTTL: z.string().nullable().optional(),
inputIV: z.string(),
inputCiphertext: z.string(),
inputTag: z.string(),
algorithm: z.string().default("aes-256-gcm"),
keyEncoding: z.string().default("utf8"),
folderId: z.string().uuid(),
status: z.string().nullable().optional(),
statusDetails: z.string().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
export type TDynamicSecretsInsert = Omit<z.input<typeof DynamicSecretsSchema>, TImmutableDBKeys>;
export type TDynamicSecretsUpdate = Partial<Omit<z.input<typeof DynamicSecretsSchema>, TImmutableDBKeys>>;

View File

@@ -1,31 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const IdentityProjectAdditionalPrivilegeSchema = z.object({
id: z.string().uuid(),
slug: z.string(),
projectMembershipId: z.string().uuid(),
isTemporary: z.boolean().default(false),
temporaryMode: z.string().nullable().optional(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional(),
permissions: z.unknown(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TIdentityProjectAdditionalPrivilege = z.infer<typeof IdentityProjectAdditionalPrivilegeSchema>;
export type TIdentityProjectAdditionalPrivilegeInsert = Omit<
z.input<typeof IdentityProjectAdditionalPrivilegeSchema>,
TImmutableDBKeys
>;
export type TIdentityProjectAdditionalPrivilegeUpdate = Partial<
Omit<z.input<typeof IdentityProjectAdditionalPrivilegeSchema>, TImmutableDBKeys>
>;

View File

@@ -3,14 +3,11 @@ export * from "./audit-logs";
export * from "./auth-token-sessions";
export * from "./auth-tokens";
export * from "./backup-private-key";
export * from "./dynamic-secret-leases";
export * from "./dynamic-secrets";
export * from "./git-app-install-sessions";
export * from "./git-app-org";
export * from "./identities";
export * from "./identity-access-tokens";
export * from "./identity-org-memberships";
export * from "./identity-project-additional-privilege";
export * from "./identity-project-membership-role";
export * from "./identity-project-memberships";
export * from "./identity-ua-client-secrets";
@@ -29,7 +26,6 @@ export * from "./project-environments";
export * from "./project-keys";
export * from "./project-memberships";
export * from "./project-roles";
export * from "./project-user-additional-privilege";
export * from "./project-user-membership-roles";
export * from "./projects";
export * from "./saml-configs";

View File

@@ -20,7 +20,6 @@ export enum TableName {
Environment = "project_environments",
ProjectMembership = "project_memberships",
ProjectRoles = "project_roles",
ProjectUserAdditionalPrivilege = "project_user_additional_privilege",
ProjectUserMembershipRole = "project_user_membership_roles",
ProjectKeys = "project_keys",
Secret = "secrets",
@@ -44,7 +43,6 @@ export enum TableName {
IdentityOrgMembership = "identity_org_memberships",
IdentityProjectMembership = "identity_project_memberships",
IdentityProjectMembershipRole = "identity_project_membership_role",
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
ScimToken = "scim_tokens",
SecretApprovalPolicy = "secret_approval_policies",
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
@@ -61,8 +59,6 @@ export enum TableName {
GitAppOrg = "git_app_org",
SecretScanningGitRisk = "secret_scanning_git_risks",
TrustedIps = "trusted_ips",
DynamicSecret = "dynamic_secrets",
DynamicSecretLease = "dynamic_secret_leases",
// junction tables with tags
JnSecretTag = "secret_tag_junction",
SecretVersionTag = "secret_version_tag_junction"

View File

@@ -1,31 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const ProjectUserAdditionalPrivilegeSchema = z.object({
id: z.string().uuid(),
slug: z.string(),
projectMembershipId: z.string().uuid(),
isTemporary: z.boolean().default(false),
temporaryMode: z.string().nullable().optional(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional(),
permissions: z.unknown(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TProjectUserAdditionalPrivilege = z.infer<typeof ProjectUserAdditionalPrivilegeSchema>;
export type TProjectUserAdditionalPrivilegeInsert = Omit<
z.input<typeof ProjectUserAdditionalPrivilegeSchema>,
TImmutableDBKeys
>;
export type TProjectUserAdditionalPrivilegeUpdate = Partial<
Omit<z.input<typeof ProjectUserAdditionalPrivilegeSchema>, TImmutableDBKeys>
>;

View File

@@ -1,197 +0,0 @@
import ms from "ms";
import { z } from "zod";
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
import { daysToMillisecond } from "@app/lib/dates";
import { removeTrailingSlash } from "@app/lib/fn";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
body: z.object({
dynamicSecretName: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.dynamicSecretName).toLowerCase(),
projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.projectSlug),
ttl: z
.string()
.optional()
.describe(DYNAMIC_SECRET_LEASES.CREATE.ttl)
.superRefine((val, ctx) => {
if (!val) return;
const valMs = ms(val);
if (valMs < 60 * 1000)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRET_LEASES.CREATE.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.path)
}),
response: {
200: z.object({
lease: DynamicSecretLeasesSchema,
dynamicSecret: SanitizedDynamicSecretSchema,
data: z.unknown()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { data, lease, dynamicSecret } = await server.services.dynamicSecretLease.create({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
name: req.body.dynamicSecretName,
...req.body
});
return { lease, data, dynamicSecret };
}
});
server.route({
method: "DELETE",
url: "/:leaseId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.leaseId)
}),
body: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.projectSlug),
path: z
.string()
.min(1)
.trim()
.default("/")
.transform(removeTrailingSlash)
.describe(DYNAMIC_SECRET_LEASES.DELETE.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.environmentSlug),
isForced: z.boolean().default(false).describe(DYNAMIC_SECRET_LEASES.DELETE.isForced)
}),
response: {
200: z.object({
lease: DynamicSecretLeasesSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const lease = await server.services.dynamicSecretLease.revokeLease({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
leaseId: req.params.leaseId,
...req.body
});
return { lease };
}
});
server.route({
method: "POST",
url: "/:leaseId/renew",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.leaseId)
}),
body: z.object({
ttl: z
.string()
.describe(DYNAMIC_SECRET_LEASES.RENEW.ttl)
.optional()
.superRefine((val, ctx) => {
if (!val) return;
const valMs = ms(val);
if (valMs < 60 * 1000)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}),
projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.projectSlug),
path: z
.string()
.min(1)
.trim()
.default("/")
.transform(removeTrailingSlash)
.describe(DYNAMIC_SECRET_LEASES.RENEW.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.ttl)
}),
response: {
200: z.object({
lease: DynamicSecretLeasesSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const lease = await server.services.dynamicSecretLease.renewLease({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
leaseId: req.params.leaseId,
...req.body
});
return { lease };
}
});
server.route({
url: "/:leaseId",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.leaseId)
}),
querystring: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.projectSlug),
path: z
.string()
.trim()
.default("/")
.transform(removeTrailingSlash)
.describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.environmentSlug)
}),
response: {
200: z.object({
lease: DynamicSecretLeasesSchema.extend({
dynamicSecret: SanitizedDynamicSecretSchema
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const lease = await server.services.dynamicSecretLease.getLeaseDetails({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
leaseId: req.params.leaseId,
...req.query
});
return { lease };
}
});
};

View File

@@ -1,290 +0,0 @@
import slugify from "@sindresorhus/slugify";
import ms from "ms";
import { z } from "zod";
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
import { DynamicSecretProviderSchema } from "@app/ee/services/dynamic-secret/providers/models";
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
import { daysToMillisecond } from "@app/lib/dates";
import { removeTrailingSlash } from "@app/lib/fn";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
body: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.CREATE.projectSlug),
provider: DynamicSecretProviderSchema.describe(DYNAMIC_SECRETS.CREATE.provider),
defaultTTL: z
.string()
.describe(DYNAMIC_SECRETS.CREATE.defaultTTL)
.superRefine((val, ctx) => {
const valMs = ms(val);
if (valMs < 60 * 1000)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}),
maxTTL: z
.string()
.describe(DYNAMIC_SECRETS.CREATE.maxTTL)
.optional()
.superRefine((val, ctx) => {
if (!val) return;
const valMs = ms(val);
if (valMs < 60 * 1000)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
})
.nullable(),
path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
name: z
.string()
.describe(DYNAMIC_SECRETS.CREATE.name)
.min(1)
.toLowerCase()
.max(64)
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid"
})
}),
response: {
200: z.object({
dynamicSecret: SanitizedDynamicSecretSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const dynamicSecretCfg = await server.services.dynamicSecret.create({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
return { dynamicSecret: dynamicSecretCfg };
}
});
server.route({
method: "PATCH",
url: "/:name",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.UPDATE.name)
}),
body: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.UPDATE.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.UPDATE.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.UPDATE.environmentSlug),
data: z.object({
inputs: z.any().optional().describe(DYNAMIC_SECRETS.UPDATE.inputs),
defaultTTL: z
.string()
.describe(DYNAMIC_SECRETS.UPDATE.defaultTTL)
.optional()
.superRefine((val, ctx) => {
if (!val) return;
const valMs = ms(val);
if (valMs < 60 * 1000)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}),
maxTTL: z
.string()
.describe(DYNAMIC_SECRETS.UPDATE.maxTTL)
.optional()
.superRefine((val, ctx) => {
if (!val) return;
const valMs = ms(val);
if (valMs < 60 * 1000)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
})
.nullable(),
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional()
})
}),
response: {
200: z.object({
dynamicSecret: SanitizedDynamicSecretSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const dynamicSecretCfg = await server.services.dynamicSecret.updateByName({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
name: req.params.name,
path: req.body.path,
projectSlug: req.body.projectSlug,
environmentSlug: req.body.environmentSlug,
...req.body.data
});
return { dynamicSecret: dynamicSecretCfg };
}
});
server.route({
method: "DELETE",
url: "/:name",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.DELETE.name)
}),
body: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.DELETE.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.DELETE.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.DELETE.environmentSlug),
isForced: z.boolean().default(false).describe(DYNAMIC_SECRETS.DELETE.isForced)
}),
response: {
200: z.object({
dynamicSecret: SanitizedDynamicSecretSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const dynamicSecretCfg = await server.services.dynamicSecret.deleteByName({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
name: req.params.name,
...req.body
});
return { dynamicSecret: dynamicSecretCfg };
}
});
server.route({
url: "/:name",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
name: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.name)
}),
querystring: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.GET_BY_NAME.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.environmentSlug)
}),
response: {
200: z.object({
dynamicSecret: SanitizedDynamicSecretSchema.extend({
inputs: z.unknown()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const dynamicSecretCfg = await server.services.dynamicSecret.getDetails({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
name: req.params.name,
...req.query
});
return { dynamicSecret: dynamicSecretCfg };
}
});
server.route({
url: "/",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.LIST.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.environmentSlug)
}),
response: {
200: z.object({
dynamicSecrets: SanitizedDynamicSecretSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const dynamicSecretCfgs = await server.services.dynamicSecret.list({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query
});
return { dynamicSecrets: dynamicSecretCfgs };
}
});
server.route({
url: "/:name/leases",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
name: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.name)
}),
querystring: z.object({
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.projectSlug),
path: z
.string()
.trim()
.default("/")
.transform(removeTrailingSlash)
.describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.environmentSlug)
}),
response: {
200: z.object({
leases: DynamicSecretLeasesSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const leases = await server.services.dynamicSecretLease.listLeases({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
name: req.params.name,
...req.query
});
return { leases };
}
});
};

View File

@@ -1,329 +0,0 @@
import { MongoAbility, RawRuleOf } from "@casl/ability";
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
import slugify from "@sindresorhus/slugify";
import ms from "ms";
import { z } from "zod";
import { IdentityProjectAdditionalPrivilegeSchema } from "@app/db/schemas";
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
import { ProjectPermissionSet } from "@app/ee/services/permission/project-permission";
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
import { alphaNumericNanoId } from "@app/lib/nanoid";
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";
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/permanent",
config: {
rateLimit: writeLimit
},
schema: {
description: "Create a permanent or a non expiry specific privilege for identity.",
security: [
{
bearerAuth: []
}
],
body: z.object({
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
slug: z
.string()
.min(1)
.max(60)
.trim()
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.optional()
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: false,
permissions: JSON.stringify(packRules(req.body.permissions))
});
return { privilege };
}
});
server.route({
method: "POST",
url: "/temporary",
config: {
rateLimit: writeLimit
},
schema: {
description: "Create a temporary or a expiring specific privilege for identity.",
security: [
{
bearerAuth: []
}
],
body: z.object({
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
slug: z
.string()
.min(1)
.max(60)
.trim()
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.optional()
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions),
temporaryMode: z
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryRange),
temporaryAccessStartTime: z
.string()
.datetime()
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryAccessStartTime)
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: true,
permissions: JSON.stringify(packRules(req.body.permissions))
});
return { privilege };
}
});
server.route({
method: "PATCH",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
description: "Update a specific privilege of an identity.",
security: [
{
bearerAuth: []
}
],
body: z.object({
// disallow empty string
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.slug),
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.identityId),
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.projectSlug),
privilegeDetails: z
.object({
slug: z
.string()
.min(1)
.max(60)
.trim()
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
temporaryMode: z
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
temporaryAccessStartTime: z
.string()
.datetime()
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryAccessStartTime)
})
.partial()
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const updatedInfo = req.body.privilegeDetails;
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
slug: req.body.privilegeSlug,
identityId: req.body.identityId,
projectSlug: req.body.projectSlug,
data: {
...updatedInfo,
permissions: updatedInfo?.permissions ? JSON.stringify(packRules(updatedInfo.permissions)) : undefined
}
});
return { privilege };
}
});
server.route({
method: "DELETE",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
description: "Delete a specific privilege of an identity.",
security: [
{
bearerAuth: []
}
],
body: z.object({
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.slug),
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.identityId),
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.projectSlug)
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const privilege = await server.services.identityProjectAdditionalPrivilege.deleteBySlug({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
slug: req.body.privilegeSlug,
identityId: req.body.identityId,
projectSlug: req.body.projectSlug
});
return { privilege };
}
});
server.route({
method: "GET",
url: "/:privilegeSlug",
config: {
rateLimit: readLimit
},
schema: {
description: "Retrieve details of a specific privilege by privilege slug.",
security: [
{
bearerAuth: []
}
],
params: z.object({
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.slug)
}),
querystring: z.object({
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.identityId),
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.projectSlug)
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const privilege = await server.services.identityProjectAdditionalPrivilege.getPrivilegeDetailsBySlug({
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
slug: req.params.privilegeSlug,
...req.query
});
return { privilege };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
description: "List of a specific privilege of an identity in a project.",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug),
unpacked: z
.enum(["false", "true"])
.transform((el) => el === "true")
.default("true")
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.unpacked)
}),
response: {
200: z.object({
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const privileges = await server.services.identityProjectAdditionalPrivilege.listIdentityProjectPrivileges({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query
});
if (req.query.unpacked) {
return {
privileges: privileges.map(({ permissions, ...el }) => ({
...el,
permissions: unpackRules(permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
}))
};
}
return { privileges };
}
});
};

View File

@@ -1,6 +1,3 @@
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
import { registerLdapRouter } from "./ldap-router";
import { registerLicenseRouter } from "./license-router";
import { registerOrgRoleRouter } from "./org-role-router";
@@ -16,7 +13,6 @@ import { registerSecretScanningRouter } from "./secret-scanning-router";
import { registerSecretVersionRouter } from "./secret-version-router";
import { registerSnapshotRouter } from "./snapshot-router";
import { registerTrustedIpRouter } from "./trusted-ip-router";
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
// org role starts with organization
@@ -38,26 +34,10 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerSecretRotationProviderRouter, {
prefix: "/secret-rotation-providers"
});
await server.register(
async (dynamicSecretRouter) => {
await dynamicSecretRouter.register(registerDynamicSecretRouter);
await dynamicSecretRouter.register(registerDynamicSecretLeaseRouter, { prefix: "/leases" });
},
{ prefix: "/dynamic-secrets" }
);
await server.register(registerSamlRouter, { prefix: "/sso" });
await server.register(registerScimRouter, { prefix: "/scim" });
await server.register(registerLdapRouter, { prefix: "/ldap" });
await server.register(registerSecretScanningRouter, { prefix: "/secret-scanning" });
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
await server.register(registerSecretVersionRouter, { prefix: "/secret" });
await server.register(
async (privilegeRouter) => {
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
await privilegeRouter.register(registerIdentityProjectAdditionalPrivilegeRouter, { prefix: "/identity" });
},
{ prefix: "/additional-privilege" }
);
};

View File

@@ -17,7 +17,6 @@ import { z } from "zod";
import { LdapConfigsSchema } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { logger } from "@app/lib/logger";
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";
@@ -98,11 +97,8 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/config",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
querystring: z.object({
@@ -126,7 +122,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
orgId: req.query.organizationId,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
return ldap;
@@ -134,11 +129,8 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/config",
config: {
rateLimit: writeLimit
},
method: "POST",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z.object({
@@ -159,7 +151,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
orgId: req.body.organizationId,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
@@ -171,9 +162,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/config",
method: "PATCH",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z
@@ -196,7 +184,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
orgId: req.body.organizationId,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});

View File

@@ -3,17 +3,13 @@
// TODO(akhilmhdh): Fix this when licence service gets it type
import { z } from "zod";
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";
export const registerLicenseRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:organizationId/plans/table",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
querystring: z.object({ billingCycle: z.enum(["monthly", "yearly"]) }),
params: z.object({ organizationId: z.string().trim() }),
@@ -28,7 +24,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId,
actorAuthMethod: req.permission.authMethod,
billingCycle: req.query.billingCycle
});
return data;
@@ -36,11 +31,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/plan",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -53,7 +45,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return { plan };
@@ -61,11 +52,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/plans",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({ organizationId: z.string().trim() }),
querystring: z.object({ workspaceId: z.string().trim().optional() }),
@@ -78,8 +66,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgPlan({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return data;
@@ -87,11 +73,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/:organizationId/session/trial",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
params: z.object({ organizationId: z.string().trim() }),
body: z.object({ success_url: z.string().trim() }),
@@ -106,7 +89,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId,
actorAuthMethod: req.permission.authMethod,
success_url: req.body.success_url
});
return data;
@@ -116,9 +98,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/:organizationId/customer-portal-session",
method: "POST",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -131,7 +110,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return data;
@@ -139,11 +117,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/plan/billing",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -156,7 +131,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return data;
@@ -164,11 +138,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/plan/table",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -181,7 +152,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return data;
@@ -189,11 +159,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/billing-details",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -206,7 +173,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return data;
@@ -214,11 +180,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "PATCH",
url: "/:organizationId/billing-details",
config: {
rateLimit: writeLimit
},
method: "PATCH",
schema: {
params: z.object({ organizationId: z.string().trim() }),
body: z.object({
@@ -235,7 +198,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId,
name: req.body.name,
email: req.body.email
@@ -245,11 +207,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/billing-details/payment-methods",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -262,7 +221,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return data;
@@ -270,11 +228,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/:organizationId/billing-details/payment-methods",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
params: z.object({ organizationId: z.string().trim() }),
body: z.object({
@@ -291,7 +246,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId,
success_url: req.body.success_url,
cancel_url: req.body.cancel_url
@@ -301,11 +255,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "DELETE",
url: "/:organizationId/billing-details/payment-methods/:pmtMethodId",
config: {
rateLimit: writeLimit
},
method: "DELETE",
schema: {
params: z.object({
organizationId: z.string().trim(),
@@ -320,7 +271,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.delOrgPmtMethods({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId,
pmtMethodId: req.params.pmtMethodId
@@ -330,11 +280,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/billing-details/tax-ids",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
organizationId: z.string().trim()
@@ -348,7 +295,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgTaxIds({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId
});
@@ -357,11 +303,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/:organizationId/billing-details/tax-ids",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
params: z.object({
organizationId: z.string().trim()
@@ -379,7 +322,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.addOrgTaxId({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId,
type: req.body.type,
@@ -390,11 +332,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "DELETE",
url: "/:organizationId/billing-details/tax-ids/:taxId",
config: {
rateLimit: writeLimit
},
method: "DELETE",
schema: {
params: z.object({
organizationId: z.string().trim(),
@@ -409,7 +348,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.delOrgTaxId({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId,
taxId: req.params.taxId
@@ -419,11 +357,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/:organizationId/invoices",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
organizationId: z.string().trim()
@@ -438,19 +373,15 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId,
actorAuthMethod: req.permission.authMethod
orgId: req.params.organizationId
});
return data;
}
});
server.route({
method: "GET",
url: "/:organizationId/licenses",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
organizationId: z.string().trim()
@@ -465,7 +396,6 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.params.organizationId
});
return data;

View File

@@ -2,7 +2,6 @@ import slugify from "@sindresorhus/slugify";
import { z } from "zod";
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
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";
@@ -10,9 +9,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/:organizationId/roles",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
organizationId: z.string().trim()
@@ -23,7 +19,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
.min(1)
.trim()
.refine(
(val) => !Object.keys(OrgMembershipRole).includes(val),
(val) => Object.keys(OrgMembershipRole).includes(val),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((v) => slugify(v) === v, {
@@ -45,7 +41,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
req.permission.id,
req.params.organizationId,
req.body,
req.permission.authMethod,
req.permission.orgId
);
return { role };
@@ -55,9 +50,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "PATCH",
url: "/:organizationId/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
organizationId: z.string().trim(),
@@ -92,7 +84,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
req.params.organizationId,
req.params.roleId,
req.body,
req.permission.authMethod,
req.permission.orgId
);
return { role };
@@ -102,9 +93,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "DELETE",
url: "/:organizationId/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
organizationId: z.string().trim(),
@@ -122,7 +110,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
req.permission.id,
req.params.organizationId,
req.params.roleId,
req.permission.authMethod,
req.permission.orgId
);
return { role };
@@ -132,9 +119,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:organizationId/roles",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
organizationId: z.string().trim()
@@ -154,7 +138,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
const roles = await server.services.orgRole.listRoles(
req.permission.id,
req.params.organizationId,
req.permission.authMethod,
req.permission.orgId
);
return { data: { roles } };
@@ -164,9 +147,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:organizationId/permissions",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
organizationId: z.string().trim()
@@ -183,7 +163,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
const { permissions, membership } = await server.services.orgRole.getUserPermission(
req.permission.id,
req.params.organizationId,
req.permission.authMethod,
req.permission.orgId
);
return { permissions, membership };

View File

@@ -1,7 +1,6 @@
import { z } from "zod";
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
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";
@@ -9,9 +8,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/:projectId/roles",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
@@ -35,7 +31,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.permission.id,
req.params.projectId,
req.body,
req.permission.authMethod,
req.permission.orgId
);
return { role };
@@ -45,9 +40,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "PATCH",
url: "/:projectId/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
projectId: z.string().trim(),
@@ -73,7 +65,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.params.projectId,
req.params.roleId,
req.body,
req.permission.authMethod,
req.permission.orgId
);
return { role };
@@ -83,9 +74,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "DELETE",
url: "/:projectId/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
projectId: z.string().trim(),
@@ -104,7 +92,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.permission.id,
req.params.projectId,
req.params.roleId,
req.permission.authMethod,
req.permission.orgId
);
return { role };
@@ -114,9 +101,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:projectId/roles",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
@@ -137,7 +121,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.permission.type,
req.permission.id,
req.params.projectId,
req.permission.authMethod,
req.permission.orgId
);
return { data: { roles } };
@@ -147,9 +130,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:projectId/permissions",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
@@ -168,7 +148,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
const { permissions, membership } = await server.services.projectRole.getUserPermission(
req.permission.id,
req.params.projectId,
req.permission.authMethod,
req.permission.orgId
);
return { data: { permissions, membership } };

View File

@@ -4,7 +4,6 @@ import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
import { removeTrailingSlash } from "@app/lib/fn";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -12,9 +11,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:workspaceId/secret-snapshots",
config: {
rateLimit: readLimit
},
schema: {
description: "Return project secret snapshots ids",
security: [
@@ -42,7 +38,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
handler: async (req) => {
const secretSnapshots = await server.services.snapshot.listSnapshots({
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId,
@@ -55,9 +50,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:workspaceId/secret-snapshots/count",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
workspaceId: z.string().trim()
@@ -77,7 +69,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const count = await server.services.snapshot.projectSecretSnapshotCount({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId,
environment: req.query.environment,
@@ -90,9 +81,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:workspaceId/audit-logs",
config: {
rateLimit: readLimit
},
schema: {
description: "Return audit logs",
security: [
@@ -142,7 +130,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const auditLogs = await server.services.auditLog.listProjectAuditLogs({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
projectId: req.params.workspaceId,
...req.query,
auditLogActor: req.query.actor,
@@ -155,9 +142,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:workspaceId/audit-logs/filters/actors",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
workspaceId: z.string().trim()

View File

@@ -17,7 +17,6 @@ import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
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";
@@ -204,11 +203,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/config",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
querystring: z.object({
@@ -235,7 +231,6 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.query.organizationId,
type: "org"
});
@@ -244,11 +239,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/config",
config: {
rateLimit: writeLimit
},
method: "POST",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z.object({
@@ -267,7 +259,6 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
const saml = await server.services.saml.createSamlCfg({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.body.organizationId,
...req.body
@@ -277,11 +268,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "PATCH",
url: "/config",
config: {
rateLimit: writeLimit
},
method: "PATCH",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z
@@ -302,7 +290,6 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
const saml = await server.services.saml.updateSamlCfg({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.body.organizationId,
...req.body

View File

@@ -1,7 +1,6 @@
import { z } from "zod";
import { ScimTokensSchema } from "@app/db/schemas";
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";
@@ -21,9 +20,6 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/scim-tokens",
method: "POST",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z.object({
@@ -43,7 +39,6 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.body.organizationId,
actorAuthMethod: req.permission.authMethod,
description: req.body.description,
ttlDays: req.body.ttlDays
});
@@ -55,9 +50,6 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/scim-tokens",
method: "GET",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
querystring: z.object({
@@ -73,7 +65,6 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
const scimTokens = await server.services.scim.listScimTokens({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.query.organizationId
});
@@ -85,9 +76,6 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/scim-tokens/:scimTokenId",
method: "DELETE",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -104,7 +92,6 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
scimTokenId: req.params.scimTokenId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
@@ -156,7 +143,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
offset: req.query.startIndex,
limit: req.query.count,
filter: req.query.filter,
orgId: req.permission.orgId
orgId: req.permission.orgId as string
});
return users;
}
@@ -194,7 +181,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
handler: async (req) => {
const user = await req.server.services.scim.getScimUser({
userId: req.params.userId,
orgId: req.permission.orgId
orgId: req.permission.orgId as string
});
return user;
}
@@ -253,7 +240,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
email: primaryEmail,
firstName: req.body.name.givenName,
lastName: req.body.name.familyName,
orgId: req.permission.orgId
orgId: req.permission.orgId as string
});
return user;
@@ -290,7 +277,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
handler: async (req) => {
const user = await req.server.services.scim.updateScimUser({
userId: req.params.userId,
orgId: req.permission.orgId,
orgId: req.permission.orgId as string,
operations: req.body.Operations
});
return user;
@@ -340,7 +327,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
handler: async (req) => {
const user = await req.server.services.scim.replaceScimUser({
userId: req.params.userId,
orgId: req.permission.orgId,
orgId: req.permission.orgId as string,
active: req.body.active
});
return user;

View File

@@ -1,7 +1,6 @@
import { nanoid } from "nanoid";
import { z } from "zod";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -10,9 +9,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
server.route({
url: "/",
method: "POST",
config: {
rateLimit: writeLimit
},
schema: {
body: z
.object({
@@ -38,7 +34,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approval = await server.services.secretApprovalPolicy.createSecretApprovalPolicy({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId,
...req.body,
@@ -51,9 +46,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
server.route({
url: "/:sapId",
method: "PATCH",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
sapId: z.string()
@@ -80,7 +72,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approval = await server.services.secretApprovalPolicy.updateSecretApprovalPolicy({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
secretPolicyId: req.params.sapId
@@ -92,9 +83,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
server.route({
url: "/:sapId",
method: "DELETE",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
sapId: z.string()
@@ -110,7 +98,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approval = await server.services.secretApprovalPolicy.deleteSecretApprovalPolicy({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
secretPolicyId: req.params.sapId
});
@@ -121,9 +108,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
server.route({
url: "/",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
workspaceId: z.string().trim()
@@ -139,7 +123,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approvals = await server.services.secretApprovalPolicy.getSecretApprovalPolicyByProjectId({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId
});
@@ -150,9 +133,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
server.route({
url: "/board",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
workspaceId: z.string().trim(),
@@ -170,7 +150,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId,
...req.query

View File

@@ -10,17 +10,13 @@ import {
} 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";
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";
export const registerSecretApprovalRequestRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
querystring: z.object({
workspaceId: z.string().trim(),
@@ -56,7 +52,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approvals = await server.services.secretApprovalRequest.getSecretApprovals({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query,
projectId: req.query.workspaceId
@@ -66,11 +61,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
});
server.route({
method: "GET",
url: "/count",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
querystring: z.object({
workspaceId: z.string().trim()
@@ -89,7 +81,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approvals = await server.services.secretApprovalRequest.requestCount({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId
});
@@ -100,9 +91,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
server.route({
url: "/:id/merge",
method: "POST",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
id: z.string()
@@ -118,7 +106,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const { approval } = await server.services.secretApprovalRequest.mergeSecretApprovalRequest({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
approvalId: req.params.id
});
@@ -127,11 +114,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
});
server.route({
method: "POST",
url: "/:id/review",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
params: z.object({
id: z.string()
@@ -150,7 +134,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const review = await server.services.secretApprovalRequest.reviewApproval({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
approvalId: req.params.id,
status: req.body.status
@@ -160,11 +143,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
});
server.route({
method: "POST",
url: "/:id/status",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
params: z.object({
id: z.string()
@@ -183,7 +163,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approval = await server.services.secretApprovalRequest.updateApprovalStatus({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
approvalId: req.params.id,
status: req.body.status
@@ -219,11 +198,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
.array()
.optional();
server.route({
method: "GET",
url: "/:id",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
id: z.string()
@@ -295,7 +271,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approval = await server.services.secretApprovalRequest.getSecretApprovalDetails({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.id
});

View File

@@ -1,16 +1,12 @@
import { z } from "zod";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretRotationProviderRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:workspaceId",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
workspaceId: z.string().trim()
@@ -34,7 +30,6 @@ export const registerSecretRotationProviderRouter = async (server: FastifyZodPro
const providers = await server.services.secretRotation.getProviderTemplates({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId
});

View File

@@ -2,17 +2,13 @@ import { z } from "zod";
import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas";
import { removeTrailingSlash } from "@app/lib/fn";
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";
export const registerSecretRotationRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
body: z.object({
workspaceId: z.string().trim(),
@@ -43,7 +39,6 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
handler: async (req) => {
const secretRotation = await server.services.secretRotation.createRotation({
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body,
@@ -56,9 +51,6 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
server.route({
url: "/restart",
method: "POST",
config: {
rateLimit: writeLimit
},
schema: {
body: z.object({
id: z.string().trim()
@@ -82,7 +74,6 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
const secretRotation = await server.services.secretRotation.restartById({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
rotationId: req.body.id
});
@@ -93,9 +84,6 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
server.route({
url: "/",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
workspaceId: z.string().trim()
@@ -137,7 +125,6 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
const secretRotations = await server.services.secretRotation.getByProjectId({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId
});
@@ -146,11 +133,8 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
});
server.route({
method: "DELETE",
url: "/:id",
config: {
rateLimit: writeLimit
},
method: "DELETE",
schema: {
params: z.object({
id: z.string().trim()
@@ -174,7 +158,6 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
const secretRotation = await server.services.secretRotation.deleteById({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
rotationId: req.params.id
});

View File

@@ -2,17 +2,13 @@ import { z } from "zod";
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
import { SecretScanningRiskStatus } from "@app/ee/services/secret-scanning/secret-scanning-types";
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";
export const registerSecretScanningRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/create-installation-session/organization",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
body: z.object({ organizationId: z.string().trim() }),
response: {
@@ -26,7 +22,6 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const session = await server.services.secretScanning.createInstallationSession({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.body.organizationId
});
@@ -35,11 +30,8 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
});
server.route({
method: "POST",
url: "/link-installation",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
body: z.object({
installationId: z.string(),
@@ -54,7 +46,6 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const { installatedApp } = await server.services.secretScanning.linkInstallationToOrg({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
@@ -63,11 +54,8 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
});
server.route({
method: "GET",
url: "/installation-status/organization/:organizationId",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -79,7 +67,6 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const appInstallationCompleted = await server.services.secretScanning.getOrgInstallationStatus({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId
});
@@ -90,9 +77,6 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
server.route({
url: "/organization/:organizationId/risks",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({ organizationId: z.string().trim() }),
response: {
@@ -104,7 +88,6 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const { risks } = await server.services.secretScanning.getRisksByOrg({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId
});
@@ -113,11 +96,8 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
});
server.route({
method: "POST",
url: "/organization/:organizationId/risks/:riskId/status",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
params: z.object({ organizationId: z.string().trim(), riskId: z.string().trim() }),
body: z.object({ status: z.nativeEnum(SecretScanningRiskStatus) }),
@@ -130,7 +110,6 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const { risk } = await server.services.secretScanning.updateRiskStatus({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId,
riskId: req.params.riskId,

View File

@@ -1,17 +1,13 @@
import { z } from "zod";
import { SecretVersionsSchema } from "@app/db/schemas";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretVersionRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:secretId/secret-versions",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
secretId: z.string()
@@ -31,7 +27,6 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) =>
const secretVersions = await server.services.secret.getSecretVersions({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
limit: req.query.limit,
offset: req.query.offset,

View File

@@ -2,7 +2,6 @@ import { z } from "zod";
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
import { PROJECTS } from "@app/lib/api-docs";
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";
@@ -10,9 +9,6 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:secretSnapshotId",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
secretSnapshotId: z.string().trim()
@@ -51,7 +47,6 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
const secretSnapshot = await server.services.snapshot.getSnapshotData({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.secretSnapshotId
});
@@ -62,9 +57,6 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/:secretSnapshotId/rollback",
config: {
rateLimit: writeLimit
},
schema: {
description: "Roll back project secrets to those captured in a secret snapshot version.",
security: [
@@ -87,7 +79,6 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
const secretSnapshot = await server.services.snapshot.rollbackSnapshot({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.secretSnapshotId
});

View File

@@ -2,17 +2,13 @@ import { z } from "zod";
import { TrustedIpsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
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";
export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:workspaceId/trusted-ips",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
workspaceId: z.string().trim()
@@ -26,7 +22,6 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const trustedIps = await server.services.trustedIp.listIpsByProjectId({
actorAuthMethod: req.permission.authMethod,
projectId: req.params.workspaceId,
actor: req.permission.type,
actorId: req.permission.id,
@@ -37,11 +32,8 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/:workspaceId/trusted-ips",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
params: z.object({
workspaceId: z.string().trim()
@@ -60,7 +52,6 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { trustedIp, project } = await server.services.trustedIp.addProjectIp({
actorAuthMethod: req.permission.authMethod,
projectId: req.params.workspaceId,
actor: req.permission.type,
actorId: req.permission.id,
@@ -85,11 +76,8 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "PATCH",
url: "/:workspaceId/trusted-ips/:trustedIpId",
config: {
rateLimit: writeLimit
},
method: "PATCH",
schema: {
params: z.object({
workspaceId: z.string().trim(),
@@ -111,7 +99,6 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
trustedIpId: req.params.trustedIpId,
...req.body
@@ -134,11 +121,8 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "DELETE",
url: "/:workspaceId/trusted-ips/:trustedIpId",
config: {
rateLimit: writeLimit
},
method: "DELETE",
schema: {
params: z.object({
workspaceId: z.string().trim(),
@@ -156,7 +140,6 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
trustedIpId: req.params.trustedIpId
});

View File

@@ -1,256 +0,0 @@
import slugify from "@sindresorhus/slugify";
import ms from "ms";
import { z } from "zod";
import { ProjectUserAdditionalPrivilegeSchema } from "@app/db/schemas";
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
import { alphaNumericNanoId } from "@app/lib/nanoid";
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";
export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/permanent",
method: "POST",
config: {
rateLimit: writeLimit
},
schema: {
body: z.object({
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
slug: z
.string()
.min(1)
.max(60)
.trim()
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.optional()
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions)
}),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.create({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: false,
permissions: JSON.stringify(req.body.permissions)
});
return { privilege };
}
});
server.route({
method: "POST",
url: "/temporary",
config: {
rateLimit: writeLimit
},
schema: {
body: z.object({
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
slug: z
.string()
.min(1)
.max(60)
.trim()
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.optional()
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions),
temporaryMode: z
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryRange),
temporaryAccessStartTime: z
.string()
.datetime()
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryAccessStartTime)
}),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.create({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : `privilege-${slugify(alphaNumericNanoId(12))}`,
isTemporary: true,
permissions: JSON.stringify(req.body.permissions)
});
return { privilege };
}
});
server.route({
method: "PATCH",
url: "/:privilegeId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
privilegeId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.privilegeId)
}),
body: z
.object({
slug: z
.string()
.max(60)
.trim()
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.slug),
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
isTemporary: z.boolean().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
temporaryMode: z
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
temporaryAccessStartTime: z
.string()
.datetime()
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryAccessStartTime)
})
.partial(),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.updateById({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
permissions: req.body.permissions ? JSON.stringify(req.body.permissions) : undefined,
privilegeId: req.params.privilegeId
});
return { privilege };
}
});
server.route({
method: "DELETE",
url: "/:privilegeId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.DELETE.privilegeId)
}),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.deleteById({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
privilegeId: req.params.privilegeId
});
return { privilege };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
projectMembershipId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.LIST.projectMembershipId)
}),
response: {
200: z.object({
privileges: ProjectUserAdditionalPrivilegeSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privileges = await server.services.projectUserAdditionalPrivilege.listPrivileges({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
projectMembershipId: req.query.projectMembershipId
});
return { privileges };
}
});
server.route({
method: "GET",
url: "/:privilegeId",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.GET_BY_PRIVILEGEID.privilegeId)
}),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.getPrivilegeDetailsById({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
privilegeId: req.params.privilegeId
});
return { privilege };
}
});
};

View File

@@ -31,17 +31,10 @@ export const auditLogServiceFactory = ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
projectId,
auditLogActor
}: TListProjectAuditLogDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
const auditLogs = await auditLogDAL.find({
startDate,

View File

@@ -1,80 +0,0 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { DynamicSecretLeasesSchema, TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex";
export type TDynamicSecretLeaseDALFactory = ReturnType<typeof dynamicSecretLeaseDALFactory>;
export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.DynamicSecretLease);
const countLeasesForDynamicSecret = async (dynamicSecretId: string, tx?: Knex) => {
try {
const doc = await (tx || db)(TableName.DynamicSecretLease).count("*").where({ dynamicSecretId }).first();
return parseInt(doc || "0", 10);
} catch (error) {
throw new DatabaseError({ error, name: "DynamicSecretCountLeases" });
}
};
const findById = async (id: string, tx?: Knex) => {
try {
const doc = await (tx || db)(TableName.DynamicSecretLease)
.where({ [`${TableName.DynamicSecretLease}.id` as "id"]: id })
.first()
.join(
TableName.DynamicSecret,
`${TableName.DynamicSecretLease}.dynamicSecretId`,
`${TableName.DynamicSecret}.id`
)
.select(selectAllTableCols(TableName.DynamicSecretLease))
.select(
db.ref("id").withSchema(TableName.DynamicSecret).as("dynId"),
db.ref("name").withSchema(TableName.DynamicSecret).as("dynName"),
db.ref("version").withSchema(TableName.DynamicSecret).as("dynVersion"),
db.ref("type").withSchema(TableName.DynamicSecret).as("dynType"),
db.ref("defaultTTL").withSchema(TableName.DynamicSecret).as("dynDefaultTTL"),
db.ref("maxTTL").withSchema(TableName.DynamicSecret).as("dynMaxTTL"),
db.ref("inputIV").withSchema(TableName.DynamicSecret).as("dynInputIV"),
db.ref("inputTag").withSchema(TableName.DynamicSecret).as("dynInputTag"),
db.ref("inputCiphertext").withSchema(TableName.DynamicSecret).as("dynInputCiphertext"),
db.ref("algorithm").withSchema(TableName.DynamicSecret).as("dynAlgorithm"),
db.ref("keyEncoding").withSchema(TableName.DynamicSecret).as("dynKeyEncoding"),
db.ref("folderId").withSchema(TableName.DynamicSecret).as("dynFolderId"),
db.ref("status").withSchema(TableName.DynamicSecret).as("dynStatus"),
db.ref("statusDetails").withSchema(TableName.DynamicSecret).as("dynStatusDetails"),
db.ref("createdAt").withSchema(TableName.DynamicSecret).as("dynCreatedAt"),
db.ref("updatedAt").withSchema(TableName.DynamicSecret).as("dynUpdatedAt")
);
if (!doc) return;
return {
...DynamicSecretLeasesSchema.parse(doc),
dynamicSecret: {
id: doc.dynId,
name: doc.dynName,
version: doc.dynVersion,
type: doc.dynType,
defaultTTL: doc.dynDefaultTTL,
maxTTL: doc.dynMaxTTL,
inputIV: doc.dynInputIV,
inputTag: doc.dynInputTag,
inputCiphertext: doc.dynInputCiphertext,
algorithm: doc.dynAlgorithm,
keyEncoding: doc.dynKeyEncoding,
folderId: doc.dynFolderId,
status: doc.dynStatus,
statusDetails: doc.dynStatusDetails,
createdAt: doc.dynCreatedAt,
updatedAt: doc.dynUpdatedAt
}
};
} catch (error) {
throw new DatabaseError({ error, name: "DynamicSecretLeaseFindById" });
}
};
return { ...orm, findById, countLeasesForDynamicSecret };
};

View File

@@ -1,159 +0,0 @@
import { SecretKeyEncoding } from "@app/db/schemas";
import { DisableRotationErrors } from "@app/ee/services/secret-rotation/secret-rotation-queue";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { logger } from "@app/lib/logger";
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal";
import { DynamicSecretStatus } from "../dynamic-secret/dynamic-secret-types";
import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models";
import { TDynamicSecretLeaseDALFactory } from "./dynamic-secret-lease-dal";
type TDynamicSecretLeaseQueueServiceFactoryDep = {
queueService: TQueueServiceFactory;
dynamicSecretLeaseDAL: Pick<TDynamicSecretLeaseDALFactory, "findById" | "deleteById" | "find" | "updateById">;
dynamicSecretDAL: Pick<TDynamicSecretDALFactory, "findById" | "deleteById" | "updateById">;
dynamicSecretProviders: Record<DynamicSecretProviders, TDynamicProviderFns>;
};
export type TDynamicSecretLeaseQueueServiceFactory = ReturnType<typeof dynamicSecretLeaseQueueServiceFactory>;
export const dynamicSecretLeaseQueueServiceFactory = ({
queueService,
dynamicSecretDAL,
dynamicSecretProviders,
dynamicSecretLeaseDAL
}: TDynamicSecretLeaseQueueServiceFactoryDep) => {
const pruneDynamicSecret = async (dynamicSecretCfgId: string) => {
await queueService.queue(
QueueName.DynamicSecretRevocation,
QueueJobs.DynamicSecretPruning,
{ dynamicSecretCfgId },
{
jobId: dynamicSecretCfgId,
backoff: {
type: "exponential",
delay: 3000
},
removeOnFail: {
count: 3
},
removeOnComplete: true
}
);
};
const setLeaseRevocation = async (leaseId: string, expiry: number) => {
await queueService.queue(
QueueName.DynamicSecretRevocation,
QueueJobs.DynamicSecretRevocation,
{ leaseId },
{
jobId: leaseId,
backoff: {
type: "exponential",
delay: 3000
},
delay: expiry,
removeOnFail: {
count: 3
},
removeOnComplete: true
}
);
};
const unsetLeaseRevocation = async (leaseId: string) => {
await queueService.stopJobById(QueueName.DynamicSecretRevocation, leaseId);
};
queueService.start(QueueName.DynamicSecretRevocation, async (job) => {
try {
if (job.name === QueueJobs.DynamicSecretRevocation) {
const { leaseId } = job.data as { leaseId: string };
logger.info("Dynamic secret lease revocation started: ", leaseId, job.id);
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
if (!dynamicSecretLease) throw new DisableRotationErrors({ message: "Dynamic secret lease not found" });
const dynamicSecretCfg = dynamicSecretLease.dynamicSecret;
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding,
ciphertext: dynamicSecretCfg.inputCiphertext,
tag: dynamicSecretCfg.inputTag,
iv: dynamicSecretCfg.inputIV
})
) as object;
await selectedProvider.revoke(decryptedStoredInput, dynamicSecretLease.externalEntityId);
await dynamicSecretLeaseDAL.deleteById(dynamicSecretLease.id);
return;
}
if (job.name === QueueJobs.DynamicSecretPruning) {
const { dynamicSecretCfgId } = job.data as { dynamicSecretCfgId: string };
logger.info("Dynamic secret pruning started: ", dynamicSecretCfgId, job.id);
const dynamicSecretCfg = await dynamicSecretDAL.findById(dynamicSecretCfgId);
if (!dynamicSecretCfg) throw new DisableRotationErrors({ message: "Dynamic secret not found" });
if ((dynamicSecretCfg.status as DynamicSecretStatus) !== DynamicSecretStatus.Deleting)
throw new DisableRotationErrors({ message: "Document not deleted" });
const dynamicSecretLeases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfgId });
if (dynamicSecretLeases.length) {
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding,
ciphertext: dynamicSecretCfg.inputCiphertext,
tag: dynamicSecretCfg.inputTag,
iv: dynamicSecretCfg.inputIV
})
) as object;
await Promise.all(dynamicSecretLeases.map(({ id }) => unsetLeaseRevocation(id)));
await Promise.all(
dynamicSecretLeases.map(({ externalEntityId }) =>
selectedProvider.revoke(decryptedStoredInput, externalEntityId)
)
);
}
await dynamicSecretDAL.deleteById(dynamicSecretCfgId);
}
logger.info("Finished dynamic secret job", job.id);
} catch (error) {
logger.error(error);
if (job?.name === QueueJobs.DynamicSecretPruning) {
const { dynamicSecretCfgId } = job.data as { dynamicSecretCfgId: string };
await dynamicSecretDAL.updateById(dynamicSecretCfgId, {
status: DynamicSecretStatus.FailedDeletion,
statusDetails: (error as Error)?.message?.slice(0, 255)
});
}
if (job?.name === QueueJobs.DynamicSecretRevocation) {
const { leaseId } = job.data as { leaseId: string };
await dynamicSecretLeaseDAL.updateById(leaseId, {
status: DynamicSecretStatus.FailedDeletion,
statusDetails: (error as Error)?.message?.slice(0, 255)
});
}
if (error instanceof DisableRotationErrors) {
if (job.id) {
await queueService.stopRepeatableJobByJobId(QueueName.DynamicSecretRevocation, job.id);
}
}
// propogate to next part
throw error;
}
});
return {
pruneDynamicSecret,
setLeaseRevocation,
unsetLeaseRevocation
};
};

View File

@@ -1,343 +0,0 @@
import { ForbiddenError, subject } from "@casl/ability";
import ms from "ms";
import { SecretKeyEncoding } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal";
import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models";
import { TDynamicSecretLeaseDALFactory } from "./dynamic-secret-lease-dal";
import { TDynamicSecretLeaseQueueServiceFactory } from "./dynamic-secret-lease-queue";
import {
DynamicSecretLeaseStatus,
TCreateDynamicSecretLeaseDTO,
TDeleteDynamicSecretLeaseDTO,
TDetailsDynamicSecretLeaseDTO,
TListDynamicSecretLeasesDTO,
TRenewDynamicSecretLeaseDTO
} from "./dynamic-secret-lease-types";
type TDynamicSecretLeaseServiceFactoryDep = {
dynamicSecretLeaseDAL: TDynamicSecretLeaseDALFactory;
dynamicSecretDAL: Pick<TDynamicSecretDALFactory, "findOne">;
dynamicSecretProviders: Record<DynamicSecretProviders, TDynamicProviderFns>;
dynamicSecretQueueService: TDynamicSecretLeaseQueueServiceFactory;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
};
export type TDynamicSecretLeaseServiceFactory = ReturnType<typeof dynamicSecretLeaseServiceFactory>;
export const dynamicSecretLeaseServiceFactory = ({
dynamicSecretLeaseDAL,
dynamicSecretProviders,
dynamicSecretDAL,
folderDAL,
permissionService,
dynamicSecretQueueService,
projectDAL,
licenseService
}: TDynamicSecretLeaseServiceFactoryDep) => {
const create = async ({
environmentSlug,
path,
name,
projectSlug,
actor,
actorId,
actorOrgId,
actorAuthMethod,
ttl
}: TCreateDynamicSecretLeaseDTO) => {
const appCfg = getConfig();
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to create lease due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id });
if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const totalLeasesTaken = await dynamicSecretLeaseDAL.countLeasesForDynamicSecret(dynamicSecretCfg.id);
if (totalLeasesTaken >= appCfg.MAX_LEASE_LIMIT)
throw new BadRequestError({ message: `Max lease limit reached. Limit: ${appCfg.MAX_LEASE_LIMIT}` });
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding,
ciphertext: dynamicSecretCfg.inputCiphertext,
tag: dynamicSecretCfg.inputTag,
iv: dynamicSecretCfg.inputIV
})
) as object;
const selectedTTL = ttl ?? dynamicSecretCfg.defaultTTL;
const { maxTTL } = dynamicSecretCfg;
const expireAt = new Date(new Date().getTime() + ms(selectedTTL));
if (maxTTL) {
const maxExpiryDate = new Date(new Date().getTime() + ms(maxTTL));
if (expireAt > maxExpiryDate) throw new BadRequestError({ message: "TTL cannot be larger than max TTL" });
}
const { entityId, data } = await selectedProvider.create(decryptedStoredInput, expireAt.getTime());
const dynamicSecretLease = await dynamicSecretLeaseDAL.create({
expireAt,
version: 1,
dynamicSecretId: dynamicSecretCfg.id,
externalEntityId: entityId
});
await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, Number(expireAt) - Number(new Date()));
return { lease: dynamicSecretLease, dynamicSecret: dynamicSecretCfg, data };
};
const renewLease = async ({
ttl,
actorAuthMethod,
actorOrgId,
actorId,
actor,
projectSlug,
path,
environmentSlug,
leaseId
}: TRenewDynamicSecretLeaseDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to renew lease due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
if (!dynamicSecretLease) throw new BadRequestError({ message: "Dynamic secret lease not found" });
const dynamicSecretCfg = dynamicSecretLease.dynamicSecret;
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding,
ciphertext: dynamicSecretCfg.inputCiphertext,
tag: dynamicSecretCfg.inputTag,
iv: dynamicSecretCfg.inputIV
})
) as object;
const selectedTTL = ttl ?? dynamicSecretCfg.defaultTTL;
const { maxTTL } = dynamicSecretCfg;
const expireAt = new Date(dynamicSecretLease.expireAt.getTime() + ms(selectedTTL));
if (maxTTL) {
const maxExpiryDate = new Date(dynamicSecretLease.createdAt.getTime() + ms(maxTTL));
if (expireAt > maxExpiryDate) throw new BadRequestError({ message: "TTL cannot be larger than max ttl" });
}
const { entityId } = await selectedProvider.renew(
decryptedStoredInput,
dynamicSecretLease.externalEntityId,
expireAt.getTime()
);
await dynamicSecretQueueService.unsetLeaseRevocation(dynamicSecretLease.id);
await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, Number(expireAt) - Number(new Date()));
const updatedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, {
expireAt,
externalEntityId: entityId
});
return updatedDynamicSecretLease;
};
const revokeLease = async ({
leaseId,
environmentSlug,
path,
projectSlug,
actor,
actorId,
actorOrgId,
actorAuthMethod,
isForced
}: TDeleteDynamicSecretLeaseDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
if (!dynamicSecretLease) throw new BadRequestError({ message: "Dynamic secret lease not found" });
const dynamicSecretCfg = dynamicSecretLease.dynamicSecret;
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding,
ciphertext: dynamicSecretCfg.inputCiphertext,
tag: dynamicSecretCfg.inputTag,
iv: dynamicSecretCfg.inputIV
})
) as object;
const revokeResponse = await selectedProvider
.revoke(decryptedStoredInput, dynamicSecretLease.externalEntityId)
.catch(async (err) => {
// only propogate this error if forced is false
if (!isForced) return { error: err as Error };
});
if ((revokeResponse as { error?: Error })?.error) {
const { error } = revokeResponse as { error?: Error };
logger.error("Failed to revoke lease", { error: error?.message });
const deletedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, {
status: DynamicSecretLeaseStatus.FailedDeletion,
statusDetails: error?.message?.slice(0, 255)
});
return deletedDynamicSecretLease;
}
await dynamicSecretQueueService.unsetLeaseRevocation(dynamicSecretLease.id);
const deletedDynamicSecretLease = await dynamicSecretLeaseDAL.deleteById(dynamicSecretLease.id);
return deletedDynamicSecretLease;
};
const listLeases = async ({
path,
name,
actor,
actorId,
projectSlug,
actorOrgId,
environmentSlug,
actorAuthMethod
}: TListDynamicSecretLeasesDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id });
if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const dynamicSecretLeases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id });
return dynamicSecretLeases;
};
const getLeaseDetails = async ({
projectSlug,
actorOrgId,
path,
environmentSlug,
actor,
actorId,
leaseId,
actorAuthMethod
}: TDetailsDynamicSecretLeaseDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
if (!dynamicSecretLease) throw new BadRequestError({ message: "Dynamic secret lease not found" });
return dynamicSecretLease;
};
return {
create,
listLeases,
revokeLease,
renewLease,
getLeaseDetails
};
};

View File

@@ -1,43 +0,0 @@
import { TProjectPermission } from "@app/lib/types";
export enum DynamicSecretLeaseStatus {
FailedDeletion = "Failed to delete"
}
export type TCreateDynamicSecretLeaseDTO = {
name: string;
path: string;
environmentSlug: string;
ttl?: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;
export type TDetailsDynamicSecretLeaseDTO = {
leaseId: string;
path: string;
environmentSlug: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;
export type TListDynamicSecretLeasesDTO = {
name: string;
path: string;
environmentSlug: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;
export type TDeleteDynamicSecretLeaseDTO = {
leaseId: string;
path: string;
environmentSlug: string;
projectSlug: string;
isForced?: boolean;
} & Omit<TProjectPermission, "projectId">;
export type TRenewDynamicSecretLeaseDTO = {
leaseId: string;
path: string;
environmentSlug: string;
ttl?: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;

View File

@@ -1,10 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TDynamicSecretDALFactory = ReturnType<typeof dynamicSecretDALFactory>;
export const dynamicSecretDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.DynamicSecret);
return orm;
};

View File

@@ -1,341 +0,0 @@
import { ForbiddenError, subject } from "@casl/ability";
import { SecretKeyEncoding } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
import {
DynamicSecretStatus,
TCreateDynamicSecretDTO,
TDeleteDynamicSecretDTO,
TDetailsDynamicSecretDTO,
TListDynamicSecretsDTO,
TUpdateDynamicSecretDTO
} from "./dynamic-secret-types";
import { DynamicSecretProviders, TDynamicProviderFns } from "./providers/models";
type TDynamicSecretServiceFactoryDep = {
dynamicSecretDAL: TDynamicSecretDALFactory;
dynamicSecretLeaseDAL: Pick<TDynamicSecretLeaseDALFactory, "find">;
dynamicSecretProviders: Record<DynamicSecretProviders, TDynamicProviderFns>;
dynamicSecretQueueService: Pick<
TDynamicSecretLeaseQueueServiceFactory,
"pruneDynamicSecret" | "unsetLeaseRevocation"
>;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
export type TDynamicSecretServiceFactory = ReturnType<typeof dynamicSecretServiceFactory>;
export const dynamicSecretServiceFactory = ({
dynamicSecretDAL,
dynamicSecretLeaseDAL,
licenseService,
folderDAL,
dynamicSecretProviders,
permissionService,
dynamicSecretQueueService,
projectDAL
}: TDynamicSecretServiceFactoryDep) => {
const create = async ({
path,
actor,
name,
actorId,
maxTTL,
provider,
environmentSlug,
projectSlug,
actorOrgId,
defaultTTL,
actorAuthMethod
}: TCreateDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to create dynamic secret due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const existingDynamicSecret = await dynamicSecretDAL.findOne({ name, folderId: folder.id });
if (existingDynamicSecret)
throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" });
const selectedProvider = dynamicSecretProviders[provider.type];
const inputs = await selectedProvider.validateProviderInputs(provider.inputs);
const isConnected = await selectedProvider.validateConnection(provider.inputs);
if (!isConnected) throw new BadRequestError({ message: "Provider connection failed" });
const encryptedInput = infisicalSymmetricEncypt(JSON.stringify(inputs));
const dynamicSecretCfg = await dynamicSecretDAL.create({
type: provider.type,
version: 1,
inputIV: encryptedInput.iv,
inputTag: encryptedInput.tag,
inputCiphertext: encryptedInput.ciphertext,
algorithm: encryptedInput.algorithm,
keyEncoding: encryptedInput.encoding,
maxTTL,
defaultTTL,
folderId: folder.id,
name
});
return dynamicSecretCfg;
};
const updateByName = async ({
name,
maxTTL,
defaultTTL,
inputs,
environmentSlug,
projectSlug,
path,
actor,
actorId,
newName,
actorOrgId,
actorAuthMethod
}: TUpdateDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to update dynamic secret due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id });
if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
if (newName) {
const existingDynamicSecret = await dynamicSecretDAL.findOne({ name: newName, folderId: folder.id });
if (existingDynamicSecret)
throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" });
}
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding,
ciphertext: dynamicSecretCfg.inputCiphertext,
tag: dynamicSecretCfg.inputTag,
iv: dynamicSecretCfg.inputIV
})
) as object;
const newInput = { ...decryptedStoredInput, ...(inputs || {}) };
const updatedInput = await selectedProvider.validateProviderInputs(newInput);
const isConnected = await selectedProvider.validateConnection(newInput);
if (!isConnected) throw new BadRequestError({ message: "Provider connection failed" });
const encryptedInput = infisicalSymmetricEncypt(JSON.stringify(updatedInput));
const updatedDynamicCfg = await dynamicSecretDAL.updateById(dynamicSecretCfg.id, {
inputIV: encryptedInput.iv,
inputTag: encryptedInput.tag,
inputCiphertext: encryptedInput.ciphertext,
algorithm: encryptedInput.algorithm,
keyEncoding: encryptedInput.encoding,
maxTTL,
defaultTTL,
name: newName ?? name,
status: null,
statusDetails: null
});
return updatedDynamicCfg;
};
const deleteByName = async ({
actorAuthMethod,
actorOrgId,
actorId,
actor,
projectSlug,
name,
path,
environmentSlug,
isForced
}: TDeleteDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id });
if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const leases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id });
// when not forced we check with the external system to first remove the things
// we introduce a forced concept because consider the external lease got deleted by some other external like a human or another system
// this allows user to clean up it from infisical
if (isForced) {
// clear all queues for lease revocations
await Promise.all(leases.map(({ id: leaseId }) => dynamicSecretQueueService.unsetLeaseRevocation(leaseId)));
const deletedDynamicSecretCfg = await dynamicSecretDAL.deleteById(dynamicSecretCfg.id);
return deletedDynamicSecretCfg;
}
// if leases exist we should flag it as deleting and then remove leases in background
// then delete the main one
if (leases.length) {
const updatedDynamicSecretCfg = await dynamicSecretDAL.updateById(dynamicSecretCfg.id, {
status: DynamicSecretStatus.Deleting
});
await dynamicSecretQueueService.pruneDynamicSecret(updatedDynamicSecretCfg.id);
return updatedDynamicSecretCfg;
}
// if no leases just delete the config
const deletedDynamicSecretCfg = await dynamicSecretDAL.deleteById(dynamicSecretCfg.id);
return deletedDynamicSecretCfg;
};
const getDetails = async ({
name,
projectSlug,
path,
environmentSlug,
actorAuthMethod,
actorOrgId,
actorId,
actor
}: TDetailsDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id });
if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding,
ciphertext: dynamicSecretCfg.inputCiphertext,
tag: dynamicSecretCfg.inputTag,
iv: dynamicSecretCfg.inputIV
})
) as object;
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const providerInputs = (await selectedProvider.validateProviderInputs(decryptedStoredInput)) as object;
return { ...dynamicSecretCfg, inputs: providerInputs };
};
const list = async ({
actorAuthMethod,
actorOrgId,
actorId,
actor,
projectSlug,
path,
environmentSlug
}: TListDynamicSecretsDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretCfg = await dynamicSecretDAL.find({ folderId: folder.id });
return dynamicSecretCfg;
};
return {
create,
updateByName,
deleteByName,
getDetails,
list
};
};

View File

@@ -1,54 +0,0 @@
import { z } from "zod";
import { TProjectPermission } from "@app/lib/types";
import { DynamicSecretProviderSchema } from "./providers/models";
// various status for dynamic secret that happens in background
export enum DynamicSecretStatus {
Deleting = "Revocation in process",
FailedDeletion = "Failed to delete"
}
type TProvider = z.infer<typeof DynamicSecretProviderSchema>;
export type TCreateDynamicSecretDTO = {
provider: TProvider;
defaultTTL: string;
maxTTL?: string | null;
path: string;
environmentSlug: string;
name: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;
export type TUpdateDynamicSecretDTO = {
name: string;
newName?: string;
defaultTTL?: string;
maxTTL?: string | null;
path: string;
environmentSlug: string;
inputs?: TProvider["inputs"];
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;
export type TDeleteDynamicSecretDTO = {
name: string;
path: string;
environmentSlug: string;
projectSlug: string;
isForced?: boolean;
} & Omit<TProjectPermission, "projectId">;
export type TDetailsDynamicSecretDTO = {
name: string;
path: string;
environmentSlug: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;
export type TListDynamicSecretsDTO = {
path: string;
environmentSlug: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;

View File

@@ -1,6 +0,0 @@
import { DynamicSecretProviders } from "./models";
import { SqlDatabaseProvider } from "./sql-database";
export const buildDynamicSecretProviders = () => ({
[DynamicSecretProviders.SqlDatabase]: SqlDatabaseProvider()
});

View File

@@ -1,34 +0,0 @@
import { z } from "zod";
export enum SqlProviders {
Postgres = "postgres"
}
export const DynamicSecretSqlDBSchema = z.object({
client: z.nativeEnum(SqlProviders),
host: z.string().toLowerCase(),
port: z.number(),
database: z.string(),
username: z.string(),
password: z.string(),
creationStatement: z.string(),
revocationStatement: z.string(),
renewStatement: z.string(),
ca: z.string().optional()
});
export enum DynamicSecretProviders {
SqlDatabase = "sql-database"
}
export const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal(DynamicSecretProviders.SqlDatabase), inputs: DynamicSecretSqlDBSchema })
]);
export type TDynamicProviderFns = {
create: (inputs: unknown, expireAt: number) => Promise<{ entityId: string; data: unknown }>;
validateConnection: (inputs: unknown) => Promise<boolean>;
validateProviderInputs: (inputs: object) => Promise<unknown>;
revoke: (inputs: unknown, entityId: string) => Promise<{ entityId: string }>;
renew: (inputs: unknown, entityId: string, expireAt: number) => Promise<{ entityId: string }>;
};

View File

@@ -1,123 +0,0 @@
import handlebars from "handlebars";
import knex from "knex";
import { customAlphabet } from "nanoid";
import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { getDbConnectionHost } from "@app/lib/knex";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { DynamicSecretSqlDBSchema, TDynamicProviderFns } from "./models";
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
const generatePassword = (size?: number) => {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#";
return customAlphabet(charset, 48)(size);
};
export const SqlDatabaseProvider = (): TDynamicProviderFns => {
const validateProviderInputs = async (inputs: unknown) => {
const appCfg = getConfig();
const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
if (
// localhost
providerInputs.host === "localhost" ||
providerInputs.host === "127.0.0.1" ||
// database infisical uses
dbHost === providerInputs.host ||
// internal ips
providerInputs.host === "host.docker.internal" ||
providerInputs.host.match(/^10\.\d+\.\d+\.\d+/) ||
providerInputs.host.match(/^192\.168\.\d+\.\d+/)
)
throw new BadRequestError({ message: "Invalid db host" });
return providerInputs;
};
const getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>) => {
const ssl = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
const db = knex({
client: providerInputs.client,
connection: {
database: providerInputs.database,
port: providerInputs.port,
host: providerInputs.host,
user: providerInputs.username,
password: providerInputs.password,
connectionTimeoutMillis: EXTERNAL_REQUEST_TIMEOUT,
ssl,
pool: { min: 0, max: 1 }
}
});
return db;
};
const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs);
const db = await getClient(providerInputs);
const isConnected = await db
.raw("SELECT NOW()")
.then(() => true)
.catch(() => false);
await db.destroy();
return isConnected;
};
const create = async (inputs: unknown, expireAt: number) => {
const providerInputs = await validateProviderInputs(inputs);
const db = await getClient(providerInputs);
const username = alphaNumericNanoId(32);
const password = generatePassword();
const expiration = new Date(expireAt).toISOString();
const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({
username,
password,
expiration
});
await db.raw(creationStatement.toString());
await db.destroy();
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
};
const revoke = async (inputs: unknown, entityId: string) => {
const providerInputs = await validateProviderInputs(inputs);
const db = await getClient(providerInputs);
const username = entityId;
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username });
await db.raw(revokeStatement);
await db.destroy();
return { entityId: username };
};
const renew = async (inputs: unknown, entityId: string, expireAt: number) => {
const providerInputs = await validateProviderInputs(inputs);
const db = await getClient(providerInputs);
const username = entityId;
const expiration = new Date(expireAt).toISOString();
const renewStatement = handlebars.compile(providerInputs.renewStatement)({ username, expiration });
await db.raw(renewStatement);
await db.destroy();
return { entityId: username };
};
return {
validateProviderInputs,
validateConnection,
create,
revoke,
renew
};
};

View File

@@ -1,12 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TIdentityProjectAdditionalPrivilegeDALFactory = ReturnType<
typeof identityProjectAdditionalPrivilegeDALFactory
>;
export const identityProjectAdditionalPrivilegeDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.IdentityProjectAdditionalPrivilege);
return orm;
};

View File

@@ -1,297 +0,0 @@
import { ForbiddenError } from "@casl/ability";
import ms from "ms";
import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
import { ActorType } from "@app/services/auth/auth-type";
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
import {
IdentityProjectAdditionalPrivilegeTemporaryMode,
TCreateIdentityPrivilegeDTO,
TDeleteIdentityPrivilegeDTO,
TGetIdentityPrivilegeDetailsDTO,
TListIdentityPrivilegesDTO,
TUpdateIdentityPrivilegeDTO
} from "./identity-project-additional-privilege-types";
type TIdentityProjectAdditionalPrivilegeServiceFactoryDep = {
identityProjectAdditionalPrivilegeDAL: TIdentityProjectAdditionalPrivilegeDALFactory;
identityProjectDAL: Pick<TIdentityProjectDALFactory, "findOne" | "findById">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
typeof identityProjectAdditionalPrivilegeServiceFactory
>;
export const identityProjectAdditionalPrivilegeServiceFactory = ({
identityProjectAdditionalPrivilegeDAL,
identityProjectDAL,
permissionService,
projectDAL
}: TIdentityProjectAdditionalPrivilegeServiceFactoryDep) => {
const create = async ({
slug,
actor,
actorId,
identityId,
projectSlug,
permissions: customPermission,
actorOrgId,
actorAuthMethod,
...dto
}: TCreateIdentityPrivilegeDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
if (!identityProjectMembership)
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
slug,
projectMembershipId: identityProjectMembership.id
});
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
if (!dto.isTemporary) {
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id,
slug,
permissions: customPermission
});
return additionalPrivilege;
}
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id,
slug,
permissions: customPermission,
isTemporary: true,
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: dto.temporaryRange,
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
});
return additionalPrivilege;
};
const updateBySlug = async ({
projectSlug,
slug,
identityId,
data,
actorOrgId,
actor,
actorId,
actorAuthMethod
}: TUpdateIdentityPrivilegeDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
if (!identityProjectMembership)
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityProjectMembership.identityId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
slug,
projectMembershipId: identityProjectMembership.id
});
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
if (data?.slug) {
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
slug: data.slug,
projectMembershipId: identityProjectMembership.id
});
if (existingSlug && existingSlug.id !== identityPrivilege.id)
throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
}
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
if (isTemporary) {
const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange;
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
...data,
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
});
return additionalPrivilege;
}
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
...data,
isTemporary: false,
temporaryAccessStartTime: null,
temporaryAccessEndTime: null,
temporaryRange: null,
temporaryMode: null
});
return additionalPrivilege;
};
const deleteBySlug = async ({
actorId,
slug,
identityId,
projectSlug,
actor,
actorOrgId,
actorAuthMethod
}: TDeleteIdentityPrivilegeDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
if (!identityProjectMembership)
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityProjectMembership.identityId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to edit more privileged identity" });
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
slug,
projectMembershipId: identityProjectMembership.id
});
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
return deletedPrivilege;
};
const getPrivilegeDetailsBySlug = async ({
projectSlug,
identityId,
slug,
actorOrgId,
actor,
actorId,
actorAuthMethod
}: TGetIdentityPrivilegeDetailsDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
if (!identityProjectMembership)
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
slug,
projectMembershipId: identityProjectMembership.id
});
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
return identityPrivilege;
};
const listIdentityProjectPrivileges = async ({
identityId,
actorOrgId,
actor,
actorId,
actorAuthMethod,
projectSlug
}: TListIdentityPrivilegesDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
const projectId = project.id;
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
if (!identityProjectMembership)
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
projectMembershipId: identityProjectMembership.id
});
return identityPrivileges;
};
return {
create,
updateBySlug,
deleteBySlug,
getPrivilegeDetailsBySlug,
listIdentityProjectPrivileges
};
};

View File

@@ -1,54 +0,0 @@
import { TProjectPermission } from "@app/lib/types";
export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
Relative = "relative"
}
export type TCreateIdentityPrivilegeDTO = {
permissions: unknown;
identityId: string;
projectSlug: string;
slug: string;
} & (
| {
isTemporary: false;
}
| {
isTemporary: true;
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
temporaryRange: string;
temporaryAccessStartTime: string;
}
) &
Omit<TProjectPermission, "projectId">;
export type TUpdateIdentityPrivilegeDTO = { slug: string; identityId: string; projectSlug: string } & Omit<
TProjectPermission,
"projectId"
> & {
data: Partial<{
permissions: unknown;
slug: string;
isTemporary: boolean;
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
temporaryRange: string;
temporaryAccessStartTime: string;
}>;
};
export type TDeleteIdentityPrivilegeDTO = Omit<TProjectPermission, "projectId"> & {
slug: string;
identityId: string;
projectSlug: string;
};
export type TGetIdentityPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & {
slug: string;
identityId: string;
projectSlug: string;
};
export type TListIdentityPrivilegesDTO = Omit<TProjectPermission, "projectId"> & {
identityId: string;
projectSlug: string;
};

View File

@@ -55,7 +55,6 @@ export const ldapConfigServiceFactory = ({
actorId,
orgId,
actorOrgId,
actorAuthMethod,
isActive,
url,
bindDN,
@@ -63,7 +62,7 @@ export const ldapConfigServiceFactory = ({
searchBase,
caCert
}: TCreateLdapCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
const plan = await licenseService.getPlan(orgId);
@@ -150,14 +149,13 @@ export const ldapConfigServiceFactory = ({
orgId,
actorOrgId,
isActive,
actorAuthMethod,
url,
bindDN,
bindPass,
searchBase,
caCert
}: TUpdateLdapCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
const plan = await licenseService.getPlan(orgId);
@@ -276,14 +274,8 @@ export const ldapConfigServiceFactory = ({
};
};
const getLdapCfgWithPermissionCheck = async ({
actor,
actorId,
orgId,
actorAuthMethod,
actorOrgId
}: TOrgPermission) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getLdapCfgWithPermissionCheck = async ({ actor, actorId, orgId, actorOrgId }: TOrgPermission) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
return getLdapCfg({
orgId

View File

@@ -17,7 +17,7 @@ export const getDefaultOnPremFeatures = () => {
customAlerts: false,
auditLogs: false,
auditLogsRetentionDays: 0,
samlSSO: false,
samlSSO: true,
scim: false,
ldap: false,
status: null,

View File

@@ -15,7 +15,6 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
membersUsed: 0,
environmentLimit: null,
environmentsUsed: 0,
dynamicSecret: false,
secretVersioning: true,
pitRecovery: false,
ipAllowlisting: false,
@@ -24,7 +23,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
customAlerts: false,
auditLogs: false,
auditLogsRetentionDays: 0,
samlSSO: false,
samlSSO: true,
scim: false,
ldap: false,
status: null,

View File

@@ -224,10 +224,9 @@ export const licenseServiceFactory = ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
billingCycle
}: TOrgPlansTableDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const { data } = await licenseServerCloudApi.request.get(
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
@@ -235,22 +234,15 @@ export const licenseServiceFactory = ({
return data;
};
const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, actorAuthMethod, projectId }: TOrgPlanDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, projectId }: TOrgPlanDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const plan = await getPlan(orgId, projectId);
return plan;
};
const startOrgTrial = async ({
orgId,
actorId,
actor,
actorOrgId,
actorAuthMethod,
success_url
}: TStartOrgTrialDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const startOrgTrial = async ({ orgId, actorId, actor, actorOrgId, success_url }: TStartOrgTrialDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
@@ -271,14 +263,8 @@ export const licenseServiceFactory = ({
return { url };
};
const createOrganizationPortalSession = async ({
orgId,
actorId,
actor,
actorAuthMethod,
actorOrgId
}: TCreateOrgPortalSession) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const createOrganizationPortalSession = async ({ orgId, actorId, actor, actorOrgId }: TCreateOrgPortalSession) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
@@ -324,8 +310,8 @@ export const licenseServiceFactory = ({
return { url };
};
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -341,8 +327,8 @@ export const licenseServiceFactory = ({
};
// returns org current plan feature table
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgPlanTable = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -357,8 +343,8 @@ export const licenseServiceFactory = ({
return data;
};
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -378,12 +364,11 @@ export const licenseServiceFactory = ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
orgId,
name,
email
}: TUpdateOrgBillingDetailsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -402,8 +387,8 @@ export const licenseServiceFactory = ({
return data;
};
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorOrgId }: TOrgPmtMethodsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -425,12 +410,11 @@ export const licenseServiceFactory = ({
orgId,
actor,
actorId,
actorAuthMethod,
actorOrgId,
success_url,
cancel_url
}: TAddOrgPmtMethodDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -451,15 +435,8 @@ export const licenseServiceFactory = ({
return { url };
};
const delOrgPmtMethods = async ({
actorId,
actor,
actorAuthMethod,
actorOrgId,
orgId,
pmtMethodId
}: TDelOrgPmtMethodDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const delOrgPmtMethods = async ({ actorId, actor, actorOrgId, orgId, pmtMethodId }: TDelOrgPmtMethodDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -475,8 +452,8 @@ export const licenseServiceFactory = ({
return data;
};
const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgTaxIds = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -493,8 +470,8 @@ export const licenseServiceFactory = ({
return taxIds;
};
const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const addOrgTaxId = async ({ actorId, actor, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -514,8 +491,8 @@ export const licenseServiceFactory = ({
return data;
};
const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const delOrgTaxId = async ({ orgId, actor, actorId, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -531,8 +508,8 @@ export const licenseServiceFactory = ({
return data;
};
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, orgId }: TOrgInvoiceDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);
@@ -548,8 +525,8 @@ export const licenseServiceFactory = ({
return invoices;
};
const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgLicenses = async ({ orgId, actor, actorId, actorOrgId }: TOrgLicensesDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId);

View File

@@ -27,7 +27,6 @@ export type TFeatureSet = {
tier: -1;
workspaceLimit: null;
workspacesUsed: 0;
dynamicSecret: false;
memberLimit: null;
membersUsed: 0;
environmentLimit: null;
@@ -40,7 +39,7 @@ export type TFeatureSet = {
customAlerts: false;
auditLogs: false;
auditLogsRetentionDays: 0;
samlSSO: false;
samlSSO: true;
scim: false;
ldap: false;
status: null;

View File

@@ -56,11 +56,6 @@ export const permissionDALFactory = (db: TDbClient) => {
`${TableName.ProjectUserMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.leftJoin(
TableName.ProjectUserAdditionalPrivilege,
`${TableName.ProjectUserAdditionalPrivilege}.projectMembershipId`,
`${TableName.ProjectMembership}.id`
)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
.where("userId", userId)
@@ -74,22 +69,9 @@ export const permissionDALFactory = (db: TDbClient) => {
db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"),
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
db.ref("orgId").withSchema(TableName.Project),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
db.ref("permissions").withSchema(TableName.ProjectRoles),
db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"),
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApPermissions"),
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"),
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessStartTime"),
db
.ref("temporaryAccessEndTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessEndTime")
);
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
)
.select("permissions");
const permission = sqlNestRelationships({
data: docs,
@@ -120,44 +102,15 @@ export const permissionDALFactory = (db: TDbClient) => {
permissions: z.unknown(),
customRoleSlug: z.string().optional().nullable()
}).parse(data)
},
{
key: "userApId",
label: "additionalPrivileges" as const,
mapper: ({
userApId,
userApPermissions,
userApIsTemporary,
userApTemporaryMode,
userApTemporaryRange,
userApTemporaryAccessEndTime,
userApTemporaryAccessStartTime
}) => ({
id: userApId,
permissions: userApPermissions,
temporaryRange: userApTemporaryRange,
temporaryMode: userApTemporaryMode,
temporaryAccessEndTime: userApTemporaryAccessEndTime,
temporaryAccessStartTime: userApTemporaryAccessStartTime,
isTemporary: userApIsTemporary
})
}
]
});
if (!permission?.[0]) return undefined;
// when introducting cron mode change it here
const activeRoles = permission?.[0]?.roles?.filter(
const activeRoles = permission?.[0]?.roles.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
);
const activeAdditionalPrivileges = permission?.[0]?.additionalPrivileges?.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
);
return { ...permission[0], roles: activeRoles, additionalPrivileges: activeAdditionalPrivileges };
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
} catch (error) {
throw new DatabaseError({ error, name: "GetProjectPermission" });
}
@@ -176,62 +129,31 @@ export const permissionDALFactory = (db: TDbClient) => {
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.leftJoin(
TableName.IdentityProjectAdditionalPrivilege,
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`,
`${TableName.IdentityProjectMembership}.id`
)
.join(
// Join the Project table to later select orgId
TableName.Project,
`${TableName.IdentityProjectMembership}.projectId`,
`${TableName.Project}.id`
)
.where("identityId", identityId)
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
.select(selectAllTableCols(TableName.IdentityProjectMembershipRole))
.select(
db.ref("id").withSchema(TableName.IdentityProjectMembership).as("membershipId"),
db.ref("orgId").withSchema(TableName.Project).as("orgId"), // Now you can select orgId from Project
db.ref("role").withSchema(TableName.IdentityProjectMembership).as("oldRoleField"),
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership).as("membershipCreatedAt"),
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership).as("membershipUpdatedAt"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
db.ref("permissions").withSchema(TableName.ProjectRoles),
db.ref("id").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApId"),
db.ref("permissions").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApPermissions"),
db
.ref("temporaryMode")
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
.as("identityApTemporaryMode"),
db.ref("isTemporary").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApIsTemporary"),
db
.ref("temporaryRange")
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
.as("identityApTemporaryRange"),
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
.as("identityApTemporaryAccessStartTime"),
db
.ref("temporaryAccessEndTime")
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
.as("identityApTemporaryAccessEndTime")
);
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
)
.select("permissions");
const permission = sqlNestRelationships({
data: docs,
key: "membershipId",
parentMapper: ({ membershipId, membershipCreatedAt, membershipUpdatedAt, oldRoleField, orgId }) => ({
parentMapper: ({ membershipId, membershipCreatedAt, membershipUpdatedAt, oldRoleField }) => ({
id: membershipId,
identityId,
projectId,
role: oldRoleField,
createdAt: membershipCreatedAt,
updatedAt: membershipUpdatedAt,
orgId,
// just a prefilled value
orgAuthEnforced: false
orgAuthEnforced: false,
orgId: ""
}),
childrenMapper: [
{
@@ -242,44 +164,16 @@ export const permissionDALFactory = (db: TDbClient) => {
permissions: z.unknown(),
customRoleSlug: z.string().optional().nullable()
}).parse(data)
},
{
key: "identityApId",
label: "additionalPrivileges" as const,
mapper: ({
identityApId,
identityApPermissions,
identityApIsTemporary,
identityApTemporaryMode,
identityApTemporaryRange,
identityApTemporaryAccessEndTime,
identityApTemporaryAccessStartTime
}) => ({
id: identityApId,
permissions: identityApPermissions,
temporaryRange: identityApTemporaryRange,
temporaryMode: identityApTemporaryMode,
temporaryAccessEndTime: identityApTemporaryAccessEndTime,
temporaryAccessStartTime: identityApTemporaryAccessStartTime,
isTemporary: identityApIsTemporary
})
}
]
});
if (!permission?.[0]) return undefined;
// when introducting cron mode change it here
const activeRoles = permission?.[0]?.roles.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
);
const activeAdditionalPrivileges = permission?.[0]?.additionalPrivileges?.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
);
return { ...permission[0], roles: activeRoles, additionalPrivileges: activeAdditionalPrivileges };
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
} catch (error) {
throw new DatabaseError({ error, name: "GetProjectIdentityPermission" });
}

View File

@@ -1,27 +0,0 @@
import { TOrganizations } from "@app/db/schemas";
import { UnauthorizedError } from "@app/lib/errors";
import { ActorAuthMethod, AuthMethod } from "@app/services/auth/auth-type";
function isAuthMethodSaml(actorAuthMethod: ActorAuthMethod) {
if (!actorAuthMethod) return false;
return [
AuthMethod.AZURE_SAML,
AuthMethod.OKTA_SAML,
AuthMethod.JUMPCLOUD_SAML,
AuthMethod.GOOGLE_SAML,
AuthMethod.KEYCLOAK_SAML
].includes(actorAuthMethod);
}
function validateOrgSAML(actorAuthMethod: ActorAuthMethod, isSamlEnforced: TOrganizations["authEnforced"]) {
if (actorAuthMethod === undefined) {
throw new UnauthorizedError({ name: "No auth method defined" });
}
if (isSamlEnforced && actorAuthMethod !== null && !isAuthMethodSaml(actorAuthMethod)) {
throw new UnauthorizedError({ name: "Cannot access org-scoped resource" });
}
}
export { isAuthMethodSaml, validateOrgSAML };

View File

@@ -11,15 +11,13 @@ import {
} from "@app/db/schemas";
import { conditionsMatcher } from "@app/lib/casl";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { ActorType } from "@app/services/auth/auth-type";
import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectRoleDALFactory } from "@app/services/project-role/project-role-dal";
import { TServiceTokenDALFactory } from "@app/services/service-token/service-token-dal";
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
import { TPermissionDALFactory } from "./permission-dal";
import { validateOrgSAML } from "./permission-fns";
import { TBuildProjectPermissionDTO } from "./permission-types";
import {
buildServiceTokenProjectPermission,
@@ -34,7 +32,6 @@ type TPermissionServiceFactoryDep = {
orgRoleDAL: Pick<TOrgRoleDALFactory, "findOne">;
projectRoleDAL: Pick<TProjectRoleDALFactory, "findOne">;
serviceTokenDAL: Pick<TServiceTokenDALFactory, "findById">;
projectDAL: Pick<TProjectDALFactory, "findById">;
permissionDAL: TPermissionDALFactory;
};
@@ -44,8 +41,7 @@ export const permissionServiceFactory = ({
permissionDAL,
orgRoleDAL,
projectRoleDAL,
serviceTokenDAL,
projectDAL
serviceTokenDAL
}: TPermissionServiceFactoryDep) => {
const buildOrgPermission = (role: string, permission?: unknown) => {
switch (role) {
@@ -102,30 +98,16 @@ export const permissionServiceFactory = ({
/*
* Get user permission in an organization
*/
const getUserOrgPermission = async (
userId: string,
orgId: string,
authMethod: ActorAuthMethod,
userOrgId?: string
) => {
* */
const getUserOrgPermission = async (userId: string, orgId: string, userOrgId?: string) => {
const membership = await permissionDAL.getOrgPermission(userId, orgId);
if (!membership) throw new UnauthorizedError({ name: "User not in org" });
if (membership.role === OrgMembershipRole.Custom && !membership.permissions) {
throw new BadRequestError({ name: "Custom permission not found" });
}
// If the org ID is API_KEY, the request is being made with an API Key.
// Since we can't scope API keys to an organization, we'll need to do an arbitrary check to see if the user is a member of the organization.
// Extra: This means that when users are using API keys to make requests, they can't use slug-based routes.
// Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization.
if (userOrgId !== "API_KEY" && membership.orgId !== userOrgId) {
throw new UnauthorizedError({ name: "You are not logged into this organization" });
if (membership.orgAuthEnforced && membership.orgId !== userOrgId) {
throw new BadRequestError({ name: "Cannot access org-scoped resource" });
}
validateOrgSAML(authMethod, membership.orgAuthEnforced);
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
};
@@ -138,16 +120,10 @@ export const permissionServiceFactory = ({
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
};
const getOrgPermission = async (
type: ActorType,
id: string,
orgId: string,
authMethod: ActorAuthMethod,
actorOrgId: string | undefined
) => {
const getOrgPermission = async (type: ActorType, id: string, orgId: string, actorOrgId?: string) => {
switch (type) {
case ActorType.USER:
return getUserOrgPermission(id, orgId, authMethod, actorOrgId);
return getUserOrgPermission(id, orgId, actorOrgId);
case ActorType.IDENTITY:
return getIdentityOrgPermission(id, orgId);
default:
@@ -177,7 +153,6 @@ export const permissionServiceFactory = ({
const getUserProjectPermission = async (
userId: string,
projectId: string,
authMethod: ActorAuthMethod,
userOrgId?: string
): Promise<TProjectPermissionRT<ActorType.USER>> => {
const userProjectPermission = await permissionDAL.getProjectPermission(userId, projectId);
@@ -189,27 +164,12 @@ export const permissionServiceFactory = ({
throw new BadRequestError({ name: "Custom permission not found" });
}
// If the org ID is API_KEY, the request is being made with an API Key.
// Since we can't scope API keys to an organization, we'll need to do an arbitrary check to see if the user is a member of the organization.
// Extra: This means that when users are using API keys to make requests, they can't use slug-based routes.
// Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization.
if (userOrgId !== "API_KEY" && userProjectPermission.orgId !== userOrgId) {
throw new UnauthorizedError({ name: "You are not logged into this organization" });
if (userProjectPermission.orgAuthEnforced && userProjectPermission.orgId !== userOrgId) {
throw new BadRequestError({ name: "Cannot access org-scoped resource" });
}
validateOrgSAML(authMethod, userProjectPermission.orgAuthEnforced);
// join two permissions and pass to build the final permission set
const rolePermissions = userProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
const additionalPrivileges =
userProjectPermission.additionalPrivileges?.map(({ permissions }) => ({
role: ProjectMembershipRole.Custom,
permissions
})) || [];
return {
permission: buildProjectPermission(rolePermissions.concat(additionalPrivileges)),
permission: buildProjectPermission(userProjectPermission.roles),
membership: userProjectPermission,
hasRole: (role: string) =>
userProjectPermission.roles.findIndex(
@@ -220,8 +180,7 @@ export const permissionServiceFactory = ({
const getIdentityProjectPermission = async (
identityId: string,
projectId: string,
identityOrgId: string | undefined
projectId: string
): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
if (!identityProjectPermission) throw new UnauthorizedError({ name: "Identity not in project" });
@@ -234,20 +193,8 @@ export const permissionServiceFactory = ({
throw new BadRequestError({ name: "Custom permission not found" });
}
if (identityProjectPermission.orgId !== identityOrgId) {
throw new UnauthorizedError({ name: "You are not a member of this organization" });
}
const rolePermissions =
identityProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
const additionalPrivileges =
identityProjectPermission.additionalPrivileges?.map(({ permissions }) => ({
role: ProjectMembershipRole.Custom,
permissions
})) || [];
return {
permission: buildProjectPermission(rolePermissions.concat(additionalPrivileges)),
permission: buildProjectPermission(identityProjectPermission.roles),
membership: identityProjectPermission,
hasRole: (role: string) =>
identityProjectPermission.roles.findIndex(
@@ -256,32 +203,14 @@ export const permissionServiceFactory = ({
};
};
const getServiceTokenProjectPermission = async (
serviceTokenId: string,
projectId: string,
actorOrgId: string | undefined
) => {
const getServiceTokenProjectPermission = async (serviceTokenId: string, projectId: string) => {
const serviceToken = await serviceTokenDAL.findById(serviceTokenId);
if (!serviceToken) throw new BadRequestError({ message: "Service token not found" });
const serviceTokenProject = await projectDAL.findById(serviceToken.projectId);
if (!serviceTokenProject) throw new BadRequestError({ message: "Service token not linked to a project" });
if (serviceTokenProject.orgId !== actorOrgId) {
throw new UnauthorizedError({ message: "Service token not a part of this organization" });
}
if (serviceToken.projectId !== projectId)
throw new UnauthorizedError({
message: "Failed to find service authorization for given project"
});
if (serviceTokenProject.orgId !== actorOrgId)
throw new UnauthorizedError({
message: "Failed to find service authorization for given project"
});
const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []);
return {
permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions),
@@ -309,16 +238,15 @@ export const permissionServiceFactory = ({
type: T,
id: string,
projectId: string,
actorAuthMethod: ActorAuthMethod,
actorOrgId: string | undefined
actorOrgId?: string
): Promise<TProjectPermissionRT<T>> => {
switch (type) {
case ActorType.USER:
return getUserProjectPermission(id, projectId, actorAuthMethod, actorOrgId) as Promise<TProjectPermissionRT<T>>;
return getUserProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
case ActorType.SERVICE:
return getServiceTokenProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
return getServiceTokenProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>;
case ActorType.IDENTITY:
return getIdentityProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
return getIdentityProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>;
default:
throw new UnauthorizedError({
message: "Permission not defined",

View File

@@ -1,10 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TProjectUserAdditionalPrivilegeDALFactory = ReturnType<typeof projectUserAdditionalPrivilegeDALFactory>;
export const projectUserAdditionalPrivilegeDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.ProjectUserAdditionalPrivilege);
return orm;
};

View File

@@ -1,212 +0,0 @@
import { ForbiddenError } from "@casl/ability";
import ms from "ms";
import { BadRequestError } from "@app/lib/errors";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TProjectUserAdditionalPrivilegeDALFactory } from "./project-user-additional-privilege-dal";
import {
ProjectUserAdditionalPrivilegeTemporaryMode,
TCreateUserPrivilegeDTO,
TDeleteUserPrivilegeDTO,
TGetUserPrivilegeDetailsDTO,
TListUserPrivilegesDTO,
TUpdateUserPrivilegeDTO
} from "./project-user-additional-privilege-types";
type TProjectUserAdditionalPrivilegeServiceFactoryDep = {
projectUserAdditionalPrivilegeDAL: TProjectUserAdditionalPrivilegeDALFactory;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
export type TProjectUserAdditionalPrivilegeServiceFactory = ReturnType<
typeof projectUserAdditionalPrivilegeServiceFactory
>;
export const projectUserAdditionalPrivilegeServiceFactory = ({
projectUserAdditionalPrivilegeDAL,
projectMembershipDAL,
permissionService
}: TProjectUserAdditionalPrivilegeServiceFactoryDep) => {
const create = async ({
slug,
actor,
actorId,
permissions: customPermission,
actorOrgId,
actorAuthMethod,
projectMembershipId,
...dto
}: TCreateUserPrivilegeDTO) => {
const projectMembership = await projectMembershipDAL.findById(projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
const existingSlug = await projectUserAdditionalPrivilegeDAL.findOne({ slug, projectMembershipId });
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
if (!dto.isTemporary) {
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({
projectMembershipId,
slug,
permissions: customPermission
});
return additionalPrivilege;
}
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({
projectMembershipId,
slug,
permissions: customPermission,
isTemporary: true,
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: dto.temporaryRange,
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
});
return additionalPrivilege;
};
const updateById = async ({
privilegeId,
actorOrgId,
actor,
actorId,
actorAuthMethod,
...dto
}: TUpdateUserPrivilegeDTO) => {
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
if (dto?.slug) {
const existingSlug = await projectUserAdditionalPrivilegeDAL.findOne({
slug: dto.slug,
projectMembershipId: projectMembership.id
});
if (existingSlug && existingSlug.id !== userPrivilege.id)
throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
}
const isTemporary = typeof dto?.isTemporary !== "undefined" ? dto.isTemporary : userPrivilege.isTemporary;
if (isTemporary) {
const temporaryAccessStartTime = dto?.temporaryAccessStartTime || userPrivilege?.temporaryAccessStartTime;
const temporaryRange = dto?.temporaryRange || userPrivilege?.temporaryRange;
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
...dto,
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
});
return additionalPrivilege;
}
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
...dto,
isTemporary: false,
temporaryAccessStartTime: null,
temporaryAccessEndTime: null,
temporaryRange: null,
temporaryMode: null
});
return additionalPrivilege;
};
const deleteById = async ({ actorId, actor, actorOrgId, actorAuthMethod, privilegeId }: TDeleteUserPrivilegeDTO) => {
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
const deletedPrivilege = await projectUserAdditionalPrivilegeDAL.deleteById(userPrivilege.id);
return deletedPrivilege;
};
const getPrivilegeDetailsById = async ({
privilegeId,
actorOrgId,
actor,
actorId,
actorAuthMethod
}: TGetUserPrivilegeDetailsDTO) => {
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
return userPrivilege;
};
const listPrivileges = async ({
projectMembershipId,
actorOrgId,
actor,
actorId,
actorAuthMethod
}: TListUserPrivilegesDTO) => {
const projectMembership = await projectMembershipDAL.findById(projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectMembership.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
const userPrivileges = await projectUserAdditionalPrivilegeDAL.find({ projectMembershipId });
return userPrivileges;
};
return {
create,
updateById,
deleteById,
getPrivilegeDetailsById,
listPrivileges
};
};

View File

@@ -1,40 +0,0 @@
import { TProjectPermission } from "@app/lib/types";
export enum ProjectUserAdditionalPrivilegeTemporaryMode {
Relative = "relative"
}
export type TCreateUserPrivilegeDTO = (
| {
permissions: unknown;
projectMembershipId: string;
slug: string;
isTemporary: false;
}
| {
permissions: unknown;
projectMembershipId: string;
slug: string;
isTemporary: true;
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative;
temporaryRange: string;
temporaryAccessStartTime: string;
}
) &
Omit<TProjectPermission, "projectId">;
export type TUpdateUserPrivilegeDTO = { privilegeId: string } & Omit<TProjectPermission, "projectId"> &
Partial<{
permissions: unknown;
slug: string;
isTemporary: boolean;
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative;
temporaryRange: string;
temporaryAccessStartTime: string;
}>;
export type TDeleteUserPrivilegeDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
export type TGetUserPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
export type TListUserPrivilegesDTO = Omit<TProjectPermission, "projectId"> & { projectMembershipId: string };

View File

@@ -55,7 +55,6 @@ export const samlConfigServiceFactory = ({
const createSamlCfg = async ({
cert,
actor,
actorAuthMethod,
actorOrgId,
orgId,
issuer,
@@ -64,7 +63,7 @@ export const samlConfigServiceFactory = ({
entryPoint,
authProvider
}: TCreateSamlCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
const plan = await licenseService.getPlan(orgId);
@@ -147,7 +146,6 @@ export const samlConfigServiceFactory = ({
orgId,
actor,
actorOrgId,
actorAuthMethod,
cert,
actorId,
issuer,
@@ -155,7 +153,7 @@ export const samlConfigServiceFactory = ({
entryPoint,
authProvider
}: TUpdateSamlCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
const plan = await licenseService.getPlan(orgId);
if (!plan.samlSSO)
@@ -240,7 +238,6 @@ export const samlConfigServiceFactory = ({
dto.actor,
dto.actorId,
ssoConfig.orgId,
dto.actorAuthMethod,
dto.actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
@@ -319,11 +316,6 @@ export const samlConfigServiceFactory = ({
const organization = await orgDAL.findOrgById(orgId);
if (!organization) throw new BadRequestError({ message: "Org not found" });
// TODO(dangtony98): remove this after aliases update
if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) {
throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" });
}
if (user) {
await userDAL.transaction(async (tx) => {
const [orgMembership] = await orgDAL.findMembership(

View File

@@ -1,12 +1,11 @@
import { TOrgPermission } from "@app/lib/types";
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { ActorType } from "@app/services/auth/auth-type";
export enum SamlProviders {
OKTA_SAML = "okta-saml",
AZURE_SAML = "azure-saml",
JUMPCLOUD_SAML = "jumpcloud-saml",
GOOGLE_SAML = "google-saml",
KEYCLOAK_SAML = "keycloak-saml"
GOOGLE_SAML = "google-saml"
}
export type TCreateSamlCfgDTO = {
@@ -27,14 +26,7 @@ export type TUpdateSamlCfgDTO = Partial<{
TOrgPermission;
export type TGetSamlCfgDTO =
| {
type: "org";
orgId: string;
actor: ActorType;
actorId: string;
actorAuthMethod: ActorAuthMethod;
actorOrgId: string | undefined;
}
| { type: "org"; orgId: string; actor: ActorType; actorId: string; actorOrgId?: string }
| {
type: "orgSlug";
orgSlug: string;

View File

@@ -56,16 +56,8 @@ export const scimServiceFactory = ({
permissionService,
smtpService
}: TScimServiceFactoryDep) => {
const createScimToken = async ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
orgId,
description,
ttlDays
}: TCreateScimTokenDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const createScimToken = async ({ actor, actorId, actorOrgId, orgId, description, ttlDays }: TCreateScimTokenDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Scim);
const plan = await licenseService.getPlan(orgId);
@@ -93,8 +85,8 @@ export const scimServiceFactory = ({
return { scimToken };
};
const listScimTokens = async ({ actor, actorId, actorOrgId, actorAuthMethod, orgId }: TOrgPermission) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const listScimTokens = async ({ actor, actorId, actorOrgId, orgId }: TOrgPermission) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Scim);
const plan = await licenseService.getPlan(orgId);
@@ -107,17 +99,11 @@ export const scimServiceFactory = ({
return scimTokens;
};
const deleteScimToken = async ({ scimTokenId, actor, actorId, actorAuthMethod, actorOrgId }: TDeleteScimTokenDTO) => {
const deleteScimToken = async ({ scimTokenId, actor, actorId, actorOrgId }: TDeleteScimTokenDTO) => {
let scimToken = await scimDAL.findById(scimTokenId);
if (!scimToken) throw new BadRequestError({ message: "Failed to find SCIM token to delete" });
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
scimToken.orgId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getOrgPermission(actor, actorId, scimToken.orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Scim);
const plan = await licenseService.getPlan(scimToken.orgId);

View File

@@ -45,7 +45,6 @@ export const secretApprovalPolicyServiceFactory = ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
approvals,
approvers,
projectId,
@@ -55,13 +54,7 @@ export const secretApprovalPolicyServiceFactory = ({
if (approvals > approvers.length)
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretApproval
@@ -105,7 +98,6 @@ export const secretApprovalPolicyServiceFactory = ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
approvals,
secretPolicyId
}: TUpdateSapDTO) => {
@@ -116,7 +108,6 @@ export const secretApprovalPolicyServiceFactory = ({
actor,
actorId,
secretApprovalPolicy.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
@@ -161,13 +152,7 @@ export const secretApprovalPolicyServiceFactory = ({
};
};
const deleteSecretApprovalPolicy = async ({
secretPolicyId,
actor,
actorId,
actorAuthMethod,
actorOrgId
}: TDeleteSapDTO) => {
const deleteSecretApprovalPolicy = async ({ secretPolicyId, actor, actorId, actorOrgId }: TDeleteSapDTO) => {
const sapPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId);
if (!sapPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
@@ -175,7 +160,6 @@ export const secretApprovalPolicyServiceFactory = ({
actor,
actorId,
sapPolicy.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
@@ -187,20 +171,8 @@ export const secretApprovalPolicyServiceFactory = ({
return sapPolicy;
};
const getSecretApprovalPolicyByProjectId = async ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
projectId
}: TListSapDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const getSecretApprovalPolicyByProjectId = async ({ actorId, actor, actorOrgId, projectId }: TListSapDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
const sapPolicies = await secretApprovalPolicyDAL.find({ projectId });
@@ -229,17 +201,10 @@ export const secretApprovalPolicyServiceFactory = ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
environment,
secretPath
}: TGetBoardSapDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { secretPath, environment })

View File

@@ -82,14 +82,13 @@ export const secretApprovalRequestServiceFactory = ({
secretVersionDAL,
secretQueueService
}: TSecretApprovalRequestServiceFactoryDep) => {
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
const requestCount = async ({ projectId, actor, actorId, actorOrgId }: TApprovalRequestCountDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
const { membership } = await permissionService.getProjectPermission(
actor as ActorType.USER,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
@@ -101,7 +100,6 @@ export const secretApprovalRequestServiceFactory = ({
projectId,
actorId,
actor,
actorAuthMethod,
actorOrgId,
status,
environment,
@@ -111,13 +109,7 @@ 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
);
const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
const approvals = await secretApprovalRequestDAL.findByProjectId({
projectId,
committer,
@@ -130,13 +122,7 @@ export const secretApprovalRequestServiceFactory = ({
return approvals;
};
const getSecretApprovalDetails = async ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
id
}: TSecretApprovalDetailsDTO) => {
const getSecretApprovalDetails = async ({ actor, actorId, actorOrgId, id }: TSecretApprovalDetailsDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
const secretApprovalRequest = await secretApprovalRequestDAL.findById(id);
@@ -147,7 +133,6 @@ export const secretApprovalRequestServiceFactory = ({
actor,
actorId,
secretApprovalRequest.projectId,
actorAuthMethod,
actorOrgId
);
if (
@@ -165,14 +150,7 @@ export const secretApprovalRequestServiceFactory = ({
return { ...secretApprovalRequest, secretPath: secretPath?.[0]?.path || "/", commits: secrets };
};
const reviewApproval = async ({
approvalId,
actor,
status,
actorId,
actorAuthMethod,
actorOrgId
}: TReviewRequestDTO) => {
const reviewApproval = async ({ approvalId, actor, status, actorId, actorOrgId }: TReviewRequestDTO) => {
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
@@ -182,7 +160,6 @@ export const secretApprovalRequestServiceFactory = ({
ActorType.USER,
actorId,
secretApprovalRequest.projectId,
actorAuthMethod,
actorOrgId
);
if (
@@ -215,14 +192,7 @@ export const secretApprovalRequestServiceFactory = ({
return reviewStatus;
};
const updateApprovalStatus = async ({
actorId,
status,
approvalId,
actor,
actorOrgId,
actorAuthMethod
}: TStatusChangeDTO) => {
const updateApprovalStatus = async ({ actorId, status, approvalId, actor, actorOrgId }: TStatusChangeDTO) => {
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
@@ -232,7 +202,6 @@ export const secretApprovalRequestServiceFactory = ({
ActorType.USER,
actorId,
secretApprovalRequest.projectId,
actorAuthMethod,
actorOrgId
);
if (
@@ -260,8 +229,7 @@ export const secretApprovalRequestServiceFactory = ({
approvalId,
actor,
actorId,
actorOrgId,
actorAuthMethod
actorOrgId
}: TMergeSecretApprovalRequestDTO) => {
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
@@ -272,10 +240,8 @@ export const secretApprovalRequestServiceFactory = ({
ActorType.USER,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
if (
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerId !== membership.id &&
@@ -472,7 +438,6 @@ export const secretApprovalRequestServiceFactory = ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
policy,
projectId,
secretPath,
@@ -484,7 +449,6 @@ export const secretApprovalRequestServiceFactory = ({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(

View File

@@ -9,7 +9,6 @@ import jmespath from "jmespath";
import knex from "knex";
import { getConfig } from "@app/lib/config/env";
import { getDbConnectionHost } from "@app/lib/knex";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TAssignOp, TDbProviderClients, TDirectAssignOp, THttpProviderFunction } from "../templates/types";
@@ -90,17 +89,7 @@ export const secretRotationDbFn = async ({
const appCfg = getConfig();
const ssl = ca ? { rejectUnauthorized: false, ca } : undefined;
const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
if (
host === "localhost" ||
host === "127.0.0.1" ||
// database infisical uses
dbHost === host ||
// internal ips
host === "host.docker.internal" ||
host.match(/^10\.\d+\.\d+\.\d+/) ||
host.match(/^192\.168\.\d+\.\d+/)
)
if (host === "localhost" || host === "127.0.0.1" || appCfg.DB_CONNECTION_URI.includes(host))
throw new Error("Invalid db host");
const db = knex({

View File

@@ -39,20 +39,8 @@ export const secretRotationServiceFactory = ({
folderDAL,
secretDAL
}: TSecretRotationServiceFactoryDep) => {
const getProviderTemplates = async ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
projectId
}: TProjectPermission) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const getProviderTemplates = async ({ actor, actorId, actorOrgId, projectId }: TProjectPermission) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
return {
@@ -66,7 +54,6 @@ export const secretRotationServiceFactory = ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
inputs,
outputs,
interval,
@@ -74,13 +61,7 @@ export const secretRotationServiceFactory = ({
secretPath,
environment
}: TCreateSecretRotationDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRotation
@@ -158,20 +139,14 @@ export const secretRotationServiceFactory = ({
return secretRotation;
};
const getByProjectId = async ({ actorId, projectId, actor, actorOrgId, actorAuthMethod }: TListByProjectIdDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const getByProjectId = async ({ actorId, projectId, actor, actorOrgId }: TListByProjectIdDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
const doc = await secretRotationDAL.find({ projectId });
return doc;
};
const restartById = async ({ actor, actorId, actorOrgId, actorAuthMethod, rotationId }: TRestartDTO) => {
const restartById = async ({ actor, actorId, actorOrgId, rotationId }: TRestartDTO) => {
const doc = await secretRotationDAL.findById(rotationId);
if (!doc) throw new BadRequestError({ message: "Rotation not found" });
@@ -182,30 +157,18 @@ export const secretRotationServiceFactory = ({
message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation."
});
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
doc.projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation);
await secretRotationQueue.removeFromQueue(doc.id, doc.interval);
await secretRotationQueue.addToQueue(doc.id, doc.interval);
return doc;
};
const deleteById = async ({ actor, actorId, actorOrgId, actorAuthMethod, rotationId }: TDeleteDTO) => {
const deleteById = async ({ actor, actorId, actorOrgId, rotationId }: TDeleteDTO) => {
const doc = await secretRotationDAL.findById(rotationId);
if (!doc) throw new BadRequestError({ message: "Rotation not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
doc.projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
ProjectPermissionSub.SecretRotation

View File

@@ -39,14 +39,8 @@ export const secretScanningServiceFactory = ({
permissionService,
secretScanningQueue
}: TSecretScanningServiceFactoryDep) => {
const createInstallationSession = async ({
actor,
orgId,
actorId,
actorAuthMethod,
actorOrgId
}: TInstallAppSessionDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const createInstallationSession = async ({ actor, orgId, actorId, actorOrgId }: TInstallAppSessionDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
const sessionId = crypto.randomBytes(16).toString("hex");
@@ -59,19 +53,12 @@ export const secretScanningServiceFactory = ({
actorId,
installationId,
actor,
actorAuthMethod,
actorOrgId
}: TLinkInstallSessionDTO) => {
const session = await gitAppInstallSessionDAL.findOne({ sessionId });
if (!session) throw new UnauthorizedError({ message: "Session not found" });
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
session.orgId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
const installatedApp = await gitAppOrgDAL.transaction(async (tx) => {
await gitAppInstallSessionDAL.deleteById(session.id, tx);
@@ -102,37 +89,23 @@ export const secretScanningServiceFactory = ({
return { installatedApp };
};
const getOrgInstallationStatus = async ({
actorId,
orgId,
actor,
actorAuthMethod,
actorOrgId
}: TGetOrgInstallStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getOrgInstallationStatus = async ({ actorId, orgId, actor, actorOrgId }: TGetOrgInstallStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
const appInstallation = await gitAppOrgDAL.findOne({ orgId });
return Boolean(appInstallation);
};
const getRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId }: TGetOrgRisksDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getRisksByOrg = async ({ actor, orgId, actorId, actorOrgId }: TGetOrgRisksDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
return { risks };
};
const updateRiskStatus = async ({
actorId,
orgId,
actor,
actorOrgId,
actorAuthMethod,
riskId,
status
}: TUpdateRiskStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const updateRiskStatus = async ({ actorId, orgId, actor, actorOrgId, riskId, status }: TUpdateRiskStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
const isRiskResolved = Boolean(

View File

@@ -59,16 +59,9 @@ export const secretSnapshotServiceFactory = ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
path
}: TProjectSnapshotCountDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
@@ -84,18 +77,11 @@ export const secretSnapshotServiceFactory = ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
path,
limit = 20,
offset = 0
}: TProjectSnapshotListDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
@@ -105,16 +91,10 @@ export const secretSnapshotServiceFactory = ({
return snapshots;
};
const getSnapshotData = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TGetSnapshotDataDTO) => {
const getSnapshotData = async ({ actorId, actor, actorOrgId, id }: TGetSnapshotDataDTO) => {
const snapshot = await snapshotDAL.findSecretSnapshotDataById(id);
if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
snapshot.projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
return snapshot;
};
@@ -165,23 +145,11 @@ export const secretSnapshotServiceFactory = ({
}
};
const rollbackSnapshot = async ({
id: snapshotId,
actor,
actorId,
actorAuthMethod,
actorOrgId
}: TRollbackSnapshotDTO) => {
const rollbackSnapshot = async ({ id: snapshotId, actor, actorId, actorOrgId }: TRollbackSnapshotDTO) => {
const snapshot = await snapshotDAL.findById(snapshotId);
if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
snapshot.projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRollback

View File

@@ -26,14 +26,8 @@ export const trustedIpServiceFactory = ({
licenseService,
projectDAL
}: TTrustedIpServiceFactoryDep) => {
const listIpsByProjectId = async ({ projectId, actor, actorId, actorAuthMethod, actorOrgId }: TProjectPermission) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const listIpsByProjectId = async ({ projectId, actor, actorId, actorOrgId }: TProjectPermission) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
const trustedIps = await trustedIpDAL.find({
projectId
@@ -44,20 +38,13 @@ export const trustedIpServiceFactory = ({
const addProjectIp = async ({
projectId,
actorId,
actorAuthMethod,
actor,
actorOrgId,
ipAddress: ip,
comment,
isActive
}: TCreateIpDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
const project = await projectDAL.findById(projectId);
@@ -91,18 +78,11 @@ export const trustedIpServiceFactory = ({
actorId,
actor,
actorOrgId,
actorAuthMethod,
ipAddress: ip,
comment,
trustedIpId
}: TUpdateIpDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
const project = await projectDAL.findById(projectId);
@@ -133,21 +113,8 @@ export const trustedIpServiceFactory = ({
return { trustedIp, project }; // for audit log
};
const deleteProjectIp = async ({
projectId,
actorId,
actor,
actorOrgId,
actorAuthMethod,
trustedIpId
}: TDeleteIpDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const deleteProjectIp = async ({ projectId, actorId, actor, actorOrgId, trustedIpId }: TDeleteIpDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
const project = await projectDAL.findById(projectId);

View File

@@ -84,7 +84,7 @@ export const ORGANIZATIONS = {
export const PROJECTS = {
CREATE: {
organizationSlug: "The slug of the organization to create the project in.",
organizationId: "The ID of the organization to create the project in.",
projectName: "The name of the project to create.",
slug: "An optional slug for the project."
},
@@ -194,30 +194,9 @@ export const FOLDERS = {
}
} as const;
export const SECRETS = {
ATTACH_TAGS: {
secretName: "The name of the secret to attach tags to.",
secretPath: "The path of the secret to attach tags to.",
type: "The type of the secret to attach tags to. (shared/personal)",
environment: "The slug of the environment where the secret is located",
projectSlug: "The slug of the project where the secret is located",
tagSlugs: "An array of existing tag slugs to attach to the secret."
},
DETACH_TAGS: {
secretName: "The name of the secret to detach tags from.",
secretPath: "The path of the secret to detach tags from.",
type: "The type of the secret to attach tags to. (shared/personal)",
environment: "The slug of the environment where the secret is located",
projectSlug: "The slug of the project where the secret is located",
tagSlugs: "An array of existing tag slugs to detach from the secret."
}
} as const;
export const RAW_SECRETS = {
LIST: {
recursive: "Whether or not to fetch all secrets from the specified base path, and all of its subdirectories.",
workspaceId: "The ID of the project to list secrets from.",
workspaceSlug: "The slug of the project to list secrets from. This parameter is only usable by machine identities.",
environment: "The slug of the environment to list secrets from.",
secretPath: "The secret path to list secrets from.",
includeImports: "Weather to include imported secrets or not."
@@ -305,251 +284,3 @@ export const AUDIT_LOGS = {
actor: "The actor to filter the audit logs by."
}
} as const;
export const DYNAMIC_SECRETS = {
LIST: {
projectSlug: "The slug of the project to create dynamic secret in.",
environmentSlug: "The slug of the environment to list folders from.",
path: "The path to list folders from."
},
LIST_LEAES_BY_NAME: {
projectSlug: "The slug of the project to create dynamic secret in.",
environmentSlug: "The slug of the environment to list folders from.",
path: "The path to list folders from.",
name: "The name of the dynamic secret."
},
GET_BY_NAME: {
projectSlug: "The slug of the project to create dynamic secret in.",
environmentSlug: "The slug of the environment to list folders from.",
path: "The path to list folders from.",
name: "The name of the dynamic secret."
},
CREATE: {
projectSlug: "The slug of the project to create dynamic secret in.",
environmentSlug: "The slug of the environment to create the dynamic secret in.",
path: "The path to create the dynamic secret in.",
name: "The name of the dynamic secret.",
provider: "The type of dynamic secret.",
defaultTTL: "The default TTL that will be applied for all the leases.",
maxTTL: "The maximum limit a TTL can be leases or renewed."
},
UPDATE: {
projectSlug: "The slug of the project to update dynamic secret in.",
environmentSlug: "The slug of the environment to update the dynamic secret in.",
path: "The path to update the dynamic secret in.",
name: "The name of the dynamic secret.",
inputs: "The new partial values for the configurated provider of the dynamic secret",
defaultTTL: "The default TTL that will be applied for all the leases.",
maxTTL: "The maximum limit a TTL can be leases or renewed.",
newName: "The new name for the dynamic secret."
},
DELETE: {
projectSlug: "The slug of the project to delete dynamic secret in.",
environmentSlug: "The slug of the environment to delete the dynamic secret in.",
path: "The path to delete the dynamic secret in.",
name: "The name of the dynamic secret.",
isForced:
"A boolean flag to delete the the dynamic secret from infisical without trying to remove it from external provider. Used when the dynamic secret got modified externally."
}
} as const;
export const DYNAMIC_SECRET_LEASES = {
GET_BY_LEASEID: {
projectSlug: "The slug of the project to create dynamic secret in.",
environmentSlug: "The slug of the environment to list folders from.",
path: "The path to list folders from.",
leaseId: "The ID of the dynamic secret lease."
},
CREATE: {
projectSlug: "The slug of the project of the dynamic secret in.",
environmentSlug: "The slug of the environment of the dynamic secret in.",
path: "The path of the dynamic secret in.",
dynamicSecretName: "The name of the dynamic secret.",
ttl: "The lease lifetime ttl. If not provided the default TTL of dynamic secret will be used."
},
RENEW: {
projectSlug: "The slug of the project of the dynamic secret in.",
environmentSlug: "The slug of the environment of the dynamic secret in.",
path: "The path of the dynamic secret in.",
leaseId: "The ID of the dynamic secret lease.",
ttl: "The renew TTL that gets added with current expiry (ensure it's below max TTL) for a total less than creation time + max TTL."
},
DELETE: {
projectSlug: "The slug of the project of the dynamic secret in.",
environmentSlug: "The slug of the environment of the dynamic secret in.",
path: "The path of the dynamic secret in.",
leaseId: "The ID of the dynamic secret lease.",
isForced:
"A boolean flag to delete the the dynamic secret from infisical without trying to remove it from external provider. Used when the dynamic secret got modified externally."
}
} as const;
export const SECRET_TAGS = {
LIST: {
projectId: "The ID of the project to list tags from."
},
CREATE: {
projectId: "The ID of the project to create the tag in.",
name: "The name of the tag to create.",
slug: "The slug of the tag to create.",
color: "The color of the tag to create."
},
DELETE: {
tagId: "The ID of the tag to delete.",
projectId: "The ID of the project to delete the tag from."
}
} as const;
export const IDENTITY_ADDITIONAL_PRIVILEGE = {
CREATE: {
projectSlug: "The slug of the project of the identity in.",
identityId: "The ID of the identity to delete.",
slug: "The slug of the privilege to create.",
permissions: `The permission object for the privilege.
1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]]
2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]]
2. [["read", "secrets", {environment: "dev"}]]
`,
isPackPermission: "Whether the server should pack(compact) the permission object.",
isTemporary: "Whether the privilege is temporary.",
temporaryMode: "Type of temporary access given. Types: relative",
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
temporaryAccessStartTime: "ISO time for which temporary access should begin."
},
UPDATE: {
projectSlug: "The slug of the project of the identity in.",
identityId: "The ID of the identity to update.",
slug: "The slug of the privilege to update.",
newSlug: "The new slug of the privilege to update.",
permissions: `The permission object for the privilege.
1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]]
2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]]
2. [["read", "secrets", {environment: "dev"}]]
`,
isPackPermission: "Whether the server should pack(compact) the permission object.",
isTemporary: "Whether the privilege is temporary.",
temporaryMode: "Type of temporary access given. Types: relative",
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
temporaryAccessStartTime: "ISO time for which temporary access should begin."
},
DELETE: {
projectSlug: "The slug of the project of the identity in.",
identityId: "The ID of the identity to delete.",
slug: "The slug of the privilege to delete."
},
GET_BY_SLUG: {
projectSlug: "The slug of the project of the identity in.",
identityId: "The ID of the identity to list.",
slug: "The slug of the privilege."
},
LIST: {
projectSlug: "The slug of the project of the identity in.",
identityId: "The ID of the identity to list.",
unpacked: "Whether the system should send the permissions as unpacked"
}
};
export const PROJECT_USER_ADDITIONAL_PRIVILEGE = {
CREATE: {
projectMembershipId: "Project membership id of user",
slug: "The slug of the privilege to create.",
permissions:
"The permission object for the privilege. Refer https://casl.js.org/v6/en/guide/define-rules#the-shape-of-raw-rule to understand the shape",
isPackPermission: "Whether the server should pack(compact) the permission object.",
isTemporary: "Whether the privilege is temporary.",
temporaryMode: "Type of temporary access given. Types: relative",
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
temporaryAccessStartTime: "ISO time for which temporary access should begin."
},
UPDATE: {
privilegeId: "The id of privilege object",
slug: "The slug of the privilege to create.",
newSlug: "The new slug of the privilege to create.",
permissions:
"The permission object for the privilege. Refer https://casl.js.org/v6/en/guide/define-rules#the-shape-of-raw-rule to understand the shape",
isPackPermission: "Whether the server should pack(compact) the permission object.",
isTemporary: "Whether the privilege is temporary.",
temporaryMode: "Type of temporary access given. Types: relative",
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
temporaryAccessStartTime: "ISO time for which temporary access should begin."
},
DELETE: {
privilegeId: "The id of privilege object"
},
GET_BY_PRIVILEGEID: {
privilegeId: "The id of privilege object"
},
LIST: {
projectMembershipId: "Project membership id of user"
}
};
export const INTEGRATION_AUTH = {
GET: {
integrationAuthId: "The id of integration authentication object."
},
DELETE: {
integration: "The slug of the integration to be unauthorized.",
projectId: "The ID of the project to delete the integration auth from."
},
DELETE_BY_ID: {
integrationAuthId: "The id of integration authentication object to delete."
},
CREATE_ACCESS_TOKEN: {
workspaceId: "The ID of the project to create the integration auth for.",
integration: "The slug of integration for the auth object.",
accessId: "The unique authorized access id of the external integration provider.",
accessToken: "The unique authorized access token of the external integration provider.",
url: "",
namespace: "",
refreshToken: "The refresh token for integration authorization."
},
LIST_AUTHORIZATION: {
workspaceId: "The ID of the project to list integration auths for."
}
};
export const INTEGRATION = {
CREATE: {
integrationAuthId: "The ID of the integration auth object to link with integration.",
app: "The name of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
isActive: "Whether the integration should be active or disabled.",
appId:
"The ID of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
secretPath: "The path of the secrets to sync secrets from.",
sourceEnvironment: "The environment to sync secret from.",
targetEnvironment:
"The target environment of the integration provider. Used in cloudflare pages, TeamCity, Gitlab integrations.",
targetEnvironmentId:
"The target environment id of the integration provider. Used in cloudflare pages, teamcity, gitlab integrations.",
targetService:
"The service based grouping identifier of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank",
targetServiceId:
"The service based grouping identifier ID of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank",
owner: "External integration providers service entity owner. Used in Github.",
path: "Path to save the synced secrets. Used by Gitlab, AWS Parameter Store, Vault",
region: "AWS region to sync secrets to.",
scope: "Scope of the provider. Used by Github, Qovery",
metadata: {
secretPrefix: "The prefix for the saved secret. Used by GCP",
secretSuffix: "The suffix for the saved secret. Used by GCP",
initialSyncBehavoir: "Type of syncing behavoir with the integration",
shouldAutoRedeploy: "Used by Render to trigger auto deploy",
secretGCPLabel: "The label for the GCP secrets"
}
},
UPDATE: {
integrationId: "The ID of the integration object.",
app: "The name of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
appId:
"The ID of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
isActive: "Whether the integration should be active or disabled.",
secretPath: "The path of the secrets to sync secrets from.",
owner: "External integration providers service entity owner. Used in Github.",
targetEnvironment:
"The target environment of the integration provider. Used in cloudflare pages, TeamCity, Gitlab integrations.",
environment: "The environment to sync secrets from."
},
DELETE: {
integrationId: "The ID of the integration object."
}
};

View File

@@ -18,7 +18,6 @@ const envSchema = z
DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")).default(
`postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`
),
MAX_LEASE_LIMIT: z.coerce.number().default(10000),
DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()),
DB_HOST: zpStr(z.string().describe("Postgres database host").optional()),
DB_PORT: zpStr(z.string().describe("Postgres database port").optional()).default("5432"),
@@ -114,8 +113,7 @@ const envSchema = z
.enum(["true", "false"])
.transform((val) => val === "true")
.optional(),
INFISICAL_CLOUD: zodStrBool.default("false"),
MAINTENANCE_MODE: zodStrBool.default("false")
INFISICAL_CLOUD: zodStrBool.default("false")
})
.transform((data) => ({
...data,

View File

@@ -59,18 +59,6 @@ export class BadRequestError extends Error {
}
}
export class DisableRotationErrors extends Error {
name: string;
error: unknown;
constructor({ name, error, message }: { message: string; name?: string; error?: unknown }) {
super(message);
this.name = name || "DisableRotationErrors";
this.error = error;
}
}
export class ScimRequestError extends Error {
name: string;

View File

@@ -1,11 +0,0 @@
import { URL } from "url"; // Import the URL class
export const getDbConnectionHost = (urlString: string) => {
try {
const url = new URL(urlString);
// Split hostname and port (if provided)
return url.hostname.split(":")[0];
} catch (error) {
return null;
}
};

View File

@@ -4,7 +4,6 @@ import { Tables } from "knex/types/tables";
import { DatabaseError } from "../errors";
export * from "./connection";
export * from "./join";
export * from "./select";

View File

@@ -1,19 +1,17 @@
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { ActorType } from "@app/services/auth/auth-type";
export type TOrgPermission = {
actor: ActorType;
actorId: string;
orgId: string;
actorAuthMethod: ActorAuthMethod;
actorOrgId: string | undefined;
actorOrgId?: string;
};
export type TProjectPermission = {
actor: ActorType;
actorId: string;
projectId: string;
actorAuthMethod: ActorAuthMethod;
actorOrgId: string;
actorOrgId?: string;
};
export type RequiredKeys<T> = {

View File

@@ -18,8 +18,7 @@ export enum QueueName {
SecretWebhook = "secret-webhook",
SecretFullRepoScan = "secret-full-repo-scan",
SecretPushEventScan = "secret-push-event-scan",
UpgradeProjectToGhost = "upgrade-project-to-ghost",
DynamicSecretRevocation = "dynamic-secret-revocation"
UpgradeProjectToGhost = "upgrade-project-to-ghost"
}
export enum QueueJobs {
@@ -31,9 +30,7 @@ export enum QueueJobs {
TelemetryInstanceStats = "telemetry-self-hosted-stats",
IntegrationSync = "secret-integration-pull",
SecretScan = "secret-scan",
UpgradeProjectToGhost = "upgrade-project-to-ghost-job",
DynamicSecretRevocation = "dynamic-secret-revocation",
DynamicSecretPruning = "dynamic-secret-pruning"
UpgradeProjectToGhost = "upgrade-project-to-ghost-job"
}
export type TQueueJobTypes = {
@@ -89,19 +86,6 @@ export type TQueueJobTypes = {
name: QueueJobs.TelemetryInstanceStats;
payload: undefined;
};
[QueueName.DynamicSecretRevocation]:
| {
name: QueueJobs.DynamicSecretRevocation;
payload: {
leaseId: string;
};
}
| {
name: QueueJobs.DynamicSecretPruning;
payload: {
dynamicSecretCfgId: string;
};
};
};
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;

View File

@@ -24,7 +24,6 @@ import { fastifyErrHandler } from "./plugins/error-handler";
import { registerExternalNextjs } from "./plugins/external-nextjs";
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "./plugins/fastify-zod";
import { fastifyIp } from "./plugins/ip";
import { maintenanceMode } from "./plugins/maintenanceMode";
import { fastifySwagger } from "./plugins/swagger";
import { registerRoutes } from "./routes";
@@ -73,8 +72,6 @@ export const main = async ({ db, smtp, logger, queue, keyStore }: TMain) => {
}
await server.register(helmet, { contentSecurityPolicy: false });
await server.register(maintenanceMode);
await server.register(registerRoutes, { smtp, queue, db, keyStore });
if (appCfg.isProductionMode) {

View File

@@ -18,43 +18,14 @@ export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
};
};
// GET endpoints
export const readLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 600,
keyGenerator: (req) => req.realIp
};
// POST, PATCH, PUT, DELETE endpoints
export const writeLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 50,
keyGenerator: (req) => req.realIp
};
// special endpoints
export const secretsLimit: RateLimitOptions = {
// secrets, folders, secret imports
timeWindow: 60 * 1000,
max: 600,
keyGenerator: (req) => req.realIp
};
export const authRateLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 60,
max: 600,
keyGenerator: (req) => req.realIp
};
export const inviteUserRateLimit: RateLimitOptions = {
export const passwordRateLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 30,
keyGenerator: (req) => req.realIp
};
export const creationLimit: RateLimitOptions = {
// identity, project, org
timeWindow: 60 * 1000,
max: 30,
max: 600,
keyGenerator: (req) => req.realIp
};

View File

@@ -6,49 +6,42 @@ import { TServiceTokens, TUsers } from "@app/db/schemas";
import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
import { getConfig } from "@app/lib/config/env";
import { UnauthorizedError } from "@app/lib/errors";
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
import { ActorType, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
export type TAuthMode =
| {
orgId?: string;
authMode: AuthMode.JWT;
actor: ActorType.USER;
userId: string;
tokenVersionId: string; // the session id of token used
user: TUsers;
orgId: string;
authMethod: AuthMethod;
}
| {
authMode: AuthMode.API_KEY;
authMethod: null;
actor: ActorType.USER;
userId: string;
user: TUsers;
orgId: string;
orgId?: string;
}
| {
authMode: AuthMode.SERVICE_TOKEN;
serviceToken: TServiceTokens & { createdByEmail: string };
actor: ActorType.SERVICE;
serviceTokenId: string;
orgId: string;
authMethod: null;
}
| {
authMode: AuthMode.IDENTITY_ACCESS_TOKEN;
actor: ActorType.IDENTITY;
identityId: string;
identityName: string;
orgId: string;
authMethod: null;
}
| {
authMode: AuthMode.SCIM_TOKEN;
actor: ActorType.SCIM_CLIENT;
scimTokenId: string;
orgId: string;
authMethod: null;
};
const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
@@ -57,7 +50,6 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
return { authMode: AuthMode.API_KEY, token: apiKey, actor: ActorType.USER } as const;
}
const authHeader = req.headers?.authorization;
if (!authHeader) return { authMode: null, token: null };
const authTokenValue = authHeader.slice(7); // slice of after Bearer
@@ -79,7 +71,6 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
actor: ActorType.USER
} as const;
case AuthTokenType.API_KEY:
// throw new Error("API Key auth is no longer supported.");
return { authMode: AuthMode.API_KEY, token: decodedToken, actor: ActorType.USER } as const;
case AuthTokenType.IDENTITY_ACCESS_TOKEN:
return {
@@ -98,30 +89,17 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
}
};
// ! Important: You can only 100% count on the `req.permission.orgId` field being present when the auth method is Identity Access Token (Machine Identity).
export const injectIdentity = fp(async (server: FastifyZodProvider) => {
server.decorateRequest("auth", null);
server.addHook("onRequest", async (req) => {
const appCfg = getConfig();
const { authMode, token, actor } = await extractAuth(req, appCfg.AUTH_SECRET);
if (req.url.includes("/api/v3/auth/")) {
return;
}
if (!authMode) return;
switch (authMode) {
case AuthMode.JWT: {
const { user, tokenVersionId, orgId } = await server.services.authToken.fnValidateJwtIdentity(token);
req.auth = {
authMode: AuthMode.JWT,
user,
userId: user.id,
tokenVersionId,
actor,
orgId: orgId as string,
authMethod: token.authMethod
};
req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor, orgId };
break;
}
case AuthMode.IDENTITY_ACCESS_TOKEN: {
@@ -129,40 +107,29 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
req.auth = {
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
actor,
orgId: identity.orgId,
identityId: identity.identityId,
identityName: identity.name,
authMethod: null
identityName: identity.name
};
break;
}
case AuthMode.SERVICE_TOKEN: {
const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token);
req.auth = {
orgId: serviceToken.orgId,
authMode: AuthMode.SERVICE_TOKEN as const,
serviceToken,
serviceTokenId: serviceToken.id,
actor,
authMethod: null
actor
};
break;
}
case AuthMode.API_KEY: {
const user = await server.services.apiKey.fnValidateApiKey(token as string);
req.auth = {
authMode: AuthMode.API_KEY as const,
userId: user.id,
actor,
user,
orgId: "API_KEY", // We set the orgId to an arbitrary value, since we can't link an API key to a specific org. We have to deprecate API keys soon!
authMethod: null
};
req.auth = { authMode: AuthMode.API_KEY as const, userId: user.id, actor, user };
break;
}
case AuthMode.SCIM_TOKEN: {
const { orgId, scimTokenId } = await server.services.scim.fnValidateScimToken(token);
req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId, authMethod: null };
req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId };
break;
}
default:

View File

@@ -9,33 +9,13 @@ export const injectPermission = fp(async (server) => {
if (!req.auth) return;
if (req.auth.actor === ActorType.USER) {
req.permission = {
type: ActorType.USER,
id: req.auth.userId,
orgId: req.auth.orgId, // if the req.auth.authMode is AuthMode.API_KEY, the orgId will be "API_KEY"
authMethod: req.auth.authMethod // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null
};
req.permission = { type: ActorType.USER, id: req.auth.userId, orgId: req.auth?.orgId };
} else if (req.auth.actor === ActorType.IDENTITY) {
req.permission = {
type: ActorType.IDENTITY,
id: req.auth.identityId,
orgId: req.auth.orgId,
authMethod: null
};
req.permission = { type: ActorType.IDENTITY, id: req.auth.identityId };
} else if (req.auth.actor === ActorType.SERVICE) {
req.permission = {
type: ActorType.SERVICE,
id: req.auth.serviceTokenId,
orgId: req.auth.orgId,
authMethod: null
};
req.permission = { type: ActorType.SERVICE, id: req.auth.serviceTokenId };
} else if (req.auth.actor === ActorType.SCIM_CLIENT) {
req.permission = {
type: ActorType.SCIM_CLIENT,
id: req.auth.scimTokenId,
orgId: req.auth.orgId,
authMethod: null
};
req.permission = { type: ActorType.SCIM_CLIENT, id: req.auth.scimTokenId, orgId: req.auth.orgId };
}
});
});

View File

@@ -3,26 +3,15 @@ import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
import { UnauthorizedError } from "@app/lib/errors";
import { AuthMode } from "@app/services/auth/auth-type";
interface TAuthOptions {
requireOrg: boolean;
}
export const verifyAuth =
<T extends FastifyRequest>(authStrategies: AuthMode[], options: TAuthOptions = { requireOrg: true }) =>
<T extends FastifyRequest>(authStrats: AuthMode[]) =>
(req: T, _res: FastifyReply, done: HookHandlerDoneFunction) => {
if (!Array.isArray(authStrategies)) throw new Error("Auth strategy must be array");
if (!Array.isArray(authStrats)) throw new Error("Auth strategy must be array");
if (!req.auth) throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" });
const isAccessAllowed = authStrategies.some((strategy) => strategy === req.auth.authMode);
const isAccessAllowed = authStrats.some((strat) => strat === req.auth.authMode);
if (!isAccessAllowed) {
throw new UnauthorizedError({ name: `${req.url} Unauthorized Access` });
}
// New optional option. There are some routes which do not require an organization ID to be present on the request.
// An example of this is the /v1 auth routes.
if (req.auth.authMode === AuthMode.JWT && options.requireOrg === true && !req.permission.orgId) {
throw new UnauthorizedError({ name: `${req.url} Unauthorized Access, no organization found in request` });
}
done();
};

View File

@@ -1,12 +0,0 @@
import fp from "fastify-plugin";
import { getConfig } from "@app/lib/config/env";
export const maintenanceMode = fp(async (fastify) => {
fastify.addHook("onRequest", async (req) => {
const serverEnvs = getConfig();
if (req.url !== "/api/v1/auth/checkAuth" && req.method !== "GET" && serverEnvs.MAINTENANCE_MODE) {
throw new Error("Infisical is in maintenance mode. Please try again later.");
}
});
});

View File

@@ -4,7 +4,6 @@ import SmeeClient from "smee-client";
import { getConfig } from "@app/lib/config/env";
import { logger } from "@app/lib/logger";
import { writeLimit } from "@app/server/config/rateLimiter";
export const registerSecretScannerGhApp = async (server: FastifyZodProvider) => {
const probotApp = (app: Probot) => {
@@ -50,9 +49,6 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) =>
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
handler: async (req, res) => {
const eventName = req.headers["x-github-event"];
const signatureSHA256 = req.headers["x-hub-signature-256"] as string;

View File

@@ -14,13 +14,13 @@ export const fastifySwagger = fp(async (fastify) => {
version: "0.0.1"
},
servers: [
{
url: "https://app.infisical.com",
description: "Production server"
},
{
url: "http://localhost:8080",
description: "Local server"
},
{
url: "https://app.infisical.com",
description: "Production server"
}
],
components: {

View File

@@ -5,22 +5,12 @@ import { registerV1EERoutes } from "@app/ee/routes/v1";
import { auditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal";
import { auditLogQueueServiceFactory } from "@app/ee/services/audit-log/audit-log-queue";
import { auditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
import { dynamicSecretDALFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-dal";
import { dynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
import { buildDynamicSecretProviders } from "@app/ee/services/dynamic-secret/providers";
import { dynamicSecretLeaseDALFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-dal";
import { dynamicSecretLeaseQueueServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue";
import { dynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
import { identityProjectAdditionalPrivilegeDALFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-dal";
import { identityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal";
import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
import { licenseDALFactory } from "@app/ee/services/license/license-dal";
import { licenseServiceFactory } from "@app/ee/services/license/license-service";
import { permissionDALFactory } from "@app/ee/services/permission/permission-dal";
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { projectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { projectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
import { samlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal";
import { samlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
import { scimDALFactory } from "@app/ee/services/scim/scim-dal";
@@ -49,7 +39,6 @@ import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue";
import { readLimit } from "@app/server/config/rateLimiter";
import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
import { authDALFactory } from "@app/services/auth/auth-dal";
@@ -154,7 +143,6 @@ export const registerRoutes = async (
const projectDAL = projectDALFactory(db);
const projectMembershipDAL = projectMembershipDALFactory(db);
const projectUserAdditionalPrivilegeDAL = projectUserAdditionalPrivilegeDALFactory(db);
const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(db);
const projectRoleDAL = projectRoleDALFactory(db);
const projectEnvDAL = projectEnvDALFactory(db);
@@ -180,7 +168,6 @@ export const registerRoutes = async (
const identityOrgMembershipDAL = identityOrgDALFactory(db);
const identityProjectDAL = identityProjectDALFactory(db);
const identityProjectMembershipRoleDAL = identityProjectMembershipRoleDALFactory(db);
const identityProjectAdditionalPrivilegeDAL = identityProjectAdditionalPrivilegeDALFactory(db);
const identityUaDAL = identityUaDALFactory(db);
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
@@ -209,15 +196,12 @@ export const registerRoutes = async (
const gitAppOrgDAL = gitAppDALFactory(db);
const secretScanningDAL = secretScanningDALFactory(db);
const licenseDAL = licenseDALFactory(db);
const dynamicSecretDAL = dynamicSecretDALFactory(db);
const dynamicSecretLeaseDAL = dynamicSecretLeaseDALFactory(db);
const permissionService = permissionServiceFactory({
permissionDAL,
orgRoleDAL,
projectRoleDAL,
serviceTokenDAL,
projectDAL
serviceTokenDAL
});
const licenseService = licenseServiceFactory({ permissionService, orgDAL, licenseDAL, keyStore });
const trustedIpService = trustedIpServiceFactory({
@@ -282,7 +266,7 @@ export const registerRoutes = async (
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL });
const userService = userServiceFactory({ userDAL });
const loginService = authLoginServiceFactory({ userDAL, smtpService, tokenService, orgDAL, tokenDAL: authTokenDAL });
const loginService = authLoginServiceFactory({ userDAL, smtpService, tokenService });
const passwordService = authPaswordServiceFactory({
tokenService,
smtpService,
@@ -352,11 +336,6 @@ export const registerRoutes = async (
projectRoleDAL,
licenseService
});
const projectUserAdditionalPrivilegeService = projectUserAdditionalPrivilegeServiceFactory({
permissionService,
projectMembershipDAL,
projectUserAdditionalPrivilegeDAL
});
const projectKeyService = projectKeyServiceFactory({
permissionService,
projectKeyDAL,
@@ -393,14 +372,12 @@ export const registerRoutes = async (
projectKeyDAL,
userDAL,
projectEnvDAL,
orgDAL,
orgService,
projectMembershipDAL,
folderDAL,
licenseService,
projectUserMembershipRoleDAL,
identityProjectMembershipRoleDAL,
keyStore
identityProjectMembershipRoleDAL
});
const projectEnvService = projectEnvServiceFactory({
@@ -411,12 +388,7 @@ export const registerRoutes = async (
folderDAL
});
const projectRoleService = projectRoleServiceFactory({
permissionService,
projectRoleDAL,
projectUserMembershipRoleDAL,
identityProjectMembershipRoleDAL
});
const projectRoleService = projectRoleServiceFactory({ permissionService, projectRoleDAL });
const snapshotService = secretSnapshotServiceFactory({
permissionService,
@@ -497,7 +469,6 @@ export const registerRoutes = async (
snapshotService,
secretQueueService,
secretImportDAL,
projectEnvDAL,
projectBotService
});
const sarService = secretApprovalRequestServiceFactory({
@@ -546,8 +517,7 @@ export const registerRoutes = async (
projectEnvDAL,
serviceTokenDAL,
userDAL,
permissionService,
projectDAL
permissionService
});
const identityService = identityServiceFactory({
@@ -555,10 +525,7 @@ export const registerRoutes = async (
identityDAL,
identityOrgMembershipDAL
});
const identityAccessTokenService = identityAccessTokenServiceFactory({
identityAccessTokenDAL,
identityOrgMembershipDAL
});
const identityAccessTokenService = identityAccessTokenServiceFactory({ identityAccessTokenDAL });
const identityProjectService = identityProjectServiceFactory({
permissionService,
projectDAL,
@@ -567,12 +534,6 @@ export const registerRoutes = async (
identityProjectMembershipRoleDAL,
projectRoleDAL
});
const identityProjectAdditionalPrivilegeService = identityProjectAdditionalPrivilegeServiceFactory({
projectDAL,
identityProjectAdditionalPrivilegeDAL,
permissionService,
identityProjectDAL
});
const identityUaService = identityUaServiceFactory({
identityOrgMembershipDAL,
permissionService,
@@ -583,34 +544,6 @@ export const registerRoutes = async (
licenseService
});
const dynamicSecretProviders = buildDynamicSecretProviders();
const dynamicSecretQueueService = dynamicSecretLeaseQueueServiceFactory({
queueService,
dynamicSecretLeaseDAL,
dynamicSecretProviders,
dynamicSecretDAL
});
const dynamicSecretService = dynamicSecretServiceFactory({
projectDAL,
dynamicSecretQueueService,
dynamicSecretDAL,
dynamicSecretLeaseDAL,
dynamicSecretProviders,
folderDAL,
permissionService,
licenseService
});
const dynamicSecretLeaseService = dynamicSecretLeaseServiceFactory({
projectDAL,
permissionService,
dynamicSecretQueueService,
dynamicSecretDAL,
dynamicSecretLeaseDAL,
dynamicSecretProviders,
folderDAL,
licenseService
});
await superAdminService.initServerCfg();
//
// setup the communication with license key server
@@ -652,8 +585,6 @@ export const registerRoutes = async (
secretApprovalPolicy: sapService,
secretApprovalRequest: sarService,
secretRotation: secretRotationService,
dynamicSecret: dynamicSecretService,
dynamicSecretLease: dynamicSecretLeaseService,
snapshot: snapshotService,
saml: samlService,
ldap: ldapService,
@@ -663,9 +594,7 @@ export const registerRoutes = async (
trustedIp: trustedIpService,
scim: scimService,
secretBlindIndex: secretBlindIndexService,
telemetry: telemetryService,
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService,
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService
telemetry: telemetryService
});
server.decorate<FastifyZodProvider["store"]>("store", {
@@ -677,11 +606,8 @@ export const registerRoutes = async (
await server.register(injectAuditLogInfo);
server.route({
method: "GET",
url: "/api/status",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
response: {
200: z.object({

View File

@@ -1,11 +1,6 @@
import { z } from "zod";
import {
DynamicSecretsSchema,
IntegrationAuthsSchema,
SecretApprovalPoliciesSchema,
UsersSchema
} from "@app/db/schemas";
import { IntegrationAuthsSchema, SecretApprovalPoliciesSchema, UsersSchema } from "@app/db/schemas";
// sometimes the return data must be santizied to avoid leaking important values
// always prefer pick over omit in zod
@@ -61,11 +56,3 @@ export const secretRawSchema = z.object({
secretValue: z.string(),
secretComment: z.string().optional()
});
export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({
inputIV: true,
inputTag: true,
inputCiphertext: true,
keyEncoding: true,
algorithm: true
});

View File

@@ -3,7 +3,6 @@ import { z } from "zod";
import { OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { UnauthorizedError } from "@app/lib/errors";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -12,33 +11,24 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
export const registerAdminRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/config",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
response: {
200: z.object({
config: SuperAdminSchema.omit({ createdAt: true, updatedAt: true }).merge(
z.object({ isMigrationModeOn: z.boolean() })
)
config: SuperAdminSchema.omit({ createdAt: true, updatedAt: true })
})
}
},
handler: async () => {
const config = await getServerCfg();
const serverEnvs = getConfig();
return { config: { ...config, isMigrationModeOn: serverEnvs.MAINTENANCE_MODE } };
return { config };
}
});
server.route({
method: "PATCH",
url: "/config",
config: {
rateLimit: writeLimit
},
method: "PATCH",
schema: {
body: z.object({
allowSignUp: z.boolean().optional(),
@@ -62,11 +52,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/signup",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
body: z.object({
email: z.string().email().trim(),

View File

@@ -3,7 +3,7 @@ import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { authRateLimit, writeLimit } from "@app/server/config/rateLimiter";
import { authRateLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode, AuthModeRefreshJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
@@ -21,7 +21,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT], { requireOrg: false }),
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req, res) => {
const appCfg = getConfig();
if (req.auth.authMode === AuthMode.JWT) {
@@ -38,11 +38,8 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/checkAuth",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
response: {
200: z.object({
@@ -55,11 +52,8 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/token",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
response: {
200: z.object({
@@ -91,7 +85,6 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
const token = jwt.sign(
{
authMethod: decodedToken.authMethod,
authTokenType: AuthTokenType.ACCESS_TOKEN,
userId: decodedToken.userId,
tokenVersionId: tokenVersion.id,

View File

@@ -1,17 +1,13 @@
import { z } from "zod";
import { ProjectBotsSchema } from "@app/db/schemas";
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";
export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:projectId",
config: {
rateLimit: readLimit
},
method: "GET",
schema: {
params: z.object({
projectId: z.string().trim()
@@ -34,7 +30,6 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
projectId: req.params.projectId
});
return { bot };
@@ -42,11 +37,8 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "PATCH",
url: "/:botId/active",
config: {
rateLimit: writeLimit
},
method: "PATCH",
schema: {
body: z.object({
isActive: z.boolean(),
@@ -78,7 +70,6 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
botId: req.params.botId,
botKey: req.body.botKey,
isActive: req.body.isActive

View File

@@ -1,15 +1,11 @@
import { z } from "zod";
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
import { writeLimit } from "@app/server/config/rateLimiter";
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/token/renew",
method: "POST",
config: {
rateLimit: writeLimit
},
schema: {
description: "Renew access token",
body: z.object({

View File

@@ -3,7 +3,6 @@ import { z } from "zod";
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { IDENTITIES } from "@app/lib/api-docs";
import { creationLimit, writeLimit } from "@app/server/config/rateLimiter";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -13,9 +12,6 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: creationLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Create identity",
@@ -39,7 +35,6 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
const identity = await server.services.identity.createIdentity({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
orgId: req.body.organizationId
@@ -75,9 +70,6 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({
method: "PATCH",
url: "/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Update identity",
@@ -103,7 +95,6 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
const identity = await server.services.identity.updateIdentity({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.identityId,
...req.body
@@ -128,9 +119,6 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({
method: "DELETE",
url: "/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Delete identity",
@@ -152,7 +140,6 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
const identity = await server.services.identity.deleteIdentity({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.identityId
});

View File

@@ -3,7 +3,6 @@ import { z } from "zod";
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
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";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
@@ -23,11 +22,8 @@ export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({
export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/universal-auth/login",
config: {
rateLimit: writeLimit
},
method: "POST",
schema: {
description: "Login with Universal Auth",
body: z.object({
@@ -70,11 +66,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/universal-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Attach Universal Auth configuration onto identity",
@@ -138,7 +131,6 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
identityId: req.params.identityId
});
@@ -163,11 +155,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "PATCH",
url: "/universal-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
method: "PATCH",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Update Universal Auth configuration on identity",
@@ -223,7 +212,6 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
identityId: req.params.identityId
});
@@ -249,11 +237,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/universal-auth/identities/:identityId",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Retrieve Universal Auth configuration on identity",
@@ -275,7 +260,6 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const identityUniversalAuth = await server.services.identityUa.getIdentityUa({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId
});
@@ -296,11 +280,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/universal-auth/identities/:identityId/client-secrets",
config: {
rateLimit: writeLimit
},
method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Create Universal Auth Client Secret for identity",
@@ -328,7 +309,6 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const { clientSecret, clientSecretData, orgId } = await server.services.identityUa.createUaClientSecret({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId,
...req.body
@@ -351,11 +331,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "GET",
url: "/universal-auth/identities/:identityId/client-secrets",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "List Universal Auth Client Secrets for identity",
@@ -377,7 +354,6 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUaClientSecrets({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId
});
@@ -397,11 +373,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
});
server.route({
method: "POST",
url: "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
config: {
rateLimit: writeLimit
},
method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Revoke Universal Auth Client Secrets for identity",
@@ -424,7 +397,6 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const clientSecretData = await server.services.identityUa.revokeUaClientSecret({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId,
clientSecretId: req.params.clientSecretId

View File

@@ -1,8 +1,6 @@
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { INTEGRATION_AUTH } from "@app/lib/api-docs";
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";
@@ -10,19 +8,10 @@ import { integrationAuthPubSchema } from "../sanitizedSchemas";
export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/integration-options",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
description: "List of integrations available.",
security: [
{
bearerAuth: []
}
],
response: {
200: z.object({
integrationOptions: z
@@ -47,21 +36,12 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
description: "Get details of an integration authorization by auth object id.",
security: [
{
bearerAuth: []
}
],
params: z.object({
integrationAuthId: z.string().trim().describe(INTEGRATION_AUTH.GET.integrationAuthId)
integrationAuthId: z.string().trim()
}),
response: {
200: z.object({
@@ -73,7 +53,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.getIntegrationAuth({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId
});
@@ -82,22 +61,13 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "DELETE",
url: "/",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
method: "DELETE",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
description: "Remove all integration's auth object from the project.",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
integration: z.string().trim().describe(INTEGRATION_AUTH.DELETE.integration),
projectId: z.string().trim().describe(INTEGRATION_AUTH.DELETE.projectId)
integration: z.string().trim(),
projectId: z.string().trim()
}),
response: {
200: z.object({
@@ -110,7 +80,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
integration: req.query.integration,
projectId: req.query.projectId
});
@@ -131,21 +100,12 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "DELETE",
url: "/:integrationAuthId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
method: "DELETE",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
description: "Remove an integration auth object by object id.",
security: [
{
bearerAuth: []
}
],
params: z.object({
integrationAuthId: z.string().trim().describe(INTEGRATION_AUTH.DELETE_BY_ID.integrationAuthId)
integrationAuthId: z.string().trim()
}),
response: {
200: z.object({
@@ -157,7 +117,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuthById({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId
});
@@ -178,11 +137,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "POST",
url: "/oauth-token",
config: {
rateLimit: writeLimit
},
method: "POST",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z.object({
@@ -201,7 +157,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.oauthExchange({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId,
...req.body
@@ -222,27 +177,18 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "POST",
url: "/access-token",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
method: "POST",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
description: "Create the integration authentication object required for syncing secrets.",
security: [
{
bearerAuth: []
}
],
body: z.object({
workspaceId: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.workspaceId),
integration: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.integration),
accessId: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessId),
accessToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessToken),
url: z.string().url().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.url),
namespace: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.namespace),
refreshToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.refreshToken)
workspaceId: z.string().trim(),
integration: z.string().trim(),
accessId: z.string().trim().optional(),
accessToken: z.string().trim().optional(),
url: z.string().url().trim().optional(),
namespace: z.string().trim().optional(),
refreshToken: z.string().trim().optional()
}),
response: {
200: z.object({
@@ -254,7 +200,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId,
...req.body
@@ -275,11 +220,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/apps",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -305,7 +247,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const apps = await server.services.integrationAuth.getIntegrationApps({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
...req.query
@@ -315,11 +256,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/teams",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -340,7 +278,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const teams = await server.services.integrationAuth.getIntegrationAuthTeams({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId
});
@@ -349,11 +286,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/vercel/branches",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -372,7 +306,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const branches = await server.services.integrationAuth.getVercelBranches({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
appId: req.query.appId
@@ -382,11 +315,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/checkly/groups",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -405,7 +335,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const groups = await server.services.integrationAuth.getChecklyGroups({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
accountId: req.query.accountId
@@ -415,79 +344,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/github/orgs",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
integrationAuthId: z.string().trim()
}),
response: {
200: z.object({
orgs: z.object({ name: z.string(), orgId: z.string() }).array()
})
}
},
handler: async (req) => {
const orgs = await server.services.integrationAuth.getGithubOrgs({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
id: req.params.integrationAuthId
});
if (!orgs) throw new Error("No organization found.");
return { orgs };
}
});
server.route({
method: "GET",
url: "/:integrationAuthId/github/envs",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
integrationAuthId: z.string().trim()
}),
querystring: z.object({
repoOwner: z.string().trim(),
repoName: z.string().trim()
}),
response: {
200: z.object({
envs: z.object({ name: z.string(), envId: z.string() }).array()
})
}
},
handler: async (req) => {
const envs = await server.services.integrationAuth.getGithubEnvs({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
actorAuthMethod: req.permission.authMethod,
repoName: req.query.repoName,
repoOwner: req.query.repoOwner
});
if (!envs) throw new Error("No organization found.");
return { envs };
}
});
server.route({
method: "GET",
url: "/:integrationAuthId/qovery/orgs",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -503,7 +361,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const orgs = await server.services.integrationAuth.getQoveryOrgs({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId
});
@@ -512,11 +369,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/qovery/projects",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -535,7 +389,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const projects = await server.services.integrationAuth.getQoveryProjects({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
orgId: req.query.orgId
@@ -545,11 +398,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/qovery/environments",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -568,7 +418,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const environments = await server.services.integrationAuth.getQoveryEnvs({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
projectId: req.query.projectId
@@ -578,11 +427,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/qovery/apps",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -601,7 +447,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const apps = await server.services.integrationAuth.getQoveryApps({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
environmentId: req.query.environmentId
@@ -611,11 +456,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/qovery/containers",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -634,7 +476,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const containers = await server.services.integrationAuth.getQoveryContainers({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
environmentId: req.query.environmentId
@@ -644,11 +485,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/qovery/jobs",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -667,7 +505,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const jobs = await server.services.integrationAuth.getQoveryJobs({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
environmentId: req.query.environmentId
@@ -677,11 +514,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/heroku/pipelines",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -703,7 +537,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const pipelines = await server.services.integrationAuth.getHerokuPipelines({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId
});
@@ -712,11 +545,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/railway/environments",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -735,7 +565,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const environments = await server.services.integrationAuth.getRailwayEnvironments({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
appId: req.query.appId
@@ -745,11 +574,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/railway/services",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -768,7 +594,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const services = await server.services.integrationAuth.getRailwayServices({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
appId: req.query.appId
@@ -778,11 +603,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/bitbucket/workspaces",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -808,7 +630,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const workspaces = await server.services.integrationAuth.getBitbucketWorkspaces({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId
});
@@ -817,11 +638,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/northflank/secret-groups",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -845,7 +663,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const secretGroups = await server.services.integrationAuth.getNorthFlankSecretGroups({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
appId: req.query.appId
@@ -855,11 +672,8 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
});
server.route({
method: "GET",
url: "/:integrationAuthId/teamcity/build-configs",
config: {
rateLimit: readLimit
},
method: "GET",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
params: z.object({
@@ -883,7 +697,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const buildConfigs = await server.services.integrationAuth.getTeamcityBuildConfigs({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId,
appId: req.query.appId

Some files were not shown because too many files have changed in this diff Show More