mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-11 12:11:38 +00:00
Compare commits
228 Commits
misc/add-p
...
daniel/inf
Author | SHA1 | Date | |
---|---|---|---|
8deff5adfb | |||
1f8b3b6779 | |||
a87bc66b05 | |||
de57e1af35 | |||
09d8822816 | |||
13aaef4212 | |||
5e9193adda | |||
ec3e886624 | |||
36d30566fe | |||
dfbeac3dfe | |||
87e52ddd06 | |||
a62fbf088f | |||
f186cb4d7b | |||
2ee123c9f6 | |||
18b6c4f73e | |||
50409f0c48 | |||
54e5166bb6 | |||
b9b880d310 | |||
085d1d5a5e | |||
b02c37028b | |||
49248ee13f | |||
bafc6ee129 | |||
eb6dca425c | |||
99c1259f15 | |||
b4770116a8 | |||
eb90f503a9 | |||
e419983249 | |||
b030fe2e69 | |||
eff0604e9d | |||
e90f3af4ce | |||
baf2763287 | |||
d708a3f566 | |||
5b52c33f5f | |||
a116fc2bf3 | |||
39d09eea3d | |||
f7d071e398 | |||
0d4dd5a6fa | |||
b4de012047 | |||
b3720cdbfc | |||
0dc85dff33 | |||
a6e4e3c69a | |||
be9de82ef5 | |||
2566f4dc9e | |||
934bfbb624 | |||
509037e6d0 | |||
f041aa7557 | |||
266e2856e8 | |||
7109d2f785 | |||
2134d2e118 | |||
c2abc383d5 | |||
3a2336da44 | |||
1266949fb1 | |||
62d287f8a6 | |||
0b4e7f0096 | |||
7dda2937ba | |||
91d81bd20c | |||
f329a79771 | |||
31a31f556c | |||
1be2f806d9 | |||
38a6785ca4 | |||
377eb4cfd3 | |||
8df7401e06 | |||
0c79303582 | |||
e6edde57ba | |||
6634675b2a | |||
50840ce26b | |||
4c2f7fff5c | |||
f0a3792a64 | |||
70da6878c1 | |||
754404d905 | |||
85cfac512c | |||
d40b907308 | |||
a5b18cbb72 | |||
7add57ae78 | |||
e5879df7c7 | |||
04298bb1a7 | |||
1a6a5280a0 | |||
da0d8fdbfc | |||
d2759ea378 | |||
c4385af352 | |||
bbe2d2e053 | |||
2c9fdb7fad | |||
38eee5490e | |||
0aa7337ff4 | |||
98371f99e7 | |||
ddfc645cdd | |||
f4d9c61404 | |||
5342c85696 | |||
b05f3e0f1f | |||
9a2645b511 | |||
cb664bb042 | |||
07db1d826b | |||
74db1b75b4 | |||
d7023881e5 | |||
ef3cdd11ac | |||
612cf4f968 | |||
b6a9dc7f53 | |||
b74595cf35 | |||
a45453629c | |||
f7626d03bf | |||
bc14153bb3 | |||
8915b4055b | |||
935a3cb036 | |||
148a29db19 | |||
b12de3e4f5 | |||
661e5ec462 | |||
5cca51d711 | |||
9e9b9a7b94 | |||
df1ffcf934 | |||
0ef7eacd0e | |||
776822d7d5 | |||
fe9af20d8c | |||
398a8f363d | |||
ce5dbca6e2 | |||
ed5a7d72ab | |||
3ac6b7be65 | |||
10601b5afd | |||
8eec08356b | |||
0b4d4c008a | |||
ae953add3d | |||
5960a899ba | |||
ea98a0096d | |||
b8f65fc91a | |||
06a4e68ac1 | |||
9cbf9a675a | |||
178ddf1fb9 | |||
030d4fe152 | |||
46abda9041 | |||
c976a5ccba | |||
1eb9ea9c74 | |||
7d7612aaf4 | |||
f570b3b2ee | |||
0b8f6878fe | |||
758a9211ab | |||
0bb2b2887b | |||
eeb0111bbe | |||
d12c538511 | |||
6f67346b2a | |||
a93db44bbd | |||
1ddacfda62 | |||
5a1e43be44 | |||
04f54479cd | |||
351d0d0662 | |||
5a01edae7a | |||
506e86d666 | |||
11d9166684 | |||
1859557f90 | |||
59fc34412d | |||
1b2a1f2339 | |||
15b4c397ab | |||
fc27ad4575 | |||
b7467a83ab | |||
3baf434230 | |||
e28471a9f4 | |||
b2d6563994 | |||
cfba8f53e3 | |||
3537a5eb9b | |||
d5b17a8f24 | |||
d6881e2e68 | |||
92a663a17d | |||
b3463e0d0f | |||
c460f22665 | |||
7cdc47cd3a | |||
d666d60f9f | |||
491c4259ca | |||
cff20eb621 | |||
db39d03713 | |||
84d8879177 | |||
aa4f2adbb6 | |||
86ed3ef6d6 | |||
a5bb80adc4 | |||
0e87dd3996 | |||
e1801e9eb4 | |||
9daa5badec | |||
e1ed37c713 | |||
8eea82a1a0 | |||
694d0e3ed3 | |||
58f6c6b409 | |||
f4a33caba6 | |||
e0a6f09b5e | |||
98a15a901e | |||
1e701687ae | |||
15758b91f8 | |||
2d3a4a7559 | |||
a1d01d5cbd | |||
2e3aedc62b | |||
e0a5b1444a | |||
1c2698f533 | |||
b50833bded | |||
e0c774c045 | |||
514df55d67 | |||
311b378f3b | |||
b01b4323ca | |||
285a01af51 | |||
f7e658e62b | |||
a8aef2934a | |||
cc30476f79 | |||
5d59fe8810 | |||
90eed8d39b | |||
f5974ce9ad | |||
c6b51af4b1 | |||
c13c37fc77 | |||
259c01c110 | |||
a016d0d33f | |||
663be06d30 | |||
fa392382da | |||
d34b2669c5 | |||
11ea5990c9 | |||
d4f9faf24d | |||
a15a0a257c | |||
6029eaa9df | |||
8703314c0c | |||
084fc7c99e | |||
b6cc17d62a | |||
4072a40fe9 | |||
0dc132dda3 | |||
605ccb13e9 | |||
2160c66e20 | |||
1c5c7c75c4 | |||
24c75c6325 | |||
0a22a2a9ef | |||
d0f1cad98c | |||
afee158b95 | |||
f9a9599659 | |||
637b0b955f | |||
092665737f | |||
f83c2215a5 | |||
0f41590d6a |
@ -67,3 +67,6 @@ CLIENT_SECRET_GITLAB_LOGIN=
|
|||||||
CAPTCHA_SECRET=
|
CAPTCHA_SECRET=
|
||||||
|
|
||||||
NEXT_PUBLIC_CAPTCHA_SITE_KEY=
|
NEXT_PUBLIC_CAPTCHA_SITE_KEY=
|
||||||
|
|
||||||
|
PLAIN_API_KEY=
|
||||||
|
PLAIN_WISH_LABEL_IDS=
|
||||||
|
99
.github/workflows/build-binaries.yml
vendored
Normal file
99
.github/workflows/build-binaries.yml
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
name: Build Binaries and Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version number"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./backend
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [x64, arm64]
|
||||||
|
os: [linux, win]
|
||||||
|
include:
|
||||||
|
- os: linux
|
||||||
|
target: node20-linux
|
||||||
|
- os: win
|
||||||
|
target: node20-win
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install pkg
|
||||||
|
run: npm install -g @yao-pkg/pkg
|
||||||
|
|
||||||
|
- name: Install dependencies (backend)
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Install dependencies (frontend)
|
||||||
|
run: npm install --prefix ../frontend
|
||||||
|
|
||||||
|
- name: Prerequisites for pkg
|
||||||
|
run: npm run binary:build
|
||||||
|
|
||||||
|
- name: Package into node binary
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.os }}" != "linux" ]; then
|
||||||
|
pkg --no-bytecode --public-packages "*" --public --compress Brotli --target ${{ matrix.target }}-${{ matrix.arch }} --output ./binary/infisical-standalone-${{ matrix.os }}-${{ matrix.arch }} .
|
||||||
|
else
|
||||||
|
pkg --no-bytecode --public-packages "*" --public --compress Brotli --target ${{ matrix.target }}-${{ matrix.arch }} --output ./binary/infisical-standalone .
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set up .deb package structure (Debian/Ubuntu only)
|
||||||
|
- name: Set up .deb package structure
|
||||||
|
if: matrix.os == 'linux'
|
||||||
|
run: |
|
||||||
|
mkdir -p infisical-standalone/DEBIAN
|
||||||
|
mkdir -p infisical-standalone/usr/local/bin
|
||||||
|
cp ./binary/infisical-standalone infisical-standalone/usr/local/bin/
|
||||||
|
chmod +x infisical-standalone/usr/local/bin/infisical-standalone
|
||||||
|
|
||||||
|
- name: Create control file
|
||||||
|
if: matrix.os == 'linux'
|
||||||
|
run: |
|
||||||
|
cat <<EOF > infisical-standalone/DEBIAN/control
|
||||||
|
Package: infisical-standalone
|
||||||
|
Version: ${{ github.event.inputs.version }}
|
||||||
|
Section: base
|
||||||
|
Priority: optional
|
||||||
|
Architecture: ${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }}
|
||||||
|
Maintainer: Infisical <daniel@infisical.com>
|
||||||
|
Description: Infisical standalone executable (app.infisical.com)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Build .deb file (Debian/Ubunutu only)
|
||||||
|
- name: Build .deb package
|
||||||
|
if: matrix.os == 'linux'
|
||||||
|
run: |
|
||||||
|
dpkg-deb --build infisical-standalone
|
||||||
|
mv infisical-standalone.deb ./binary/infisical-standalone-${{matrix.arch}}.deb
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
- run: pip install --upgrade cloudsmith-cli
|
||||||
|
|
||||||
|
# Publish .deb file to Cloudsmith (Debian/Ubuntu only)
|
||||||
|
- name: Publish to Cloudsmith (Debian/Ubuntu)
|
||||||
|
if: matrix.os == 'linux'
|
||||||
|
working-directory: ./backend
|
||||||
|
run: cloudsmith push deb --republish --no-wait-for-sync --api-key=${{ secrets.CLOUDSMITH_API_KEY }} infisical/infisical-standalone/any-distro/any-version ./binary/infisical-standalone-${{ matrix.arch }}.deb
|
||||||
|
|
||||||
|
# Publish .exe file to Cloudsmith (Windows only)
|
||||||
|
- name: Publish to Cloudsmith (Windows)
|
||||||
|
if: matrix.os == 'win'
|
||||||
|
working-directory: ./backend
|
||||||
|
run: cloudsmith push raw infisical/infisical-standalone ./binary/infisical-standalone-${{ matrix.os }}-${{ matrix.arch }}.exe --republish --no-wait-for-sync --version ${{ github.event.inputs.version }} --api-key ${{ secrets.CLOUDSMITH_API_KEY }}
|
@ -56,7 +56,7 @@ jobs:
|
|||||||
# Learn more about [Twingate Services](https://docs.twingate.com/docs/services)
|
# Learn more about [Twingate Services](https://docs.twingate.com/docs/services)
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
service-key: ${{ secrets.TWINGATE_GAMMA_SERVICE_KEY }}
|
service-key: ${{ secrets.TWINGATE_SERVICE_KEY }}
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
@ -105,6 +105,13 @@ jobs:
|
|||||||
environment:
|
environment:
|
||||||
name: Production
|
name: Production
|
||||||
steps:
|
steps:
|
||||||
|
- uses: twingate/github-action@v1
|
||||||
|
with:
|
||||||
|
# The Twingate Service Key used to connect Twingate to the proper service
|
||||||
|
# Learn more about [Twingate Services](https://docs.twingate.com/docs/services)
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
service-key: ${{ secrets.TWINGATE_SERVICE_KEY }}
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
|
25
.github/workflows/check-migration-file-edited.yml
vendored
Normal file
25
.github/workflows/check-migration-file-edited.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: Check migration file edited
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- 'backend/src/db/migrations/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rename:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Check any migration files are modified, renamed or duplicated.
|
||||||
|
run: |
|
||||||
|
git diff --name-status HEAD^ HEAD backend/src/db/migrations | grep '^M\|^R\|^C' || true | cut -f2 | xargs -r -n1 basename > edited_files.txt
|
||||||
|
if [ -s edited_files.txt ]; then
|
||||||
|
echo "Exiting migration files cannot be modified."
|
||||||
|
cat edited_files.txt
|
||||||
|
exit 1
|
||||||
|
fi
|
@ -1,59 +0,0 @@
|
|||||||
name: Rename Migrations
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [closed]
|
|
||||||
paths:
|
|
||||||
- 'backend/src/db/migrations/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
rename:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event.pull_request.merged == true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Get list of newly added files in migration folder
|
|
||||||
run: |
|
|
||||||
git diff --name-status HEAD^ HEAD backend/src/db/migrations | grep '^A' | cut -f2 | xargs -n1 basename > added_files.txt
|
|
||||||
if [ ! -s added_files.txt ]; then
|
|
||||||
echo "No new files added. Skipping"
|
|
||||||
echo "SKIP_RENAME=true" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Script to rename migrations
|
|
||||||
if: env.SKIP_RENAME != 'true'
|
|
||||||
run: python .github/resources/rename_migration_files.py
|
|
||||||
|
|
||||||
- name: Commit and push changes
|
|
||||||
if: env.SKIP_RENAME != 'true'
|
|
||||||
run: |
|
|
||||||
git config user.name github-actions
|
|
||||||
git config user.email github-actions@github.com
|
|
||||||
git add ./backend/src/db/migrations
|
|
||||||
rm added_files.txt
|
|
||||||
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
|
|
||||||
|
|
||||||
- name: Get PR details
|
|
||||||
id: pr_details
|
|
||||||
run: |
|
|
||||||
PR_NUMBER=${{ github.event.pull_request.number }}
|
|
||||||
PR_MERGER=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | jq -r '.merged_by.login')
|
|
||||||
|
|
||||||
echo "PR Number: $PR_NUMBER"
|
|
||||||
echo "PR Merger: $PR_MERGER"
|
|
||||||
echo "pr_merger=$PR_MERGER" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
|
||||||
if: env.SKIP_RENAME != 'true'
|
|
||||||
uses: peter-evans/create-pull-request@v6
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
|
|
||||||
title: 'GH Action: rename new migration file timestamp'
|
|
||||||
branch-suffix: timestamp
|
|
||||||
reviewers: ${{ steps.pr_details.outputs.pr_merger }}
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -69,3 +69,4 @@ frontend-build
|
|||||||
*.tgz
|
*.tgz
|
||||||
cli/infisical-merge
|
cli/infisical-merge
|
||||||
cli/test/infisical-merge
|
cli/test/infisical-merge
|
||||||
|
/backend/binary
|
||||||
|
@ -5,3 +5,4 @@ frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/M
|
|||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
|
||||||
docs/self-hosting/configuration/envars.mdx:generic-api-key:106
|
docs/self-hosting/configuration/envars.mdx:generic-api-key:106
|
||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
||||||
|
docs/mint.json:generic-api-key:651
|
||||||
|
4
backend/babel.config.json
Normal file
4
backend/babel.config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||||
|
"plugins": ["@babel/plugin-syntax-import-attributes", "babel-plugin-transform-import-meta"]
|
||||||
|
}
|
@ -3,7 +3,6 @@ import "ts-node/register";
|
|||||||
|
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import knex from "knex";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { seedData1 } from "@app/db/seed-data";
|
import { seedData1 } from "@app/db/seed-data";
|
||||||
@ -15,6 +14,7 @@ import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
|||||||
import { mockQueue } from "./mocks/queue";
|
import { mockQueue } from "./mocks/queue";
|
||||||
import { mockSmtpServer } from "./mocks/smtp";
|
import { mockSmtpServer } from "./mocks/smtp";
|
||||||
import { mockKeyStore } from "./mocks/keystore";
|
import { mockKeyStore } from "./mocks/keystore";
|
||||||
|
import { initDbConnection } from "@app/db";
|
||||||
|
|
||||||
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
||||||
export default {
|
export default {
|
||||||
@ -23,23 +23,21 @@ export default {
|
|||||||
async setup() {
|
async setup() {
|
||||||
const logger = await initLogger();
|
const logger = await initLogger();
|
||||||
const cfg = initEnvConfig(logger);
|
const cfg = initEnvConfig(logger);
|
||||||
const db = knex({
|
const db = initDbConnection({
|
||||||
client: "pg",
|
dbConnectionUri: cfg.DB_CONNECTION_URI,
|
||||||
connection: cfg.DB_CONNECTION_URI,
|
dbRootCert: cfg.DB_ROOT_CERT
|
||||||
migrations: {
|
|
||||||
directory: path.join(__dirname, "../src/db/migrations"),
|
|
||||||
extension: "ts",
|
|
||||||
tableName: "infisical_migrations"
|
|
||||||
},
|
|
||||||
seeds: {
|
|
||||||
directory: path.join(__dirname, "../src/db/seeds"),
|
|
||||||
extension: "ts"
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.migrate.latest();
|
await db.migrate.latest({
|
||||||
await db.seed.run();
|
directory: path.join(__dirname, "../src/db/migrations"),
|
||||||
|
extension: "ts",
|
||||||
|
tableName: "infisical_migrations"
|
||||||
|
});
|
||||||
|
await db.seed.run({
|
||||||
|
directory: path.join(__dirname, "../src/db/seeds"),
|
||||||
|
extension: "ts"
|
||||||
|
});
|
||||||
const smtp = mockSmtpServer();
|
const smtp = mockSmtpServer();
|
||||||
const queue = mockQueue();
|
const queue = mockQueue();
|
||||||
const keyStore = mockKeyStore();
|
const keyStore = mockKeyStore();
|
||||||
@ -74,7 +72,14 @@ export default {
|
|||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
delete globalThis.jwtToken;
|
delete globalThis.jwtToken;
|
||||||
// called after all tests with this env have been run
|
// called after all tests with this env have been run
|
||||||
await db.migrate.rollback({}, true);
|
await db.migrate.rollback(
|
||||||
|
{
|
||||||
|
directory: path.join(__dirname, "../src/db/migrations"),
|
||||||
|
extension: "ts",
|
||||||
|
tableName: "infisical_migrations"
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
5051
backend/package-lock.json
generated
5051
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,11 +3,39 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./dist/main.mjs",
|
"main": "./dist/main.mjs",
|
||||||
|
"bin": "dist/main.js",
|
||||||
|
"pkg": {
|
||||||
|
"scripts": [
|
||||||
|
"dist/**/*.js",
|
||||||
|
"../frontend/node_modules/next/**/*.js",
|
||||||
|
"../frontend/.next/*/**/*.js",
|
||||||
|
"../frontend/node_modules/next/dist/server/**/*.js",
|
||||||
|
"../frontend/node_modules/@fortawesome/fontawesome-svg-core/**/*.js"
|
||||||
|
],
|
||||||
|
"assets": [
|
||||||
|
"dist/**",
|
||||||
|
"!dist/**/*.js",
|
||||||
|
"node_modules/**",
|
||||||
|
"../frontend/node_modules/**",
|
||||||
|
"../frontend/.next/**",
|
||||||
|
"!../frontend/node_modules/next/dist/server/**/*.js",
|
||||||
|
"../frontend/node_modules/@fortawesome/fontawesome-svg-core/**/*",
|
||||||
|
"../frontend/public/**"
|
||||||
|
],
|
||||||
|
"outputPath": "binary"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"binary:build": "npm run binary:clean && npm run build:frontend && npm run build && npm run binary:babel-frontend && npm run binary:babel-backend && npm run binary:rename-imports",
|
||||||
|
"binary:package": "pkg --no-bytecode --public-packages \"*\" --public --target host .",
|
||||||
|
"binary:babel-backend": " babel ./dist -d ./dist",
|
||||||
|
"binary:babel-frontend": "babel --copy-files ../frontend/.next/server -d ../frontend/.next/server",
|
||||||
|
"binary:clean": "rm -rf ./dist && rm -rf ./binary",
|
||||||
|
"binary:rename-imports": "ts-node ./scripts/rename-mjs.ts",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine",
|
"dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine",
|
||||||
"dev:docker": "nodemon",
|
"dev:docker": "nodemon",
|
||||||
"build": "tsup",
|
"build": "tsup",
|
||||||
|
"build:frontend": "npm run build --prefix ../frontend",
|
||||||
"start": "node dist/main.mjs",
|
"start": "node dist/main.mjs",
|
||||||
"type:check": "tsc --noEmit",
|
"type:check": "tsc --noEmit",
|
||||||
"lint:fix": "eslint --fix --ext js,ts ./src",
|
"lint:fix": "eslint --fix --ext js,ts ./src",
|
||||||
@ -31,6 +59,11 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.18.10",
|
||||||
|
"@babel/core": "^7.18.10",
|
||||||
|
"@babel/plugin-syntax-import-attributes": "^7.24.7",
|
||||||
|
"@babel/preset-env": "^7.18.10",
|
||||||
|
"@babel/preset-react": "^7.24.7",
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/jmespath": "^0.15.2",
|
"@types/jmespath": "^0.15.2",
|
||||||
"@types/jsonwebtoken": "^9.0.5",
|
"@types/jsonwebtoken": "^9.0.5",
|
||||||
@ -48,6 +81,8 @@
|
|||||||
"@types/uuid": "^9.0.7",
|
"@types/uuid": "^9.0.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
||||||
"@typescript-eslint/parser": "^6.20.0",
|
"@typescript-eslint/parser": "^6.20.0",
|
||||||
|
"@yao-pkg/pkg": "^5.12.0",
|
||||||
|
"babel-plugin-transform-import-meta": "^2.2.1",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "^17.1.0",
|
"eslint-config-airbnb-typescript": "^17.1.0",
|
||||||
@ -60,7 +95,7 @@
|
|||||||
"pino-pretty": "^10.2.3",
|
"pino-pretty": "^10.2.3",
|
||||||
"prompt-sync": "^4.2.0",
|
"prompt-sync": "^4.2.0",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.2",
|
||||||
"tsc-alias": "^1.8.8",
|
"tsc-alias": "^1.8.8",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"tsup": "^8.0.1",
|
"tsup": "^8.0.1",
|
||||||
@ -72,6 +107,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-iam": "^3.525.0",
|
"@aws-sdk/client-iam": "^3.525.0",
|
||||||
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
||||||
|
"@aws-sdk/client-sts": "^3.600.0",
|
||||||
"@casl/ability": "^6.5.0",
|
"@casl/ability": "^6.5.0",
|
||||||
"@fastify/cookie": "^9.3.1",
|
"@fastify/cookie": "^9.3.1",
|
||||||
"@fastify/cors": "^8.5.0",
|
"@fastify/cors": "^8.5.0",
|
||||||
@ -89,7 +125,8 @@
|
|||||||
"@peculiar/asn1-schema": "^2.3.8",
|
"@peculiar/asn1-schema": "^2.3.8",
|
||||||
"@peculiar/x509": "^1.10.0",
|
"@peculiar/x509": "^1.10.0",
|
||||||
"@serdnam/pino-cloudwatch-transport": "^1.0.4",
|
"@serdnam/pino-cloudwatch-transport": "^1.0.4",
|
||||||
"@sindresorhus/slugify": "^2.2.1",
|
"@team-plain/typescript-sdk": "^4.6.1",
|
||||||
|
"@sindresorhus/slugify": "1.1.0",
|
||||||
"@ucast/mongo2js": "^1.3.4",
|
"@ucast/mongo2js": "^1.3.4",
|
||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"argon2": "^0.31.2",
|
"argon2": "^0.31.2",
|
||||||
@ -117,7 +154,7 @@
|
|||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"mysql2": "^3.9.8",
|
"mysql2": "^3.9.8",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^3.3.4",
|
||||||
"nodemailer": "^6.9.9",
|
"nodemailer": "^6.9.9",
|
||||||
"openid-client": "^5.6.5",
|
"openid-client": "^5.6.5",
|
||||||
"ora": "^7.0.1",
|
"ora": "^7.0.1",
|
||||||
@ -133,6 +170,7 @@
|
|||||||
"posthog-node": "^3.6.2",
|
"posthog-node": "^3.6.2",
|
||||||
"probot": "^13.0.0",
|
"probot": "^13.0.0",
|
||||||
"smee-client": "^2.0.0",
|
"smee-client": "^2.0.0",
|
||||||
|
"tedious": "^18.2.1",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
"tweetnacl-util": "^0.15.1",
|
"tweetnacl-util": "^0.15.1",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import promptSync from "prompt-sync";
|
import promptSync from "prompt-sync";
|
||||||
|
import slugify from "@sindresorhus/slugify"
|
||||||
|
|
||||||
const prompt = promptSync({ sigint: true });
|
const prompt = promptSync({ sigint: true });
|
||||||
|
|
||||||
const migrationName = prompt("Enter name for migration: ");
|
const migrationName = prompt("Enter name for migration: ");
|
||||||
|
|
||||||
// Remove spaces from migration name and replace with hyphens
|
// Remove spaces from migration name and replace with hyphens
|
||||||
const formattedMigrationName = migrationName.replace(/\s+/g, "-");
|
const formattedMigrationName = slugify(migrationName);
|
||||||
|
|
||||||
execSync(
|
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 ${formattedMigrationName}`,
|
||||||
|
27
backend/scripts/rename-mjs.ts
Normal file
27
backend/scripts/rename-mjs.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-shadow */
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
function replaceMjsOccurrences(directory: string) {
|
||||||
|
fs.readdir(directory, (err, files) => {
|
||||||
|
if (err) throw err;
|
||||||
|
files.forEach((file) => {
|
||||||
|
const filePath = path.join(directory, file);
|
||||||
|
if (fs.statSync(filePath).isDirectory()) {
|
||||||
|
replaceMjsOccurrences(filePath);
|
||||||
|
} else {
|
||||||
|
fs.readFile(filePath, "utf8", (err, data) => {
|
||||||
|
if (err) throw err;
|
||||||
|
const result = data.replace(/\.mjs/g, ".js");
|
||||||
|
fs.writeFile(filePath, result, "utf8", (err) => {
|
||||||
|
if (err) throw err;
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`Updated: ${filePath}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceMjsOccurrences("dist");
|
2
backend/src/@types/fastify.d.ts
vendored
2
backend/src/@types/fastify.d.ts
vendored
@ -65,6 +65,7 @@ import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin
|
|||||||
import { TTelemetryServiceFactory } from "@app/services/telemetry/telemetry-service";
|
import { TTelemetryServiceFactory } from "@app/services/telemetry/telemetry-service";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { TUserServiceFactory } from "@app/services/user/user-service";
|
import { TUserServiceFactory } from "@app/services/user/user-service";
|
||||||
|
import { TUserEngagementServiceFactory } from "@app/services/user-engagement/user-engagement-service";
|
||||||
import { TWebhookServiceFactory } from "@app/services/webhook/webhook-service";
|
import { TWebhookServiceFactory } from "@app/services/webhook/webhook-service";
|
||||||
|
|
||||||
declare module "fastify" {
|
declare module "fastify" {
|
||||||
@ -157,6 +158,7 @@ declare module "fastify" {
|
|||||||
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
||||||
secretSharing: TSecretSharingServiceFactory;
|
secretSharing: TSecretSharingServiceFactory;
|
||||||
rateLimit: TRateLimitServiceFactory;
|
rateLimit: TRateLimitServiceFactory;
|
||||||
|
userEngagement: TUserEngagementServiceFactory;
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
239
backend/src/@types/knex.d.ts
vendored
239
backend/src/@types/knex.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex as KnexOriginal } from "knex";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TableName,
|
TableName,
|
||||||
@ -280,318 +280,371 @@ import {
|
|||||||
TWebhooksUpdate
|
TWebhooksUpdate
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
|
|
||||||
|
declare module "knex" {
|
||||||
|
namespace Knex {
|
||||||
|
interface QueryInterface {
|
||||||
|
primaryNode(): KnexOriginal;
|
||||||
|
replicaNode(): KnexOriginal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
declare module "knex/types/tables" {
|
declare module "knex/types/tables" {
|
||||||
interface Tables {
|
interface Tables {
|
||||||
[TableName.Users]: Knex.CompositeTableType<TUsers, TUsersInsert, TUsersUpdate>;
|
[TableName.Users]: KnexOriginal.CompositeTableType<TUsers, TUsersInsert, TUsersUpdate>;
|
||||||
[TableName.Groups]: Knex.CompositeTableType<TGroups, TGroupsInsert, TGroupsUpdate>;
|
[TableName.Groups]: KnexOriginal.CompositeTableType<TGroups, TGroupsInsert, TGroupsUpdate>;
|
||||||
[TableName.CertificateAuthority]: Knex.CompositeTableType<
|
[TableName.CertificateAuthority]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateAuthorities,
|
TCertificateAuthorities,
|
||||||
TCertificateAuthoritiesInsert,
|
TCertificateAuthoritiesInsert,
|
||||||
TCertificateAuthoritiesUpdate
|
TCertificateAuthoritiesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.CertificateAuthorityCert]: Knex.CompositeTableType<
|
[TableName.CertificateAuthorityCert]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateAuthorityCerts,
|
TCertificateAuthorityCerts,
|
||||||
TCertificateAuthorityCertsInsert,
|
TCertificateAuthorityCertsInsert,
|
||||||
TCertificateAuthorityCertsUpdate
|
TCertificateAuthorityCertsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.CertificateAuthoritySecret]: Knex.CompositeTableType<
|
[TableName.CertificateAuthoritySecret]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateAuthoritySecret,
|
TCertificateAuthoritySecret,
|
||||||
TCertificateAuthoritySecretInsert,
|
TCertificateAuthoritySecretInsert,
|
||||||
TCertificateAuthoritySecretUpdate
|
TCertificateAuthoritySecretUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.CertificateAuthorityCrl]: Knex.CompositeTableType<
|
[TableName.CertificateAuthorityCrl]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateAuthorityCrl,
|
TCertificateAuthorityCrl,
|
||||||
TCertificateAuthorityCrlInsert,
|
TCertificateAuthorityCrlInsert,
|
||||||
TCertificateAuthorityCrlUpdate
|
TCertificateAuthorityCrlUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.Certificate]: Knex.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
|
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
|
||||||
[TableName.CertificateBody]: Knex.CompositeTableType<
|
[TableName.CertificateBody]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateBodies,
|
TCertificateBodies,
|
||||||
TCertificateBodiesInsert,
|
TCertificateBodiesInsert,
|
||||||
TCertificateBodiesUpdate
|
TCertificateBodiesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.CertificateSecret]: Knex.CompositeTableType<
|
[TableName.CertificateSecret]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateSecrets,
|
TCertificateSecrets,
|
||||||
TCertificateSecretsInsert,
|
TCertificateSecretsInsert,
|
||||||
TCertificateSecretsUpdate
|
TCertificateSecretsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.UserGroupMembership]: Knex.CompositeTableType<
|
[TableName.UserGroupMembership]: KnexOriginal.CompositeTableType<
|
||||||
TUserGroupMembership,
|
TUserGroupMembership,
|
||||||
TUserGroupMembershipInsert,
|
TUserGroupMembershipInsert,
|
||||||
TUserGroupMembershipUpdate
|
TUserGroupMembershipUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.GroupProjectMembership]: Knex.CompositeTableType<
|
[TableName.GroupProjectMembership]: KnexOriginal.CompositeTableType<
|
||||||
TGroupProjectMemberships,
|
TGroupProjectMemberships,
|
||||||
TGroupProjectMembershipsInsert,
|
TGroupProjectMembershipsInsert,
|
||||||
TGroupProjectMembershipsUpdate
|
TGroupProjectMembershipsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.GroupProjectMembershipRole]: Knex.CompositeTableType<
|
[TableName.GroupProjectMembershipRole]: KnexOriginal.CompositeTableType<
|
||||||
TGroupProjectMembershipRoles,
|
TGroupProjectMembershipRoles,
|
||||||
TGroupProjectMembershipRolesInsert,
|
TGroupProjectMembershipRolesInsert,
|
||||||
TGroupProjectMembershipRolesUpdate
|
TGroupProjectMembershipRolesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.UserAliases]: Knex.CompositeTableType<TUserAliases, TUserAliasesInsert, TUserAliasesUpdate>;
|
[TableName.UserAliases]: KnexOriginal.CompositeTableType<TUserAliases, TUserAliasesInsert, TUserAliasesUpdate>;
|
||||||
[TableName.UserEncryptionKey]: Knex.CompositeTableType<
|
[TableName.UserEncryptionKey]: KnexOriginal.CompositeTableType<
|
||||||
TUserEncryptionKeys,
|
TUserEncryptionKeys,
|
||||||
TUserEncryptionKeysInsert,
|
TUserEncryptionKeysInsert,
|
||||||
TUserEncryptionKeysUpdate
|
TUserEncryptionKeysUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.AuthTokens]: Knex.CompositeTableType<TAuthTokens, TAuthTokensInsert, TAuthTokensUpdate>;
|
[TableName.AuthTokens]: KnexOriginal.CompositeTableType<TAuthTokens, TAuthTokensInsert, TAuthTokensUpdate>;
|
||||||
[TableName.AuthTokenSession]: Knex.CompositeTableType<
|
[TableName.AuthTokenSession]: KnexOriginal.CompositeTableType<
|
||||||
TAuthTokenSessions,
|
TAuthTokenSessions,
|
||||||
TAuthTokenSessionsInsert,
|
TAuthTokenSessionsInsert,
|
||||||
TAuthTokenSessionsUpdate
|
TAuthTokenSessionsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.BackupPrivateKey]: Knex.CompositeTableType<
|
[TableName.BackupPrivateKey]: KnexOriginal.CompositeTableType<
|
||||||
TBackupPrivateKey,
|
TBackupPrivateKey,
|
||||||
TBackupPrivateKeyInsert,
|
TBackupPrivateKeyInsert,
|
||||||
TBackupPrivateKeyUpdate
|
TBackupPrivateKeyUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.Organization]: Knex.CompositeTableType<TOrganizations, TOrganizationsInsert, TOrganizationsUpdate>;
|
[TableName.Organization]: KnexOriginal.CompositeTableType<
|
||||||
[TableName.OrgMembership]: Knex.CompositeTableType<TOrgMemberships, TOrgMembershipsInsert, TOrgMembershipsUpdate>;
|
TOrganizations,
|
||||||
[TableName.OrgRoles]: Knex.CompositeTableType<TOrgRoles, TOrgRolesInsert, TOrgRolesUpdate>;
|
TOrganizationsInsert,
|
||||||
[TableName.IncidentContact]: Knex.CompositeTableType<
|
TOrganizationsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.OrgMembership]: KnexOriginal.CompositeTableType<
|
||||||
|
TOrgMemberships,
|
||||||
|
TOrgMembershipsInsert,
|
||||||
|
TOrgMembershipsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.OrgRoles]: KnexOriginal.CompositeTableType<TOrgRoles, TOrgRolesInsert, TOrgRolesUpdate>;
|
||||||
|
[TableName.IncidentContact]: KnexOriginal.CompositeTableType<
|
||||||
TIncidentContacts,
|
TIncidentContacts,
|
||||||
TIncidentContactsInsert,
|
TIncidentContactsInsert,
|
||||||
TIncidentContactsUpdate
|
TIncidentContactsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.UserAction]: Knex.CompositeTableType<TUserActions, TUserActionsInsert, TUserActionsUpdate>;
|
[TableName.UserAction]: KnexOriginal.CompositeTableType<TUserActions, TUserActionsInsert, TUserActionsUpdate>;
|
||||||
[TableName.SuperAdmin]: Knex.CompositeTableType<TSuperAdmin, TSuperAdminInsert, TSuperAdminUpdate>;
|
[TableName.SuperAdmin]: KnexOriginal.CompositeTableType<TSuperAdmin, TSuperAdminInsert, TSuperAdminUpdate>;
|
||||||
[TableName.ApiKey]: Knex.CompositeTableType<TApiKeys, TApiKeysInsert, TApiKeysUpdate>;
|
[TableName.ApiKey]: KnexOriginal.CompositeTableType<TApiKeys, TApiKeysInsert, TApiKeysUpdate>;
|
||||||
[TableName.Project]: Knex.CompositeTableType<TProjects, TProjectsInsert, TProjectsUpdate>;
|
[TableName.Project]: KnexOriginal.CompositeTableType<TProjects, TProjectsInsert, TProjectsUpdate>;
|
||||||
[TableName.ProjectMembership]: Knex.CompositeTableType<
|
[TableName.ProjectMembership]: KnexOriginal.CompositeTableType<
|
||||||
TProjectMemberships,
|
TProjectMemberships,
|
||||||
TProjectMembershipsInsert,
|
TProjectMembershipsInsert,
|
||||||
TProjectMembershipsUpdate
|
TProjectMembershipsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.Environment]: Knex.CompositeTableType<
|
[TableName.Environment]: KnexOriginal.CompositeTableType<
|
||||||
TProjectEnvironments,
|
TProjectEnvironments,
|
||||||
TProjectEnvironmentsInsert,
|
TProjectEnvironmentsInsert,
|
||||||
TProjectEnvironmentsUpdate
|
TProjectEnvironmentsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.ProjectBot]: Knex.CompositeTableType<TProjectBots, TProjectBotsInsert, TProjectBotsUpdate>;
|
[TableName.ProjectBot]: KnexOriginal.CompositeTableType<TProjectBots, TProjectBotsInsert, TProjectBotsUpdate>;
|
||||||
[TableName.ProjectUserMembershipRole]: Knex.CompositeTableType<
|
[TableName.ProjectUserMembershipRole]: KnexOriginal.CompositeTableType<
|
||||||
TProjectUserMembershipRoles,
|
TProjectUserMembershipRoles,
|
||||||
TProjectUserMembershipRolesInsert,
|
TProjectUserMembershipRolesInsert,
|
||||||
TProjectUserMembershipRolesUpdate
|
TProjectUserMembershipRolesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
|
[TableName.ProjectRoles]: KnexOriginal.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
|
||||||
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
|
[TableName.ProjectUserAdditionalPrivilege]: KnexOriginal.CompositeTableType<
|
||||||
TProjectUserAdditionalPrivilege,
|
TProjectUserAdditionalPrivilege,
|
||||||
TProjectUserAdditionalPrivilegeInsert,
|
TProjectUserAdditionalPrivilegeInsert,
|
||||||
TProjectUserAdditionalPrivilegeUpdate
|
TProjectUserAdditionalPrivilegeUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
[TableName.ProjectKeys]: KnexOriginal.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
||||||
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
[TableName.Secret]: KnexOriginal.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
||||||
[TableName.SecretReference]: Knex.CompositeTableType<
|
[TableName.SecretReference]: KnexOriginal.CompositeTableType<
|
||||||
TSecretReferences,
|
TSecretReferences,
|
||||||
TSecretReferencesInsert,
|
TSecretReferencesInsert,
|
||||||
TSecretReferencesUpdate
|
TSecretReferencesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
[TableName.SecretBlindIndex]: KnexOriginal.CompositeTableType<
|
||||||
TSecretBlindIndexes,
|
TSecretBlindIndexes,
|
||||||
TSecretBlindIndexesInsert,
|
TSecretBlindIndexesInsert,
|
||||||
TSecretBlindIndexesUpdate
|
TSecretBlindIndexesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretVersion]: Knex.CompositeTableType<TSecretVersions, TSecretVersionsInsert, TSecretVersionsUpdate>;
|
[TableName.SecretVersion]: KnexOriginal.CompositeTableType<
|
||||||
[TableName.SecretFolder]: Knex.CompositeTableType<TSecretFolders, TSecretFoldersInsert, TSecretFoldersUpdate>;
|
TSecretVersions,
|
||||||
[TableName.SecretFolderVersion]: Knex.CompositeTableType<
|
TSecretVersionsInsert,
|
||||||
|
TSecretVersionsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.SecretFolder]: KnexOriginal.CompositeTableType<
|
||||||
|
TSecretFolders,
|
||||||
|
TSecretFoldersInsert,
|
||||||
|
TSecretFoldersUpdate
|
||||||
|
>;
|
||||||
|
[TableName.SecretFolderVersion]: KnexOriginal.CompositeTableType<
|
||||||
TSecretFolderVersions,
|
TSecretFolderVersions,
|
||||||
TSecretFolderVersionsInsert,
|
TSecretFolderVersionsInsert,
|
||||||
TSecretFolderVersionsUpdate
|
TSecretFolderVersionsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretSharing]: Knex.CompositeTableType<TSecretSharing, TSecretSharingInsert, TSecretSharingUpdate>;
|
[TableName.SecretSharing]: KnexOriginal.CompositeTableType<
|
||||||
[TableName.RateLimit]: Knex.CompositeTableType<TRateLimit, TRateLimitInsert, TRateLimitUpdate>;
|
TSecretSharing,
|
||||||
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
|
TSecretSharingInsert,
|
||||||
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
|
TSecretSharingUpdate
|
||||||
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
|
>;
|
||||||
[TableName.Webhook]: Knex.CompositeTableType<TWebhooks, TWebhooksInsert, TWebhooksUpdate>;
|
[TableName.RateLimit]: KnexOriginal.CompositeTableType<TRateLimit, TRateLimitInsert, TRateLimitUpdate>;
|
||||||
[TableName.ServiceToken]: Knex.CompositeTableType<TServiceTokens, TServiceTokensInsert, TServiceTokensUpdate>;
|
[TableName.SecretTag]: KnexOriginal.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
|
||||||
[TableName.IntegrationAuth]: Knex.CompositeTableType<
|
[TableName.SecretImport]: KnexOriginal.CompositeTableType<
|
||||||
|
TSecretImports,
|
||||||
|
TSecretImportsInsert,
|
||||||
|
TSecretImportsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.Integration]: KnexOriginal.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
|
||||||
|
[TableName.Webhook]: KnexOriginal.CompositeTableType<TWebhooks, TWebhooksInsert, TWebhooksUpdate>;
|
||||||
|
[TableName.ServiceToken]: KnexOriginal.CompositeTableType<
|
||||||
|
TServiceTokens,
|
||||||
|
TServiceTokensInsert,
|
||||||
|
TServiceTokensUpdate
|
||||||
|
>;
|
||||||
|
[TableName.IntegrationAuth]: KnexOriginal.CompositeTableType<
|
||||||
TIntegrationAuths,
|
TIntegrationAuths,
|
||||||
TIntegrationAuthsInsert,
|
TIntegrationAuthsInsert,
|
||||||
TIntegrationAuthsUpdate
|
TIntegrationAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.Identity]: Knex.CompositeTableType<TIdentities, TIdentitiesInsert, TIdentitiesUpdate>;
|
[TableName.Identity]: KnexOriginal.CompositeTableType<TIdentities, TIdentitiesInsert, TIdentitiesUpdate>;
|
||||||
[TableName.IdentityUniversalAuth]: Knex.CompositeTableType<
|
[TableName.IdentityUniversalAuth]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityUniversalAuths,
|
TIdentityUniversalAuths,
|
||||||
TIdentityUniversalAuthsInsert,
|
TIdentityUniversalAuthsInsert,
|
||||||
TIdentityUniversalAuthsUpdate
|
TIdentityUniversalAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityKubernetesAuth]: Knex.CompositeTableType<
|
[TableName.IdentityKubernetesAuth]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityKubernetesAuths,
|
TIdentityKubernetesAuths,
|
||||||
TIdentityKubernetesAuthsInsert,
|
TIdentityKubernetesAuthsInsert,
|
||||||
TIdentityKubernetesAuthsUpdate
|
TIdentityKubernetesAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityGcpAuth]: Knex.CompositeTableType<
|
[TableName.IdentityGcpAuth]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityGcpAuths,
|
TIdentityGcpAuths,
|
||||||
TIdentityGcpAuthsInsert,
|
TIdentityGcpAuthsInsert,
|
||||||
TIdentityGcpAuthsUpdate
|
TIdentityGcpAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityAwsAuth]: Knex.CompositeTableType<
|
[TableName.IdentityAwsAuth]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityAwsAuths,
|
TIdentityAwsAuths,
|
||||||
TIdentityAwsAuthsInsert,
|
TIdentityAwsAuthsInsert,
|
||||||
TIdentityAwsAuthsUpdate
|
TIdentityAwsAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityAzureAuth]: Knex.CompositeTableType<
|
[TableName.IdentityAzureAuth]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityAzureAuths,
|
TIdentityAzureAuths,
|
||||||
TIdentityAzureAuthsInsert,
|
TIdentityAzureAuthsInsert,
|
||||||
TIdentityAzureAuthsUpdate
|
TIdentityAzureAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
|
[TableName.IdentityUaClientSecret]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityUaClientSecrets,
|
TIdentityUaClientSecrets,
|
||||||
TIdentityUaClientSecretsInsert,
|
TIdentityUaClientSecretsInsert,
|
||||||
TIdentityUaClientSecretsUpdate
|
TIdentityUaClientSecretsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityAccessToken]: Knex.CompositeTableType<
|
[TableName.IdentityAccessToken]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityAccessTokens,
|
TIdentityAccessTokens,
|
||||||
TIdentityAccessTokensInsert,
|
TIdentityAccessTokensInsert,
|
||||||
TIdentityAccessTokensUpdate
|
TIdentityAccessTokensUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityOrgMembership]: Knex.CompositeTableType<
|
[TableName.IdentityOrgMembership]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityOrgMemberships,
|
TIdentityOrgMemberships,
|
||||||
TIdentityOrgMembershipsInsert,
|
TIdentityOrgMembershipsInsert,
|
||||||
TIdentityOrgMembershipsUpdate
|
TIdentityOrgMembershipsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityProjectMembership]: Knex.CompositeTableType<
|
[TableName.IdentityProjectMembership]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityProjectMemberships,
|
TIdentityProjectMemberships,
|
||||||
TIdentityProjectMembershipsInsert,
|
TIdentityProjectMembershipsInsert,
|
||||||
TIdentityProjectMembershipsUpdate
|
TIdentityProjectMembershipsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityProjectMembershipRole]: Knex.CompositeTableType<
|
[TableName.IdentityProjectMembershipRole]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityProjectMembershipRole,
|
TIdentityProjectMembershipRole,
|
||||||
TIdentityProjectMembershipRoleInsert,
|
TIdentityProjectMembershipRoleInsert,
|
||||||
TIdentityProjectMembershipRoleUpdate
|
TIdentityProjectMembershipRoleUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityProjectAdditionalPrivilege]: Knex.CompositeTableType<
|
[TableName.IdentityProjectAdditionalPrivilege]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityProjectAdditionalPrivilege,
|
TIdentityProjectAdditionalPrivilege,
|
||||||
TIdentityProjectAdditionalPrivilegeInsert,
|
TIdentityProjectAdditionalPrivilegeInsert,
|
||||||
TIdentityProjectAdditionalPrivilegeUpdate
|
TIdentityProjectAdditionalPrivilegeUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
[TableName.AccessApprovalPolicy]: Knex.CompositeTableType<
|
[TableName.AccessApprovalPolicy]: KnexOriginal.CompositeTableType<
|
||||||
TAccessApprovalPolicies,
|
TAccessApprovalPolicies,
|
||||||
TAccessApprovalPoliciesInsert,
|
TAccessApprovalPoliciesInsert,
|
||||||
TAccessApprovalPoliciesUpdate
|
TAccessApprovalPoliciesUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
[TableName.AccessApprovalPolicyApprover]: Knex.CompositeTableType<
|
[TableName.AccessApprovalPolicyApprover]: KnexOriginal.CompositeTableType<
|
||||||
TAccessApprovalPoliciesApprovers,
|
TAccessApprovalPoliciesApprovers,
|
||||||
TAccessApprovalPoliciesApproversInsert,
|
TAccessApprovalPoliciesApproversInsert,
|
||||||
TAccessApprovalPoliciesApproversUpdate
|
TAccessApprovalPoliciesApproversUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
[TableName.AccessApprovalRequest]: Knex.CompositeTableType<
|
[TableName.AccessApprovalRequest]: KnexOriginal.CompositeTableType<
|
||||||
TAccessApprovalRequests,
|
TAccessApprovalRequests,
|
||||||
TAccessApprovalRequestsInsert,
|
TAccessApprovalRequestsInsert,
|
||||||
TAccessApprovalRequestsUpdate
|
TAccessApprovalRequestsUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
[TableName.AccessApprovalRequestReviewer]: Knex.CompositeTableType<
|
[TableName.AccessApprovalRequestReviewer]: KnexOriginal.CompositeTableType<
|
||||||
TAccessApprovalRequestsReviewers,
|
TAccessApprovalRequestsReviewers,
|
||||||
TAccessApprovalRequestsReviewersInsert,
|
TAccessApprovalRequestsReviewersInsert,
|
||||||
TAccessApprovalRequestsReviewersUpdate
|
TAccessApprovalRequestsReviewersUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
[TableName.ScimToken]: KnexOriginal.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
||||||
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
[TableName.SecretApprovalPolicy]: KnexOriginal.CompositeTableType<
|
||||||
TSecretApprovalPolicies,
|
TSecretApprovalPolicies,
|
||||||
TSecretApprovalPoliciesInsert,
|
TSecretApprovalPoliciesInsert,
|
||||||
TSecretApprovalPoliciesUpdate
|
TSecretApprovalPoliciesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretApprovalPolicyApprover]: Knex.CompositeTableType<
|
[TableName.SecretApprovalPolicyApprover]: KnexOriginal.CompositeTableType<
|
||||||
TSecretApprovalPoliciesApprovers,
|
TSecretApprovalPoliciesApprovers,
|
||||||
TSecretApprovalPoliciesApproversInsert,
|
TSecretApprovalPoliciesApproversInsert,
|
||||||
TSecretApprovalPoliciesApproversUpdate
|
TSecretApprovalPoliciesApproversUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretApprovalRequest]: Knex.CompositeTableType<
|
[TableName.SecretApprovalRequest]: KnexOriginal.CompositeTableType<
|
||||||
TSecretApprovalRequests,
|
TSecretApprovalRequests,
|
||||||
TSecretApprovalRequestsInsert,
|
TSecretApprovalRequestsInsert,
|
||||||
TSecretApprovalRequestsUpdate
|
TSecretApprovalRequestsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretApprovalRequestReviewer]: Knex.CompositeTableType<
|
[TableName.SecretApprovalRequestReviewer]: KnexOriginal.CompositeTableType<
|
||||||
TSecretApprovalRequestsReviewers,
|
TSecretApprovalRequestsReviewers,
|
||||||
TSecretApprovalRequestsReviewersInsert,
|
TSecretApprovalRequestsReviewersInsert,
|
||||||
TSecretApprovalRequestsReviewersUpdate
|
TSecretApprovalRequestsReviewersUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretApprovalRequestSecret]: Knex.CompositeTableType<
|
[TableName.SecretApprovalRequestSecret]: KnexOriginal.CompositeTableType<
|
||||||
TSecretApprovalRequestsSecrets,
|
TSecretApprovalRequestsSecrets,
|
||||||
TSecretApprovalRequestsSecretsInsert,
|
TSecretApprovalRequestsSecretsInsert,
|
||||||
TSecretApprovalRequestsSecretsUpdate
|
TSecretApprovalRequestsSecretsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretApprovalRequestSecretTag]: Knex.CompositeTableType<
|
[TableName.SecretApprovalRequestSecretTag]: KnexOriginal.CompositeTableType<
|
||||||
TSecretApprovalRequestSecretTags,
|
TSecretApprovalRequestSecretTags,
|
||||||
TSecretApprovalRequestSecretTagsInsert,
|
TSecretApprovalRequestSecretTagsInsert,
|
||||||
TSecretApprovalRequestSecretTagsUpdate
|
TSecretApprovalRequestSecretTagsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretRotation]: Knex.CompositeTableType<
|
[TableName.SecretRotation]: KnexOriginal.CompositeTableType<
|
||||||
TSecretRotations,
|
TSecretRotations,
|
||||||
TSecretRotationsInsert,
|
TSecretRotationsInsert,
|
||||||
TSecretRotationsUpdate
|
TSecretRotationsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretRotationOutput]: Knex.CompositeTableType<
|
[TableName.SecretRotationOutput]: KnexOriginal.CompositeTableType<
|
||||||
TSecretRotationOutputs,
|
TSecretRotationOutputs,
|
||||||
TSecretRotationOutputsInsert,
|
TSecretRotationOutputsInsert,
|
||||||
TSecretRotationOutputsUpdate
|
TSecretRotationOutputsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.Snapshot]: Knex.CompositeTableType<TSecretSnapshots, TSecretSnapshotsInsert, TSecretSnapshotsUpdate>;
|
[TableName.Snapshot]: KnexOriginal.CompositeTableType<
|
||||||
[TableName.SnapshotSecret]: Knex.CompositeTableType<
|
TSecretSnapshots,
|
||||||
|
TSecretSnapshotsInsert,
|
||||||
|
TSecretSnapshotsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.SnapshotSecret]: KnexOriginal.CompositeTableType<
|
||||||
TSecretSnapshotSecrets,
|
TSecretSnapshotSecrets,
|
||||||
TSecretSnapshotSecretsInsert,
|
TSecretSnapshotSecretsInsert,
|
||||||
TSecretSnapshotSecretsUpdate
|
TSecretSnapshotSecretsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SnapshotFolder]: Knex.CompositeTableType<
|
[TableName.SnapshotFolder]: KnexOriginal.CompositeTableType<
|
||||||
TSecretSnapshotFolders,
|
TSecretSnapshotFolders,
|
||||||
TSecretSnapshotFoldersInsert,
|
TSecretSnapshotFoldersInsert,
|
||||||
TSecretSnapshotFoldersUpdate
|
TSecretSnapshotFoldersUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.DynamicSecret]: Knex.CompositeTableType<TDynamicSecrets, TDynamicSecretsInsert, TDynamicSecretsUpdate>;
|
[TableName.DynamicSecret]: KnexOriginal.CompositeTableType<
|
||||||
[TableName.DynamicSecretLease]: Knex.CompositeTableType<
|
TDynamicSecrets,
|
||||||
|
TDynamicSecretsInsert,
|
||||||
|
TDynamicSecretsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.DynamicSecretLease]: KnexOriginal.CompositeTableType<
|
||||||
TDynamicSecretLeases,
|
TDynamicSecretLeases,
|
||||||
TDynamicSecretLeasesInsert,
|
TDynamicSecretLeasesInsert,
|
||||||
TDynamicSecretLeasesUpdate
|
TDynamicSecretLeasesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SamlConfig]: Knex.CompositeTableType<TSamlConfigs, TSamlConfigsInsert, TSamlConfigsUpdate>;
|
[TableName.SamlConfig]: KnexOriginal.CompositeTableType<TSamlConfigs, TSamlConfigsInsert, TSamlConfigsUpdate>;
|
||||||
[TableName.OidcConfig]: Knex.CompositeTableType<TOidcConfigs, TOidcConfigsInsert, TOidcConfigsUpdate>;
|
[TableName.OidcConfig]: KnexOriginal.CompositeTableType<TOidcConfigs, TOidcConfigsInsert, TOidcConfigsUpdate>;
|
||||||
[TableName.LdapConfig]: Knex.CompositeTableType<TLdapConfigs, TLdapConfigsInsert, TLdapConfigsUpdate>;
|
[TableName.LdapConfig]: KnexOriginal.CompositeTableType<TLdapConfigs, TLdapConfigsInsert, TLdapConfigsUpdate>;
|
||||||
[TableName.LdapGroupMap]: Knex.CompositeTableType<TLdapGroupMaps, TLdapGroupMapsInsert, TLdapGroupMapsUpdate>;
|
[TableName.LdapGroupMap]: KnexOriginal.CompositeTableType<
|
||||||
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
|
TLdapGroupMaps,
|
||||||
[TableName.AuditLog]: Knex.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
|
TLdapGroupMapsInsert,
|
||||||
[TableName.AuditLogStream]: Knex.CompositeTableType<
|
TLdapGroupMapsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.OrgBot]: KnexOriginal.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
|
||||||
|
[TableName.AuditLog]: KnexOriginal.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
|
||||||
|
[TableName.AuditLogStream]: KnexOriginal.CompositeTableType<
|
||||||
TAuditLogStreams,
|
TAuditLogStreams,
|
||||||
TAuditLogStreamsInsert,
|
TAuditLogStreamsInsert,
|
||||||
TAuditLogStreamsUpdate
|
TAuditLogStreamsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.GitAppInstallSession]: Knex.CompositeTableType<
|
[TableName.GitAppInstallSession]: KnexOriginal.CompositeTableType<
|
||||||
TGitAppInstallSessions,
|
TGitAppInstallSessions,
|
||||||
TGitAppInstallSessionsInsert,
|
TGitAppInstallSessionsInsert,
|
||||||
TGitAppInstallSessionsUpdate
|
TGitAppInstallSessionsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.GitAppOrg]: Knex.CompositeTableType<TGitAppOrg, TGitAppOrgInsert, TGitAppOrgUpdate>;
|
[TableName.GitAppOrg]: KnexOriginal.CompositeTableType<TGitAppOrg, TGitAppOrgInsert, TGitAppOrgUpdate>;
|
||||||
[TableName.SecretScanningGitRisk]: Knex.CompositeTableType<
|
[TableName.SecretScanningGitRisk]: KnexOriginal.CompositeTableType<
|
||||||
TSecretScanningGitRisks,
|
TSecretScanningGitRisks,
|
||||||
TSecretScanningGitRisksInsert,
|
TSecretScanningGitRisksInsert,
|
||||||
TSecretScanningGitRisksUpdate
|
TSecretScanningGitRisksUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.TrustedIps]: Knex.CompositeTableType<TTrustedIps, TTrustedIpsInsert, TTrustedIpsUpdate>;
|
[TableName.TrustedIps]: KnexOriginal.CompositeTableType<TTrustedIps, TTrustedIpsInsert, TTrustedIpsUpdate>;
|
||||||
// Junction tables
|
// Junction tables
|
||||||
[TableName.JnSecretTag]: Knex.CompositeTableType<
|
[TableName.JnSecretTag]: KnexOriginal.CompositeTableType<
|
||||||
TSecretTagJunction,
|
TSecretTagJunction,
|
||||||
TSecretTagJunctionInsert,
|
TSecretTagJunctionInsert,
|
||||||
TSecretTagJunctionUpdate
|
TSecretTagJunctionUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretVersionTag]: Knex.CompositeTableType<
|
[TableName.SecretVersionTag]: KnexOriginal.CompositeTableType<
|
||||||
TSecretVersionTagJunction,
|
TSecretVersionTagJunction,
|
||||||
TSecretVersionTagJunctionInsert,
|
TSecretVersionTagJunctionInsert,
|
||||||
TSecretVersionTagJunctionUpdate
|
TSecretVersionTagJunctionUpdate
|
||||||
>;
|
>;
|
||||||
// KMS service
|
// KMS service
|
||||||
[TableName.KmsServerRootConfig]: Knex.CompositeTableType<
|
[TableName.KmsServerRootConfig]: KnexOriginal.CompositeTableType<
|
||||||
TKmsRootConfig,
|
TKmsRootConfig,
|
||||||
TKmsRootConfigInsert,
|
TKmsRootConfigInsert,
|
||||||
TKmsRootConfigUpdate
|
TKmsRootConfigUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.KmsKey]: Knex.CompositeTableType<TKmsKeys, TKmsKeysInsert, TKmsKeysUpdate>;
|
[TableName.KmsKey]: KnexOriginal.CompositeTableType<TKmsKeys, TKmsKeysInsert, TKmsKeysUpdate>;
|
||||||
[TableName.KmsKeyVersion]: Knex.CompositeTableType<TKmsKeyVersions, TKmsKeyVersionsInsert, TKmsKeyVersionsUpdate>;
|
[TableName.KmsKeyVersion]: KnexOriginal.CompositeTableType<
|
||||||
|
TKmsKeyVersions,
|
||||||
|
TKmsKeyVersionsInsert,
|
||||||
|
TKmsKeyVersionsUpdate
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,38 @@
|
|||||||
import knex from "knex";
|
import knex, { Knex } from "knex";
|
||||||
|
|
||||||
export type TDbClient = ReturnType<typeof initDbConnection>;
|
export type TDbClient = ReturnType<typeof initDbConnection>;
|
||||||
export const initDbConnection = ({ dbConnectionUri, dbRootCert }: { dbConnectionUri: string; dbRootCert?: string }) => {
|
export const initDbConnection = ({
|
||||||
const db = knex({
|
dbConnectionUri,
|
||||||
|
dbRootCert,
|
||||||
|
readReplicas = []
|
||||||
|
}: {
|
||||||
|
dbConnectionUri: string;
|
||||||
|
dbRootCert?: string;
|
||||||
|
readReplicas?: {
|
||||||
|
dbConnectionUri: string;
|
||||||
|
dbRootCert?: string;
|
||||||
|
}[];
|
||||||
|
}) => {
|
||||||
|
// akhilmhdh: the default Knex is knex.Knex<any, any[]>. but when assigned with knex({<config>}) the value is knex.Knex<any, unknown[]>
|
||||||
|
// this was causing issue with files like `snapshot-dal` `findRecursivelySnapshots` this i am explicitly putting the any and unknown[]
|
||||||
|
// eslint-disable-next-line
|
||||||
|
let db: Knex<any, unknown[]>;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
let readReplicaDbs: Knex<any, unknown[]>[];
|
||||||
|
// @ts-expect-error the querybuilder type is expected but our intension is to return a knex instance
|
||||||
|
knex.QueryBuilder.extend("primaryNode", () => {
|
||||||
|
return db;
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error the querybuilder type is expected but our intension is to return a knex instance
|
||||||
|
knex.QueryBuilder.extend("replicaNode", () => {
|
||||||
|
if (!readReplicaDbs.length) return db;
|
||||||
|
|
||||||
|
const selectedReplica = readReplicaDbs[Math.floor(Math.random() * readReplicaDbs.length)];
|
||||||
|
return selectedReplica;
|
||||||
|
});
|
||||||
|
|
||||||
|
db = knex({
|
||||||
client: "pg",
|
client: "pg",
|
||||||
connection: {
|
connection: {
|
||||||
connectionString: dbConnectionUri,
|
connectionString: dbConnectionUri,
|
||||||
@ -22,5 +52,21 @@ export const initDbConnection = ({ dbConnectionUri, dbRootCert }: { dbConnection
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
readReplicaDbs = readReplicas.map((el) => {
|
||||||
|
const replicaDbCertificate = el.dbRootCert || dbRootCert;
|
||||||
|
return knex({
|
||||||
|
client: "pg",
|
||||||
|
connection: {
|
||||||
|
connectionString: el.dbConnectionUri,
|
||||||
|
ssl: replicaDbCertificate
|
||||||
|
? {
|
||||||
|
rejectUnauthorized: true,
|
||||||
|
ca: Buffer.from(replicaDbCertificate, "base64").toString("ascii")
|
||||||
|
}
|
||||||
|
: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasAwsAssumeRoleCipherText = await knex.schema.hasColumn(
|
||||||
|
TableName.IntegrationAuth,
|
||||||
|
"awsAssumeIamRoleArnCipherText"
|
||||||
|
);
|
||||||
|
const hasAwsAssumeRoleIV = await knex.schema.hasColumn(TableName.IntegrationAuth, "awsAssumeIamRoleArnIV");
|
||||||
|
const hasAwsAssumeRoleTag = await knex.schema.hasColumn(TableName.IntegrationAuth, "awsAssumeIamRoleArnTag");
|
||||||
|
if (await knex.schema.hasTable(TableName.IntegrationAuth)) {
|
||||||
|
await knex.schema.alterTable(TableName.IntegrationAuth, (t) => {
|
||||||
|
if (!hasAwsAssumeRoleCipherText) t.text("awsAssumeIamRoleArnCipherText");
|
||||||
|
if (!hasAwsAssumeRoleIV) t.text("awsAssumeIamRoleArnIV");
|
||||||
|
if (!hasAwsAssumeRoleTag) t.text("awsAssumeIamRoleArnTag");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasAwsAssumeRoleCipherText = await knex.schema.hasColumn(
|
||||||
|
TableName.IntegrationAuth,
|
||||||
|
"awsAssumeIamRoleArnCipherText"
|
||||||
|
);
|
||||||
|
const hasAwsAssumeRoleIV = await knex.schema.hasColumn(TableName.IntegrationAuth, "awsAssumeIamRoleArnIV");
|
||||||
|
const hasAwsAssumeRoleTag = await knex.schema.hasColumn(TableName.IntegrationAuth, "awsAssumeIamRoleArnTag");
|
||||||
|
if (await knex.schema.hasTable(TableName.IntegrationAuth)) {
|
||||||
|
await knex.schema.alterTable(TableName.IntegrationAuth, (t) => {
|
||||||
|
if (hasAwsAssumeRoleCipherText) t.dropColumn("awsAssumeIamRoleArnCipherText");
|
||||||
|
if (hasAwsAssumeRoleIV) t.dropColumn("awsAssumeIamRoleArnIV");
|
||||||
|
if (hasAwsAssumeRoleTag) t.dropColumn("awsAssumeIamRoleArnTag");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SuperAdmin, "enabledLoginMethods"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (tb) => {
|
||||||
|
tb.specificType("enabledLoginMethods", "text[]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.SuperAdmin, "enabledLoginMethods")) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.dropColumn("enabledLoginMethods");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.LdapConfig, "uniqueUserAttribute"))) {
|
||||||
|
await knex.schema.alterTable(TableName.LdapConfig, (tb) => {
|
||||||
|
tb.string("uniqueUserAttribute").notNullable().defaultTo("");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.LdapConfig, "uniqueUserAttribute")) {
|
||||||
|
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
|
||||||
|
t.dropColumn("uniqueUserAttribute");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.Project, "auditLogsRetentionDays"))) {
|
||||||
|
await knex.schema.alterTable(TableName.Project, (tb) => {
|
||||||
|
tb.integer("auditLogsRetentionDays").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.Project, "auditLogsRetentionDays")) {
|
||||||
|
await knex.schema.alterTable(TableName.Project, (t) => {
|
||||||
|
t.dropColumn("auditLogsRetentionDays");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
await createOnUpdateTrigger(knex, TableName.OidcConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.OidcConfig);
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.OrgMembership, "projectFavorites"))) {
|
||||||
|
await knex.schema.alterTable(TableName.OrgMembership, (tb) => {
|
||||||
|
tb.specificType("projectFavorites", "text[]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.OrgMembership, "projectFavorites")) {
|
||||||
|
await knex.schema.alterTable(TableName.OrgMembership, (t) => {
|
||||||
|
t.dropColumn("projectFavorites");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { WebhookType } from "@app/services/webhook/webhook-types";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasUrlCipherText = await knex.schema.hasColumn(TableName.Webhook, "urlCipherText");
|
||||||
|
const hasUrlIV = await knex.schema.hasColumn(TableName.Webhook, "urlIV");
|
||||||
|
const hasUrlTag = await knex.schema.hasColumn(TableName.Webhook, "urlTag");
|
||||||
|
const hasType = await knex.schema.hasColumn(TableName.Webhook, "type");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.Webhook)) {
|
||||||
|
await knex.schema.alterTable(TableName.Webhook, (tb) => {
|
||||||
|
if (!hasUrlCipherText) {
|
||||||
|
tb.text("urlCipherText");
|
||||||
|
}
|
||||||
|
if (!hasUrlIV) {
|
||||||
|
tb.string("urlIV");
|
||||||
|
}
|
||||||
|
if (!hasUrlTag) {
|
||||||
|
tb.string("urlTag");
|
||||||
|
}
|
||||||
|
if (!hasType) {
|
||||||
|
tb.string("type").defaultTo(WebhookType.GENERAL);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasUrlCipherText = await knex.schema.hasColumn(TableName.Webhook, "urlCipherText");
|
||||||
|
const hasUrlIV = await knex.schema.hasColumn(TableName.Webhook, "urlIV");
|
||||||
|
const hasUrlTag = await knex.schema.hasColumn(TableName.Webhook, "urlTag");
|
||||||
|
const hasType = await knex.schema.hasColumn(TableName.Webhook, "type");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.Webhook)) {
|
||||||
|
await knex.schema.alterTable(TableName.Webhook, (t) => {
|
||||||
|
if (hasUrlCipherText) {
|
||||||
|
t.dropColumn("urlCipherText");
|
||||||
|
}
|
||||||
|
if (hasUrlIV) {
|
||||||
|
t.dropColumn("urlIV");
|
||||||
|
}
|
||||||
|
if (hasUrlTag) {
|
||||||
|
t.dropColumn("urlTag");
|
||||||
|
}
|
||||||
|
if (hasType) {
|
||||||
|
t.dropColumn("type");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
// migrate secret approval policy approvers to user id
|
||||||
|
const hasApproverUserId = await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverUserId");
|
||||||
|
const hasApproverId = await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverId");
|
||||||
|
if (!hasApproverUserId) {
|
||||||
|
// add the new fields
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
||||||
|
// if (hasApproverId) tb.setNullable("approverId");
|
||||||
|
tb.uuid("approverUserId");
|
||||||
|
tb.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
|
||||||
|
// convert project membership id => user id
|
||||||
|
await knex(TableName.SecretApprovalPolicyApprover).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
approverUserId: knex(TableName.ProjectMembership)
|
||||||
|
.select("userId")
|
||||||
|
.where("id", knex.raw("??", [`${TableName.SecretApprovalPolicyApprover}.approverId`]))
|
||||||
|
});
|
||||||
|
// drop the old field
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
||||||
|
if (hasApproverId) tb.dropColumn("approverId");
|
||||||
|
tb.uuid("approverUserId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrate secret approval request committer and statusChangeBy to user id
|
||||||
|
const hasSecretApprovalRequestTable = await knex.schema.hasTable(TableName.SecretApprovalRequest);
|
||||||
|
const hasCommitterUserId = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerUserId");
|
||||||
|
const hasCommitterId = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerId");
|
||||||
|
const hasStatusChangeBy = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "statusChangeBy");
|
||||||
|
const hasStatusChangedByUserId = await knex.schema.hasColumn(
|
||||||
|
TableName.SecretApprovalRequest,
|
||||||
|
"statusChangedByUserId"
|
||||||
|
);
|
||||||
|
if (hasSecretApprovalRequestTable) {
|
||||||
|
// new fields
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
|
||||||
|
// if (hasCommitterId) tb.setNullable("committerId");
|
||||||
|
if (!hasCommitterUserId) {
|
||||||
|
tb.uuid("committerUserId");
|
||||||
|
tb.foreign("committerUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
|
||||||
|
}
|
||||||
|
if (!hasStatusChangedByUserId) {
|
||||||
|
tb.uuid("statusChangedByUserId");
|
||||||
|
tb.foreign("statusChangedByUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// copy the assigned project membership => user id to new fields
|
||||||
|
await knex(TableName.SecretApprovalRequest).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
committerUserId: knex(TableName.ProjectMembership)
|
||||||
|
.select("userId")
|
||||||
|
.where("id", knex.raw("??", [`${TableName.SecretApprovalRequest}.committerId`])),
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
statusChangedByUserId: knex(TableName.ProjectMembership)
|
||||||
|
.select("userId")
|
||||||
|
.where("id", knex.raw("??", [`${TableName.SecretApprovalRequest}.statusChangeBy`]))
|
||||||
|
});
|
||||||
|
// drop old fields
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
|
||||||
|
if (hasStatusChangeBy) tb.dropColumn("statusChangeBy");
|
||||||
|
if (hasCommitterId) tb.dropColumn("committerId");
|
||||||
|
tb.uuid("committerUserId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrate secret approval request reviewer to user id
|
||||||
|
const hasMemberId = await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "member");
|
||||||
|
const hasReviewerUserId = await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "reviewerUserId");
|
||||||
|
if (!hasReviewerUserId) {
|
||||||
|
// new fields
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => {
|
||||||
|
// if (hasMemberId) tb.setNullable("member");
|
||||||
|
tb.uuid("reviewerUserId");
|
||||||
|
tb.foreign("reviewerUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
// copy project membership => user id to new fields
|
||||||
|
await knex(TableName.SecretApprovalRequestReviewer).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
reviewerUserId: knex(TableName.ProjectMembership)
|
||||||
|
.select("userId")
|
||||||
|
.where("id", knex.raw("??", [`${TableName.SecretApprovalRequestReviewer}.member`]))
|
||||||
|
});
|
||||||
|
// drop table
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => {
|
||||||
|
if (hasMemberId) tb.dropColumn("member");
|
||||||
|
tb.uuid("reviewerUserId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasApproverUserId = await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverUserId");
|
||||||
|
const hasApproverId = await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverId");
|
||||||
|
if (hasApproverUserId) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
||||||
|
if (!hasApproverId) {
|
||||||
|
tb.uuid("approverId");
|
||||||
|
tb.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasApproverId) {
|
||||||
|
await knex(TableName.SecretApprovalPolicyApprover).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
approverId: knex(TableName.ProjectMembership)
|
||||||
|
.select("id")
|
||||||
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalPolicyApprover}.approverUserId`]))
|
||||||
|
});
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
||||||
|
tb.dropColumn("approverUserId");
|
||||||
|
tb.uuid("approverId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasSecretApprovalRequestTable = await knex.schema.hasTable(TableName.SecretApprovalRequest);
|
||||||
|
const hasCommitterUserId = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerUserId");
|
||||||
|
const hasCommitterId = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerId");
|
||||||
|
const hasStatusChangeBy = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "statusChangeBy");
|
||||||
|
const hasStatusChangedByUser = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "statusChangedByUserId");
|
||||||
|
if (hasSecretApprovalRequestTable) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
|
||||||
|
// if (hasCommitterId) tb.uuid("committerId").notNullable().alter();
|
||||||
|
if (!hasCommitterId) {
|
||||||
|
tb.uuid("committerId");
|
||||||
|
tb.foreign("committerId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||||
|
}
|
||||||
|
if (!hasStatusChangeBy) {
|
||||||
|
tb.uuid("statusChangeBy");
|
||||||
|
tb.foreign("statusChangeBy").references("id").inTable(TableName.ProjectMembership).onDelete("SET NULL");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex(TableName.SecretApprovalRequest).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
committerId: knex(TableName.ProjectMembership)
|
||||||
|
.select("id")
|
||||||
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.committerUserId`])),
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
statusChangeBy: knex(TableName.ProjectMembership)
|
||||||
|
.select("id")
|
||||||
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.statusChangedByUserId`]))
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
|
||||||
|
if (hasCommitterUserId) tb.dropColumn("committerUserId");
|
||||||
|
if (hasStatusChangedByUser) tb.dropColumn("statusChangedByUserId");
|
||||||
|
if (hasCommitterId) tb.uuid("committerId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasMemberId = await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "member");
|
||||||
|
const hasReviewerUserId = await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "reviewerUserId");
|
||||||
|
if (hasReviewerUserId) {
|
||||||
|
if (!hasMemberId) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => {
|
||||||
|
// if (hasMemberId) tb.uuid("member").notNullable().alter();
|
||||||
|
tb.uuid("member");
|
||||||
|
tb.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await knex(TableName.SecretApprovalRequestReviewer).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore because generate schema happens after this
|
||||||
|
member: knex(TableName.ProjectMembership)
|
||||||
|
.select("id")
|
||||||
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`]))
|
||||||
|
});
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => {
|
||||||
|
tb.uuid("member").notNullable().alter();
|
||||||
|
tb.dropColumn("reviewerUserId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,10 @@ export const IntegrationAuthsSchema = z.object({
|
|||||||
keyEncoding: z.string(),
|
keyEncoding: z.string(),
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
awsAssumeIamRoleArnCipherText: z.string().nullable().optional(),
|
||||||
|
awsAssumeIamRoleArnIV: z.string().nullable().optional(),
|
||||||
|
awsAssumeIamRoleArnTag: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIntegrationAuths = z.infer<typeof IntegrationAuthsSchema>;
|
export type TIntegrationAuths = z.infer<typeof IntegrationAuthsSchema>;
|
||||||
|
@ -26,7 +26,8 @@ export const LdapConfigsSchema = z.object({
|
|||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
groupSearchBase: z.string().default(""),
|
groupSearchBase: z.string().default(""),
|
||||||
groupSearchFilter: z.string().default(""),
|
groupSearchFilter: z.string().default(""),
|
||||||
searchFilter: z.string().default("")
|
searchFilter: z.string().default(""),
|
||||||
|
uniqueUserAttribute: z.string().default("")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>;
|
export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>;
|
||||||
|
@ -16,7 +16,8 @@ export const OrgMembershipsSchema = z.object({
|
|||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
userId: z.string().uuid().nullable().optional(),
|
userId: z.string().uuid().nullable().optional(),
|
||||||
orgId: z.string().uuid(),
|
orgId: z.string().uuid(),
|
||||||
roleId: z.string().uuid().nullable().optional()
|
roleId: z.string().uuid().nullable().optional(),
|
||||||
|
projectFavorites: z.string().array().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TOrgMemberships = z.infer<typeof OrgMembershipsSchema>;
|
export type TOrgMemberships = z.infer<typeof OrgMembershipsSchema>;
|
||||||
|
@ -18,7 +18,8 @@ export const ProjectsSchema = z.object({
|
|||||||
version: z.number().default(1),
|
version: z.number().default(1),
|
||||||
upgradeStatus: z.string().nullable().optional(),
|
upgradeStatus: z.string().nullable().optional(),
|
||||||
pitVersionLimit: z.number().default(10),
|
pitVersionLimit: z.number().default(10),
|
||||||
kmsCertificateKeyId: z.string().uuid().nullable().optional()
|
kmsCertificateKeyId: z.string().uuid().nullable().optional(),
|
||||||
|
auditLogsRetentionDays: z.number().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TProjects = z.infer<typeof ProjectsSchema>;
|
export type TProjects = z.infer<typeof ProjectsSchema>;
|
||||||
|
@ -9,10 +9,10 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const SecretApprovalPoliciesApproversSchema = z.object({
|
export const SecretApprovalPoliciesApproversSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
approverId: z.string().uuid(),
|
|
||||||
policyId: z.string().uuid(),
|
policyId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
approverUserId: z.string().uuid()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalPoliciesApprovers = z.infer<typeof SecretApprovalPoliciesApproversSchema>;
|
export type TSecretApprovalPoliciesApprovers = z.infer<typeof SecretApprovalPoliciesApproversSchema>;
|
||||||
|
@ -9,11 +9,11 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const SecretApprovalRequestsReviewersSchema = z.object({
|
export const SecretApprovalRequestsReviewersSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
member: z.string().uuid(),
|
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
requestId: z.string().uuid(),
|
requestId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
reviewerUserId: z.string().uuid()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalRequestsReviewers = z.infer<typeof SecretApprovalRequestsReviewersSchema>;
|
export type TSecretApprovalRequestsReviewers = z.infer<typeof SecretApprovalRequestsReviewersSchema>;
|
||||||
|
@ -15,11 +15,11 @@ export const SecretApprovalRequestsSchema = z.object({
|
|||||||
conflicts: z.unknown().nullable().optional(),
|
conflicts: z.unknown().nullable().optional(),
|
||||||
slug: z.string(),
|
slug: z.string(),
|
||||||
folderId: z.string().uuid(),
|
folderId: z.string().uuid(),
|
||||||
statusChangeBy: z.string().uuid().nullable().optional(),
|
|
||||||
committerId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
isReplicated: z.boolean().nullable().optional()
|
isReplicated: z.boolean().nullable().optional(),
|
||||||
|
committerUserId: z.string().uuid(),
|
||||||
|
statusChangedByUserId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;
|
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;
|
||||||
|
@ -18,7 +18,8 @@ export const SuperAdminSchema = z.object({
|
|||||||
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
||||||
trustLdapEmails: z.boolean().default(false).nullable().optional(),
|
trustLdapEmails: z.boolean().default(false).nullable().optional(),
|
||||||
trustOidcEmails: z.boolean().default(false).nullable().optional(),
|
trustOidcEmails: z.boolean().default(false).nullable().optional(),
|
||||||
defaultAuthOrgId: z.string().uuid().nullable().optional()
|
defaultAuthOrgId: z.string().uuid().nullable().optional(),
|
||||||
|
enabledLoginMethods: z.string().array().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||||
|
@ -21,7 +21,11 @@ export const WebhooksSchema = z.object({
|
|||||||
keyEncoding: z.string().nullable().optional(),
|
keyEncoding: z.string().nullable().optional(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
envId: z.string().uuid()
|
envId: z.string().uuid(),
|
||||||
|
urlCipherText: z.string().nullable().optional(),
|
||||||
|
urlIV: z.string().nullable().optional(),
|
||||||
|
urlTag: z.string().nullable().optional(),
|
||||||
|
type: z.string().default("general").nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TWebhooks = z.infer<typeof WebhooksSchema>;
|
export type TWebhooks = z.infer<typeof WebhooksSchema>;
|
||||||
|
@ -70,10 +70,13 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
groups = await searchGroups(ldapConfig, groupSearchFilter, ldapConfig.groupSearchBase);
|
groups = await searchGroups(ldapConfig, groupSearchFilter, ldapConfig.groupSearchBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const externalId = ldapConfig.uniqueUserAttribute ? user[ldapConfig.uniqueUserAttribute] : user.uidNumber;
|
||||||
|
const username = ldapConfig.uniqueUserAttribute ? externalId : user.uid;
|
||||||
|
|
||||||
const { isUserCompleted, providerAuthToken } = await server.services.ldap.ldapLogin({
|
const { isUserCompleted, providerAuthToken } = await server.services.ldap.ldapLogin({
|
||||||
|
externalId,
|
||||||
|
username,
|
||||||
ldapConfigId: ldapConfig.id,
|
ldapConfigId: ldapConfig.id,
|
||||||
externalId: user.uidNumber,
|
|
||||||
username: user.uid,
|
|
||||||
firstName: user.givenName ?? user.cn ?? "",
|
firstName: user.givenName ?? user.cn ?? "",
|
||||||
lastName: user.sn ?? "",
|
lastName: user.sn ?? "",
|
||||||
email: user.mail,
|
email: user.mail,
|
||||||
@ -138,6 +141,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
url: z.string(),
|
url: z.string(),
|
||||||
bindDN: z.string(),
|
bindDN: z.string(),
|
||||||
bindPass: z.string(),
|
bindPass: z.string(),
|
||||||
|
uniqueUserAttribute: z.string(),
|
||||||
searchBase: z.string(),
|
searchBase: z.string(),
|
||||||
searchFilter: z.string(),
|
searchFilter: z.string(),
|
||||||
groupSearchBase: z.string(),
|
groupSearchBase: z.string(),
|
||||||
@ -172,6 +176,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
url: z.string().trim(),
|
url: z.string().trim(),
|
||||||
bindDN: z.string().trim(),
|
bindDN: z.string().trim(),
|
||||||
bindPass: z.string().trim(),
|
bindPass: z.string().trim(),
|
||||||
|
uniqueUserAttribute: z.string().trim().default("uidNumber"),
|
||||||
searchBase: z.string().trim(),
|
searchBase: z.string().trim(),
|
||||||
searchFilter: z.string().trim().default("(uid={{username}})"),
|
searchFilter: z.string().trim().default("(uid={{username}})"),
|
||||||
groupSearchBase: z.string().trim(),
|
groupSearchBase: z.string().trim(),
|
||||||
@ -213,6 +218,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
url: z.string().trim(),
|
url: z.string().trim(),
|
||||||
bindDN: z.string().trim(),
|
bindDN: z.string().trim(),
|
||||||
bindPass: z.string().trim(),
|
bindPass: z.string().trim(),
|
||||||
|
uniqueUserAttribute: z.string().trim(),
|
||||||
searchBase: z.string().trim(),
|
searchBase: z.string().trim(),
|
||||||
searchFilter: z.string().trim(),
|
searchFilter: z.string().trim(),
|
||||||
groupSearchBase: z.string().trim(),
|
groupSearchBase: z.string().trim(),
|
||||||
|
@ -25,10 +25,10 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.optional()
|
.optional()
|
||||||
.nullable()
|
.nullable()
|
||||||
.transform((val) => (val ? removeTrailingSlash(val) : val)),
|
.transform((val) => (val ? removeTrailingSlash(val) : val)),
|
||||||
approvers: z.string().array().min(1),
|
approverUserIds: z.string().array().min(1),
|
||||||
approvals: z.number().min(1).default(1)
|
approvals: z.number().min(1).default(1)
|
||||||
})
|
})
|
||||||
.refine((data) => data.approvals <= data.approvers.length, {
|
.refine((data) => data.approvals <= data.approverUserIds.length, {
|
||||||
path: ["approvals"],
|
path: ["approvals"],
|
||||||
message: "The number of approvals should be lower than the number of approvers."
|
message: "The number of approvals should be lower than the number of approvers."
|
||||||
}),
|
}),
|
||||||
@ -66,7 +66,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
body: z
|
body: z
|
||||||
.object({
|
.object({
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
approvers: z.string().array().min(1),
|
approverUserIds: z.string().array().min(1),
|
||||||
approvals: z.number().min(1).default(1),
|
approvals: z.number().min(1).default(1),
|
||||||
secretPath: z
|
secretPath: z
|
||||||
.string()
|
.string()
|
||||||
@ -74,7 +74,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.nullable()
|
.nullable()
|
||||||
.transform((val) => (val ? removeTrailingSlash(val) : val))
|
.transform((val) => (val ? removeTrailingSlash(val) : val))
|
||||||
})
|
})
|
||||||
.refine((data) => data.approvals <= data.approvers.length, {
|
.refine((data) => data.approvals <= data.approverUserIds.length, {
|
||||||
path: ["approvals"],
|
path: ["approvals"],
|
||||||
message: "The number of approvals should be lower than the number of approvers."
|
message: "The number of approvals should be lower than the number of approvers."
|
||||||
}),
|
}),
|
||||||
@ -139,7 +139,15 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array()
|
approvals: sapPubSchema
|
||||||
|
.extend({
|
||||||
|
userApprovers: z
|
||||||
|
.object({
|
||||||
|
userId: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -170,7 +178,11 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional()
|
policy: sapPubSchema
|
||||||
|
.extend({
|
||||||
|
userApprovers: z.object({ userId: z.string() }).array()
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,8 @@ import {
|
|||||||
SecretApprovalRequestsSecretsSchema,
|
SecretApprovalRequestsSecretsSchema,
|
||||||
SecretsSchema,
|
SecretsSchema,
|
||||||
SecretTagsSchema,
|
SecretTagsSchema,
|
||||||
SecretVersionsSchema
|
SecretVersionsSchema,
|
||||||
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
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 { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||||
@ -14,6 +15,15 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
const approvalRequestUser = z.object({ userId: z.string() }).merge(
|
||||||
|
UsersSchema.pick({
|
||||||
|
email: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
username: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export const registerSecretApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@ -41,9 +51,10 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
approvers: z.string().array(),
|
approvers: z.string().array(),
|
||||||
secretPath: z.string().optional().nullable()
|
secretPath: z.string().optional().nullable()
|
||||||
}),
|
}),
|
||||||
|
committerUser: approvalRequestUser,
|
||||||
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
||||||
environment: z.string(),
|
environment: z.string(),
|
||||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
reviewers: z.object({ userId: z.string(), status: z.string() }).array(),
|
||||||
approvers: z.string().array()
|
approvers: z.string().array()
|
||||||
}).array()
|
}).array()
|
||||||
})
|
})
|
||||||
@ -195,7 +206,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
|
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
metadata: {
|
metadata: {
|
||||||
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeBy as string,
|
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangedByUserId as string,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
@ -216,6 +227,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional();
|
.optional();
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:id",
|
url: "/:id",
|
||||||
@ -235,12 +247,13 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
id: z.string(),
|
id: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
approvals: z.number(),
|
approvals: z.number(),
|
||||||
approvers: z.string().array(),
|
approvers: approvalRequestUser.array(),
|
||||||
secretPath: z.string().optional().nullable()
|
secretPath: z.string().optional().nullable()
|
||||||
}),
|
}),
|
||||||
environment: z.string(),
|
environment: z.string(),
|
||||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
statusChangedByUser: approvalRequestUser.optional(),
|
||||||
approvers: z.string().array(),
|
committerUser: approvalRequestUser,
|
||||||
|
reviewers: approvalRequestUser.extend({ status: z.string() }).array(),
|
||||||
secretPath: z.string(),
|
secretPath: z.string(),
|
||||||
commits: SecretApprovalRequestsSecretsSchema.omit({ secretBlindIndex: true })
|
commits: SecretApprovalRequestsSecretsSchema.omit({ secretBlindIndex: true })
|
||||||
.merge(
|
.merge(
|
||||||
|
@ -32,7 +32,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await accessApprovalPolicyFindQuery(tx || db, {
|
const doc = await accessApprovalPolicyFindQuery(tx || db.replicaNode(), {
|
||||||
[`${TableName.AccessApprovalPolicy}.id` as "id"]: id
|
[`${TableName.AccessApprovalPolicy}.id` as "id"]: id
|
||||||
});
|
});
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
const formatedDoc = mergeOneToManyRelation(
|
||||||
@ -54,7 +54,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const find = async (filter: TFindFilter<TAccessApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
const find = async (filter: TFindFilter<TAccessApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await accessApprovalPolicyFindQuery(tx || db, filter);
|
const docs = await accessApprovalPolicyFindQuery(tx || db.replicaNode(), filter);
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
const formatedDoc = mergeOneToManyRelation(
|
||||||
docs,
|
docs,
|
||||||
"id",
|
"id",
|
||||||
|
@ -14,7 +14,8 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findRequestsWithPrivilegeByPolicyIds = async (policyIds: string[]) => {
|
const findRequestsWithPrivilegeByPolicyIds = async (policyIds: string[]) => {
|
||||||
try {
|
try {
|
||||||
const docs = await db(TableName.AccessApprovalRequest)
|
const docs = await db
|
||||||
|
.replicaNode()(TableName.AccessApprovalRequest)
|
||||||
.whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds)
|
.whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
@ -170,7 +171,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const sql = findQuery({ [`${TableName.AccessApprovalRequest}.id` as "id"]: id }, tx || db);
|
const sql = findQuery({ [`${TableName.AccessApprovalRequest}.id` as "id"]: id }, tx || db.replicaNode());
|
||||||
const docs = await sql;
|
const docs = await sql;
|
||||||
const formatedDoc = sqlNestRelationships({
|
const formatedDoc = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
@ -207,7 +208,8 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const getCount = async ({ projectId }: { projectId: string }) => {
|
const getCount = async ({ projectId }: { projectId: string }) => {
|
||||||
try {
|
try {
|
||||||
const accessRequests = await db(TableName.AccessApprovalRequest)
|
const accessRequests = await db
|
||||||
|
.replicaNode()(TableName.AccessApprovalRequest)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalPolicy,
|
TableName.AccessApprovalPolicy,
|
||||||
`${TableName.AccessApprovalRequest}.policyId`,
|
`${TableName.AccessApprovalRequest}.policyId`,
|
||||||
|
@ -4,6 +4,7 @@ import { TDbClient } from "@app/db";
|
|||||||
import { TableName } from "@app/db/schemas";
|
import { TableName } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, stripUndefinedInWhere } from "@app/lib/knex";
|
import { ormify, stripUndefinedInWhere } from "@app/lib/knex";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
|
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const sqlQuery = (tx || db)(TableName.AuditLog)
|
const sqlQuery = (tx || db.replicaNode())(TableName.AuditLog)
|
||||||
.where(
|
.where(
|
||||||
stripUndefinedInWhere({
|
stripUndefinedInWhere({
|
||||||
projectId,
|
projectId,
|
||||||
@ -55,13 +56,34 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
// delete all audit log that have expired
|
// delete all audit log that have expired
|
||||||
const pruneAuditLog = async (tx?: Knex) => {
|
const pruneAuditLog = async (tx?: Knex) => {
|
||||||
try {
|
const AUDIT_LOG_PRUNE_BATCH_SIZE = 10000;
|
||||||
const today = new Date();
|
const MAX_RETRY_ON_FAILURE = 3;
|
||||||
const docs = await (tx || db)(TableName.AuditLog).where("expiresAt", "<", today).del();
|
|
||||||
return docs;
|
const today = new Date();
|
||||||
} catch (error) {
|
let deletedAuditLogIds: { id: string }[] = [];
|
||||||
throw new DatabaseError({ error, name: "PruneAuditLog" });
|
let numberOfRetryOnFailure = 0;
|
||||||
}
|
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
const findExpiredLogSubQuery = (tx || db)(TableName.AuditLog)
|
||||||
|
.where("expiresAt", "<", today)
|
||||||
|
.select("id")
|
||||||
|
.limit(AUDIT_LOG_PRUNE_BATCH_SIZE);
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
deletedAuditLogIds = await (tx || db)(TableName.AuditLog)
|
||||||
|
.whereIn("id", findExpiredLogSubQuery)
|
||||||
|
.del()
|
||||||
|
.returning("id");
|
||||||
|
numberOfRetryOnFailure = 0; // reset
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 100); // time to breathe for db
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
numberOfRetryOnFailure += 1;
|
||||||
|
logger.error(error, "Failed to delete audit log on pruning");
|
||||||
|
}
|
||||||
|
} while (deletedAuditLogIds.length > 0 && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...auditLogOrm, pruneAuditLog, find };
|
return { ...auditLogOrm, pruneAuditLog, find };
|
||||||
|
@ -45,18 +45,29 @@ export const auditLogQueueServiceFactory = ({
|
|||||||
const { actor, event, ipAddress, projectId, userAgent, userAgentType } = job.data;
|
const { actor, event, ipAddress, projectId, userAgent, userAgentType } = job.data;
|
||||||
let { orgId } = job.data;
|
let { orgId } = job.data;
|
||||||
const MS_IN_DAY = 24 * 60 * 60 * 1000;
|
const MS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||||
|
let project;
|
||||||
|
|
||||||
if (!orgId) {
|
if (!orgId) {
|
||||||
// it will never be undefined for both org and project id
|
// it will never be undefined for both org and project id
|
||||||
// TODO(akhilmhdh): use caching here in dal to avoid db calls
|
// TODO(akhilmhdh): use caching here in dal to avoid db calls
|
||||||
const project = await projectDAL.findById(projectId as string);
|
project = await projectDAL.findById(projectId as string);
|
||||||
orgId = project.orgId;
|
orgId = project.orgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
const ttl = plan.auditLogsRetentionDays * MS_IN_DAY;
|
if (plan.auditLogsRetentionDays === 0) {
|
||||||
// skip inserting if audit log retention is 0 meaning its not supported
|
// skip inserting if audit log retention is 0 meaning its not supported
|
||||||
if (ttl === 0) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For project actions, set TTL to project-level audit log retention config
|
||||||
|
// This condition ensures that the plan's audit log retention days cannot be bypassed
|
||||||
|
const ttlInDays =
|
||||||
|
project?.auditLogsRetentionDays && project.auditLogsRetentionDays < plan.auditLogsRetentionDays
|
||||||
|
? project.auditLogsRetentionDays
|
||||||
|
: plan.auditLogsRetentionDays;
|
||||||
|
|
||||||
|
const ttl = ttlInDays * MS_IN_DAY;
|
||||||
|
|
||||||
const auditLog = await auditLogDAL.create({
|
const auditLog = await auditLogDAL.create({
|
||||||
actor: actor.type,
|
actor: actor.type,
|
||||||
|
@ -65,25 +65,31 @@ export enum EventType {
|
|||||||
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
||||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
||||||
|
REVOKE_IDENTITY_UNIVERSAL_AUTH = "revoke-identity-universal-auth",
|
||||||
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
|
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
|
||||||
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
|
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
|
||||||
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
|
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
|
||||||
GET_IDENTITY_KUBERNETES_AUTH = "get-identity-kubernetes-auth",
|
GET_IDENTITY_KUBERNETES_AUTH = "get-identity-kubernetes-auth",
|
||||||
|
REVOKE_IDENTITY_KUBERNETES_AUTH = "revoke-identity-kubernetes-auth",
|
||||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||||
|
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET_BY_ID = "get-identity-universal-auth-client-secret-by-id",
|
||||||
LOGIN_IDENTITY_GCP_AUTH = "login-identity-gcp-auth",
|
LOGIN_IDENTITY_GCP_AUTH = "login-identity-gcp-auth",
|
||||||
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp-auth",
|
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp-auth",
|
||||||
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
|
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
|
||||||
|
REVOKE_IDENTITY_GCP_AUTH = "revoke-identity-gcp-auth",
|
||||||
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
|
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
|
||||||
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
|
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
|
||||||
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
|
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
|
||||||
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
|
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
|
||||||
|
REVOKE_IDENTITY_AWS_AUTH = "revoke-identity-aws-auth",
|
||||||
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
|
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
|
||||||
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
|
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
|
||||||
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
|
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
|
||||||
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
|
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
|
||||||
GET_IDENTITY_AZURE_AUTH = "get-identity-azure-auth",
|
GET_IDENTITY_AZURE_AUTH = "get-identity-azure-auth",
|
||||||
|
REVOKE_IDENTITY_AZURE_AUTH = "revoke-identity-azure-auth",
|
||||||
CREATE_ENVIRONMENT = "create-environment",
|
CREATE_ENVIRONMENT = "create-environment",
|
||||||
UPDATE_ENVIRONMENT = "update-environment",
|
UPDATE_ENVIRONMENT = "update-environment",
|
||||||
DELETE_ENVIRONMENT = "delete-environment",
|
DELETE_ENVIRONMENT = "delete-environment",
|
||||||
@ -434,6 +440,13 @@ interface GetIdentityUniversalAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeleteIdentityUniversalAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface LoginIdentityKubernetesAuthEvent {
|
interface LoginIdentityKubernetesAuthEvent {
|
||||||
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH;
|
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -457,6 +470,13 @@ interface AddIdentityKubernetesAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeleteIdentityKubernetesAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_KUBERNETES_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface UpdateIdentityKubernetesAuthEvent {
|
interface UpdateIdentityKubernetesAuthEvent {
|
||||||
type: EventType.UPDATE_IDENTITY_KUBENETES_AUTH;
|
type: EventType.UPDATE_IDENTITY_KUBENETES_AUTH;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -493,6 +513,14 @@ interface GetIdentityUniversalAuthClientSecretsEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetIdentityUniversalAuthClientSecretByIdEvent {
|
||||||
|
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET_BY_ID;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
clientSecretId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface RevokeIdentityUniversalAuthClientSecretEvent {
|
interface RevokeIdentityUniversalAuthClientSecretEvent {
|
||||||
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -525,6 +553,13 @@ interface AddIdentityGcpAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeleteIdentityGcpAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_GCP_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface UpdateIdentityGcpAuthEvent {
|
interface UpdateIdentityGcpAuthEvent {
|
||||||
type: EventType.UPDATE_IDENTITY_GCP_AUTH;
|
type: EventType.UPDATE_IDENTITY_GCP_AUTH;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -570,6 +605,13 @@ interface AddIdentityAwsAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeleteIdentityAwsAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_AWS_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface UpdateIdentityAwsAuthEvent {
|
interface UpdateIdentityAwsAuthEvent {
|
||||||
type: EventType.UPDATE_IDENTITY_AWS_AUTH;
|
type: EventType.UPDATE_IDENTITY_AWS_AUTH;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -613,6 +655,13 @@ interface AddIdentityAzureAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeleteIdentityAzureAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_AZURE_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface UpdateIdentityAzureAuthEvent {
|
interface UpdateIdentityAzureAuthEvent {
|
||||||
type: EventType.UPDATE_IDENTITY_AZURE_AUTH;
|
type: EventType.UPDATE_IDENTITY_AZURE_AUTH;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -722,7 +771,6 @@ interface CreateWebhookEvent {
|
|||||||
webhookId: string;
|
webhookId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
webhookUrl: string;
|
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -733,7 +781,6 @@ interface UpdateWebhookStatusEvent {
|
|||||||
webhookId: string;
|
webhookId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
webhookUrl: string;
|
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -744,7 +791,6 @@ interface DeleteWebhookEvent {
|
|||||||
webhookId: string;
|
webhookId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
webhookUrl: string;
|
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1003,24 +1049,30 @@ export type Event =
|
|||||||
| LoginIdentityUniversalAuthEvent
|
| LoginIdentityUniversalAuthEvent
|
||||||
| AddIdentityUniversalAuthEvent
|
| AddIdentityUniversalAuthEvent
|
||||||
| UpdateIdentityUniversalAuthEvent
|
| UpdateIdentityUniversalAuthEvent
|
||||||
|
| DeleteIdentityUniversalAuthEvent
|
||||||
| GetIdentityUniversalAuthEvent
|
| GetIdentityUniversalAuthEvent
|
||||||
| LoginIdentityKubernetesAuthEvent
|
| LoginIdentityKubernetesAuthEvent
|
||||||
|
| DeleteIdentityKubernetesAuthEvent
|
||||||
| AddIdentityKubernetesAuthEvent
|
| AddIdentityKubernetesAuthEvent
|
||||||
| UpdateIdentityKubernetesAuthEvent
|
| UpdateIdentityKubernetesAuthEvent
|
||||||
| GetIdentityKubernetesAuthEvent
|
| GetIdentityKubernetesAuthEvent
|
||||||
| CreateIdentityUniversalAuthClientSecretEvent
|
| CreateIdentityUniversalAuthClientSecretEvent
|
||||||
| GetIdentityUniversalAuthClientSecretsEvent
|
| GetIdentityUniversalAuthClientSecretsEvent
|
||||||
|
| GetIdentityUniversalAuthClientSecretByIdEvent
|
||||||
| RevokeIdentityUniversalAuthClientSecretEvent
|
| RevokeIdentityUniversalAuthClientSecretEvent
|
||||||
| LoginIdentityGcpAuthEvent
|
| LoginIdentityGcpAuthEvent
|
||||||
| AddIdentityGcpAuthEvent
|
| AddIdentityGcpAuthEvent
|
||||||
|
| DeleteIdentityGcpAuthEvent
|
||||||
| UpdateIdentityGcpAuthEvent
|
| UpdateIdentityGcpAuthEvent
|
||||||
| GetIdentityGcpAuthEvent
|
| GetIdentityGcpAuthEvent
|
||||||
| LoginIdentityAwsAuthEvent
|
| LoginIdentityAwsAuthEvent
|
||||||
| AddIdentityAwsAuthEvent
|
| AddIdentityAwsAuthEvent
|
||||||
| UpdateIdentityAwsAuthEvent
|
| UpdateIdentityAwsAuthEvent
|
||||||
| GetIdentityAwsAuthEvent
|
| GetIdentityAwsAuthEvent
|
||||||
|
| DeleteIdentityAwsAuthEvent
|
||||||
| LoginIdentityAzureAuthEvent
|
| LoginIdentityAzureAuthEvent
|
||||||
| AddIdentityAzureAuthEvent
|
| AddIdentityAzureAuthEvent
|
||||||
|
| DeleteIdentityAzureAuthEvent
|
||||||
| UpdateIdentityAzureAuthEvent
|
| UpdateIdentityAzureAuthEvent
|
||||||
| GetIdentityAzureAuthEvent
|
| GetIdentityAzureAuthEvent
|
||||||
| CreateEnvironmentEvent
|
| CreateEnvironmentEvent
|
||||||
|
@ -12,7 +12,10 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const countLeasesForDynamicSecret = async (dynamicSecretId: string, tx?: Knex) => {
|
const countLeasesForDynamicSecret = async (dynamicSecretId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.DynamicSecretLease).count("*").where({ dynamicSecretId }).first();
|
const doc = await (tx || db.replicaNode())(TableName.DynamicSecretLease)
|
||||||
|
.count("*")
|
||||||
|
.where({ dynamicSecretId })
|
||||||
|
.first();
|
||||||
return parseInt(doc || "0", 10);
|
return parseInt(doc || "0", 10);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "DynamicSecretCountLeases" });
|
throw new DatabaseError({ error, name: "DynamicSecretCountLeases" });
|
||||||
@ -21,7 +24,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.DynamicSecretLease)
|
const doc = await (tx || db.replicaNode())(TableName.DynamicSecretLease)
|
||||||
.where({ [`${TableName.DynamicSecretLease}.id` as "id"]: id })
|
.where({ [`${TableName.DynamicSecretLease}.id` as "id"]: id })
|
||||||
.first()
|
.first()
|
||||||
.join(
|
.join(
|
||||||
|
@ -3,7 +3,8 @@ import { z } from "zod";
|
|||||||
export enum SqlProviders {
|
export enum SqlProviders {
|
||||||
Postgres = "postgres",
|
Postgres = "postgres",
|
||||||
MySQL = "mysql2",
|
MySQL = "mysql2",
|
||||||
Oracle = "oracledb"
|
Oracle = "oracledb",
|
||||||
|
MsSQL = "mssql"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DynamicSecretSqlDBSchema = z.object({
|
export const DynamicSecretSqlDBSchema = z.object({
|
||||||
|
@ -12,7 +12,7 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findGroups = async (filter: TFindFilter<TGroups>, { offset, limit, sort, tx }: TFindOpt<TGroups> = {}) => {
|
const findGroups = async (filter: TFindFilter<TGroups>, { offset, limit, sort, tx }: TFindOpt<TGroups> = {}) => {
|
||||||
try {
|
try {
|
||||||
const query = (tx || db)(TableName.Groups)
|
const query = (tx || db.replicaNode())(TableName.Groups)
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
.where(buildFindFilter(filter))
|
.where(buildFindFilter(filter))
|
||||||
.select(selectAllTableCols(TableName.Groups));
|
.select(selectAllTableCols(TableName.Groups));
|
||||||
@ -32,7 +32,7 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findByOrgId = async (orgId: string, tx?: Knex) => {
|
const findByOrgId = async (orgId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)(TableName.Groups)
|
const docs = await (tx || db.replicaNode())(TableName.Groups)
|
||||||
.where(`${TableName.Groups}.orgId`, orgId)
|
.where(`${TableName.Groups}.orgId`, orgId)
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`)
|
.leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`)
|
||||||
.select(selectAllTableCols(TableName.Groups))
|
.select(selectAllTableCols(TableName.Groups))
|
||||||
@ -74,11 +74,12 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
username?: string;
|
username?: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
let query = db(TableName.OrgMembership)
|
let query = db
|
||||||
|
.replicaNode()(TableName.OrgMembership)
|
||||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.leftJoin(TableName.UserGroupMembership, function () {
|
.leftJoin(TableName.UserGroupMembership, (bd) => {
|
||||||
this.on(`${TableName.UserGroupMembership}.userId`, "=", `${TableName.Users}.id`).andOn(
|
bd.on(`${TableName.UserGroupMembership}.userId`, "=", `${TableName.Users}.id`).andOn(
|
||||||
`${TableName.UserGroupMembership}.groupId`,
|
`${TableName.UserGroupMembership}.groupId`,
|
||||||
"=",
|
"=",
|
||||||
db.raw("?", [groupId])
|
db.raw("?", [groupId])
|
||||||
|
@ -18,7 +18,7 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
*/
|
*/
|
||||||
const filterProjectsByUserMembership = async (userId: string, groupId: string, projectIds: string[], tx?: Knex) => {
|
const filterProjectsByUserMembership = async (userId: string, groupId: string, projectIds: string[], tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const userProjectMemberships: string[] = await (tx || db)(TableName.ProjectMembership)
|
const userProjectMemberships: string[] = await (tx || db.replicaNode())(TableName.ProjectMembership)
|
||||||
.where(`${TableName.ProjectMembership}.userId`, userId)
|
.where(`${TableName.ProjectMembership}.userId`, userId)
|
||||||
.whereIn(`${TableName.ProjectMembership}.projectId`, projectIds)
|
.whereIn(`${TableName.ProjectMembership}.projectId`, projectIds)
|
||||||
.pluck(`${TableName.ProjectMembership}.projectId`);
|
.pluck(`${TableName.ProjectMembership}.projectId`);
|
||||||
@ -43,7 +43,8 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
// special query
|
// special query
|
||||||
const findUserGroupMembershipsInProject = async (usernames: string[], projectId: string) => {
|
const findUserGroupMembershipsInProject = async (usernames: string[], projectId: string) => {
|
||||||
try {
|
try {
|
||||||
const usernameDocs: string[] = await db(TableName.UserGroupMembership)
|
const usernameDocs: string[] = await db
|
||||||
|
.replicaNode()(TableName.UserGroupMembership)
|
||||||
.join(
|
.join(
|
||||||
TableName.GroupProjectMembership,
|
TableName.GroupProjectMembership,
|
||||||
`${TableName.UserGroupMembership}.groupId`,
|
`${TableName.UserGroupMembership}.groupId`,
|
||||||
@ -73,7 +74,7 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
try {
|
try {
|
||||||
// get list of groups in the project with id [projectId]
|
// get list of groups in the project with id [projectId]
|
||||||
// that that are not the group with id [groupId]
|
// that that are not the group with id [groupId]
|
||||||
const groups: string[] = await (tx || db)(TableName.GroupProjectMembership)
|
const groups: string[] = await (tx || db.replicaNode())(TableName.GroupProjectMembership)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||||
.whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId)
|
.whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId)
|
||||||
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
||||||
@ -83,8 +84,8 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||||
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
||||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.leftJoin(TableName.ProjectMembership, function () {
|
.leftJoin(TableName.ProjectMembership, (bd) => {
|
||||||
this.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn(
|
bd.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn(
|
||||||
`${TableName.ProjectMembership}.projectId`,
|
`${TableName.ProjectMembership}.projectId`,
|
||||||
"=",
|
"=",
|
||||||
db.raw("?", [projectId])
|
db.raw("?", [projectId])
|
||||||
@ -107,9 +108,9 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
|
db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
|
||||||
)
|
)
|
||||||
.where({ isGhost: false }) // MAKE SURE USER IS NOT A GHOST USER
|
.where({ isGhost: false }) // MAKE SURE USER IS NOT A GHOST USER
|
||||||
.whereNotIn(`${TableName.UserGroupMembership}.userId`, function () {
|
.whereNotIn(`${TableName.UserGroupMembership}.userId`, (bd) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.select(`${TableName.UserGroupMembership}.userId`)
|
bd.select(`${TableName.UserGroupMembership}.userId`)
|
||||||
.from(TableName.UserGroupMembership)
|
.from(TableName.UserGroupMembership)
|
||||||
.whereIn(`${TableName.UserGroupMembership}.groupId`, groups);
|
.whereIn(`${TableName.UserGroupMembership}.groupId`, groups);
|
||||||
});
|
});
|
||||||
|
@ -34,6 +34,7 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal
|
|||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
@ -53,7 +54,7 @@ import {
|
|||||||
TTestLdapConnectionDTO,
|
TTestLdapConnectionDTO,
|
||||||
TUpdateLdapCfgDTO
|
TUpdateLdapCfgDTO
|
||||||
} from "./ldap-config-types";
|
} from "./ldap-config-types";
|
||||||
import { testLDAPConfig } from "./ldap-fns";
|
import { searchGroups, testLDAPConfig } from "./ldap-fns";
|
||||||
import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
|
import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
|
||||||
|
|
||||||
type TLdapConfigServiceFactoryDep = {
|
type TLdapConfigServiceFactoryDep = {
|
||||||
@ -122,6 +123,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
url,
|
url,
|
||||||
bindDN,
|
bindDN,
|
||||||
bindPass,
|
bindPass,
|
||||||
|
uniqueUserAttribute,
|
||||||
searchBase,
|
searchBase,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
groupSearchBase,
|
groupSearchBase,
|
||||||
@ -200,6 +202,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
encryptedBindPass,
|
encryptedBindPass,
|
||||||
bindPassIV,
|
bindPassIV,
|
||||||
bindPassTag,
|
bindPassTag,
|
||||||
|
uniqueUserAttribute,
|
||||||
searchBase,
|
searchBase,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
groupSearchBase,
|
groupSearchBase,
|
||||||
@ -222,6 +225,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
url,
|
url,
|
||||||
bindDN,
|
bindDN,
|
||||||
bindPass,
|
bindPass,
|
||||||
|
uniqueUserAttribute,
|
||||||
searchBase,
|
searchBase,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
groupSearchBase,
|
groupSearchBase,
|
||||||
@ -244,7 +248,8 @@ export const ldapConfigServiceFactory = ({
|
|||||||
searchBase,
|
searchBase,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
groupSearchBase,
|
groupSearchBase,
|
||||||
groupSearchFilter
|
groupSearchFilter,
|
||||||
|
uniqueUserAttribute
|
||||||
};
|
};
|
||||||
|
|
||||||
const orgBot = await orgBotDAL.findOne({ orgId });
|
const orgBot = await orgBotDAL.findOne({ orgId });
|
||||||
@ -282,7 +287,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
return ldapConfig;
|
return ldapConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLdapCfg = async (filter: { orgId: string; isActive?: boolean }) => {
|
const getLdapCfg = async (filter: { orgId: string; isActive?: boolean; id?: string }) => {
|
||||||
const ldapConfig = await ldapConfigDAL.findOne(filter);
|
const ldapConfig = await ldapConfigDAL.findOne(filter);
|
||||||
if (!ldapConfig) throw new BadRequestError({ message: "Failed to find organization LDAP data" });
|
if (!ldapConfig) throw new BadRequestError({ message: "Failed to find organization LDAP data" });
|
||||||
|
|
||||||
@ -345,6 +350,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
url: ldapConfig.url,
|
url: ldapConfig.url,
|
||||||
bindDN,
|
bindDN,
|
||||||
bindPass,
|
bindPass,
|
||||||
|
uniqueUserAttribute: ldapConfig.uniqueUserAttribute,
|
||||||
searchBase: ldapConfig.searchBase,
|
searchBase: ldapConfig.searchBase,
|
||||||
searchFilter: ldapConfig.searchFilter,
|
searchFilter: ldapConfig.searchFilter,
|
||||||
groupSearchBase: ldapConfig.groupSearchBase,
|
groupSearchBase: ldapConfig.groupSearchBase,
|
||||||
@ -381,6 +387,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
url: ldapConfig.url,
|
url: ldapConfig.url,
|
||||||
bindDN: ldapConfig.bindDN,
|
bindDN: ldapConfig.bindDN,
|
||||||
bindCredentials: ldapConfig.bindPass,
|
bindCredentials: ldapConfig.bindPass,
|
||||||
|
uniqueUserAttribute: ldapConfig.uniqueUserAttribute,
|
||||||
searchBase: ldapConfig.searchBase,
|
searchBase: ldapConfig.searchBase,
|
||||||
searchFilter: ldapConfig.searchFilter || "(uid={{username}})",
|
searchFilter: ldapConfig.searchFilter || "(uid={{username}})",
|
||||||
// searchAttributes: ["uid", "uidNumber", "givenName", "sn", "mail"],
|
// searchAttributes: ["uid", "uidNumber", "givenName", "sn", "mail"],
|
||||||
@ -411,6 +418,13 @@ export const ldapConfigServiceFactory = ({
|
|||||||
}: TLdapLoginDTO) => {
|
}: TLdapLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.LDAP)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with LDAP is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let userAlias = await userAliasDAL.findOne({
|
let userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
orgId,
|
orgId,
|
||||||
@ -450,9 +464,24 @@ export const ldapConfigServiceFactory = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
const plan = await licenseService.getPlan(orgId);
|
||||||
|
if (plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
|
||||||
|
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
|
||||||
|
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
userAlias = await userDAL.transaction(async (tx) => {
|
userAlias = await userDAL.transaction(async (tx) => {
|
||||||
let newUser: TUsers | undefined;
|
let newUser: TUsers | undefined;
|
||||||
if (serverCfg.trustSamlEmails) {
|
if (serverCfg.trustLdapEmails) {
|
||||||
newUser = await userDAL.findOne(
|
newUser = await userDAL.findOne(
|
||||||
{
|
{
|
||||||
email,
|
email,
|
||||||
@ -695,11 +724,25 @@ export const ldapConfigServiceFactory = ({
|
|||||||
message: "Failed to create LDAP group map due to plan restriction. Upgrade plan to create LDAP group map."
|
message: "Failed to create LDAP group map due to plan restriction. Upgrade plan to create LDAP group map."
|
||||||
});
|
});
|
||||||
|
|
||||||
const ldapConfig = await ldapConfigDAL.findOne({
|
const ldapConfig = await getLdapCfg({
|
||||||
id: ldapConfigId,
|
orgId,
|
||||||
orgId
|
id: ldapConfigId
|
||||||
});
|
});
|
||||||
if (!ldapConfig) throw new BadRequestError({ message: "Failed to find organization LDAP data" });
|
|
||||||
|
if (!ldapConfig.groupSearchBase) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Configure a group search base in your LDAP configuration in order to proceed."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupSearchFilter = `(cn=${ldapGroupCN})`;
|
||||||
|
const groups = await searchGroups(ldapConfig, groupSearchFilter, ldapConfig.groupSearchBase);
|
||||||
|
|
||||||
|
if (!groups.some((g) => g.cn === ldapGroupCN)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to find LDAP Group CN"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const group = await groupDAL.findOne({ slug: groupSlug, orgId });
|
const group = await groupDAL.findOne({ slug: groupSlug, orgId });
|
||||||
if (!group) throw new BadRequestError({ message: "Failed to find group" });
|
if (!group) throw new BadRequestError({ message: "Failed to find group" });
|
||||||
|
@ -7,6 +7,7 @@ export type TLDAPConfig = {
|
|||||||
url: string;
|
url: string;
|
||||||
bindDN: string;
|
bindDN: string;
|
||||||
bindPass: string;
|
bindPass: string;
|
||||||
|
uniqueUserAttribute: string;
|
||||||
searchBase: string;
|
searchBase: string;
|
||||||
groupSearchBase: string;
|
groupSearchBase: string;
|
||||||
groupSearchFilter: string;
|
groupSearchFilter: string;
|
||||||
@ -19,6 +20,7 @@ export type TCreateLdapCfgDTO = {
|
|||||||
url: string;
|
url: string;
|
||||||
bindDN: string;
|
bindDN: string;
|
||||||
bindPass: string;
|
bindPass: string;
|
||||||
|
uniqueUserAttribute: string;
|
||||||
searchBase: string;
|
searchBase: string;
|
||||||
searchFilter: string;
|
searchFilter: string;
|
||||||
groupSearchBase: string;
|
groupSearchBase: string;
|
||||||
@ -33,6 +35,7 @@ export type TUpdateLdapCfgDTO = {
|
|||||||
url: string;
|
url: string;
|
||||||
bindDN: string;
|
bindDN: string;
|
||||||
bindPass: string;
|
bindPass: string;
|
||||||
|
uniqueUserAttribute: string;
|
||||||
searchBase: string;
|
searchBase: string;
|
||||||
searchFilter: string;
|
searchFilter: string;
|
||||||
groupSearchBase: string;
|
groupSearchBase: string;
|
||||||
|
@ -10,7 +10,8 @@ export const ldapGroupMapDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findLdapGroupMapsByLdapConfigId = async (ldapConfigId: string) => {
|
const findLdapGroupMapsByLdapConfigId = async (ldapConfigId: string) => {
|
||||||
try {
|
try {
|
||||||
const docs = await db(TableName.LdapGroupMap)
|
const docs = await db
|
||||||
|
.replicaNode()(TableName.LdapGroupMap)
|
||||||
.where(`${TableName.LdapGroupMap}.ldapConfigId`, ldapConfigId)
|
.where(`${TableName.LdapGroupMap}.ldapConfigId`, ldapConfigId)
|
||||||
.join(TableName.Groups, `${TableName.LdapGroupMap}.groupId`, `${TableName.Groups}.id`)
|
.join(TableName.Groups, `${TableName.LdapGroupMap}.groupId`, `${TableName.Groups}.id`)
|
||||||
.select(selectAllTableCols(TableName.LdapGroupMap))
|
.select(selectAllTableCols(TableName.LdapGroupMap))
|
||||||
|
@ -7,6 +7,8 @@ export const getDefaultOnPremFeatures = () => {
|
|||||||
workspacesUsed: 0,
|
workspacesUsed: 0,
|
||||||
memberLimit: null,
|
memberLimit: null,
|
||||||
membersUsed: 0,
|
membersUsed: 0,
|
||||||
|
identityLimit: null,
|
||||||
|
identitiesUsed: 0,
|
||||||
environmentLimit: null,
|
environmentLimit: null,
|
||||||
environmentsUsed: 0,
|
environmentsUsed: 0,
|
||||||
secretVersioning: true,
|
secretVersioning: true,
|
||||||
|
@ -15,6 +15,8 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
membersUsed: 0,
|
membersUsed: 0,
|
||||||
environmentLimit: null,
|
environmentLimit: null,
|
||||||
environmentsUsed: 0,
|
environmentsUsed: 0,
|
||||||
|
identityLimit: null,
|
||||||
|
identitiesUsed: 0,
|
||||||
dynamicSecret: false,
|
dynamicSecret: false,
|
||||||
secretVersioning: true,
|
secretVersioning: true,
|
||||||
pitRecovery: false,
|
pitRecovery: false,
|
||||||
|
@ -9,7 +9,7 @@ export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
|
|||||||
export const licenseDALFactory = (db: TDbClient) => {
|
export const licenseDALFactory = (db: TDbClient) => {
|
||||||
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.OrgMembership)
|
const doc = await (tx || db.replicaNode())(TableName.OrgMembership)
|
||||||
.where({ status: OrgMembershipStatus.Accepted })
|
.where({ status: OrgMembershipStatus.Accepted })
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
@ -19,11 +19,44 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
.count();
|
.count();
|
||||||
return doc?.[0].count;
|
return Number(doc?.[0].count);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Count of Org Members" });
|
throw new DatabaseError({ error, name: "Count of Org Members" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { countOfOrgMembers };
|
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
// count org users
|
||||||
|
const userDoc = await (tx || db)(TableName.OrgMembership)
|
||||||
|
.where({ status: OrgMembershipStatus.Accepted })
|
||||||
|
.andWhere((bd) => {
|
||||||
|
if (orgId) {
|
||||||
|
void bd.where({ orgId });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||||
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
const userCount = Number(userDoc?.[0].count);
|
||||||
|
|
||||||
|
// count org identities
|
||||||
|
const identityDoc = await (tx || db)(TableName.IdentityOrgMembership)
|
||||||
|
.where((bd) => {
|
||||||
|
if (orgId) {
|
||||||
|
void bd.where({ orgId });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
|
||||||
|
const identityCount = Number(identityDoc?.[0].count);
|
||||||
|
|
||||||
|
return userCount + identityCount;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Count of Org Users + Identities" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { countOfOrgMembers, countOrgUsersAndIdentities };
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
// TODO(akhilmhdh): With tony find out the api structure and fill it here
|
// TODO(akhilmhdh): With tony find out the api structure and fill it here
|
||||||
|
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
@ -155,6 +156,7 @@ export const licenseServiceFactory = ({
|
|||||||
LICENSE_SERVER_CLOUD_PLAN_TTL,
|
LICENSE_SERVER_CLOUD_PLAN_TTL,
|
||||||
JSON.stringify(currentPlan)
|
JSON.stringify(currentPlan)
|
||||||
);
|
);
|
||||||
|
|
||||||
return currentPlan;
|
return currentPlan;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -199,21 +201,27 @@ export const licenseServiceFactory = ({
|
|||||||
await licenseServerCloudApi.request.delete(`/api/license-server/v1/customers/${customerId}`);
|
await licenseServerCloudApi.request.delete(`/api/license-server/v1/customers/${customerId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSubscriptionOrgMemberCount = async (orgId: string) => {
|
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
|
||||||
if (instanceType === InstanceType.Cloud) {
|
if (instanceType === InstanceType.Cloud) {
|
||||||
const org = await orgDAL.findOrgById(orgId);
|
const org = await orgDAL.findOrgById(orgId);
|
||||||
if (!org) throw new BadRequestError({ message: "Org not found" });
|
if (!org) throw new BadRequestError({ message: "Org not found" });
|
||||||
|
|
||||||
const count = await licenseDAL.countOfOrgMembers(orgId);
|
const quantity = await licenseDAL.countOfOrgMembers(orgId, tx);
|
||||||
|
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(orgId, tx);
|
||||||
if (org?.customerId) {
|
if (org?.customerId) {
|
||||||
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
|
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
|
||||||
quantity: count
|
quantity,
|
||||||
|
quantityIdentities
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId));
|
await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId));
|
||||||
} else if (instanceType === InstanceType.EnterpriseOnPrem) {
|
} else if (instanceType === InstanceType.EnterpriseOnPrem) {
|
||||||
const usedSeats = await licenseDAL.countOfOrgMembers(null);
|
const usedSeats = await licenseDAL.countOfOrgMembers(null, tx);
|
||||||
await licenseServerOnPremApi.request.patch(`/api/license/v1/license`, { usedSeats });
|
const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null, tx);
|
||||||
|
await licenseServerOnPremApi.request.patch(`/api/license/v1/license`, {
|
||||||
|
usedSeats,
|
||||||
|
usedIdentitySeats
|
||||||
|
});
|
||||||
}
|
}
|
||||||
await refreshPlan(orgId);
|
await refreshPlan(orgId);
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,8 @@ export type TFeatureSet = {
|
|||||||
dynamicSecret: false;
|
dynamicSecret: false;
|
||||||
memberLimit: null;
|
memberLimit: null;
|
||||||
membersUsed: 0;
|
membersUsed: 0;
|
||||||
|
identityLimit: null;
|
||||||
|
identitiesUsed: 0;
|
||||||
environmentLimit: null;
|
environmentLimit: null;
|
||||||
environmentsUsed: 0;
|
environmentsUsed: 0;
|
||||||
secretVersioning: true;
|
secretVersioning: true;
|
||||||
|
@ -26,6 +26,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
|
|||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
@ -157,6 +158,13 @@ export const oidcConfigServiceFactory = ({
|
|||||||
|
|
||||||
const oidcLogin = async ({ externalId, email, firstName, lastName, orgId, callbackPort }: TOidcLoginDTO) => {
|
const oidcLogin = async ({ externalId, email, firstName, lastName, orgId, callbackPort }: TOidcLoginDTO) => {
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.OIDC)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with OIDC is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const userAlias = await userAliasDAL.findOne({
|
const userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
|
@ -10,7 +10,8 @@ export type TPermissionDALFactory = ReturnType<typeof permissionDALFactory>;
|
|||||||
export const permissionDALFactory = (db: TDbClient) => {
|
export const permissionDALFactory = (db: TDbClient) => {
|
||||||
const getOrgPermission = async (userId: string, orgId: string) => {
|
const getOrgPermission = async (userId: string, orgId: string) => {
|
||||||
try {
|
try {
|
||||||
const membership = await db(TableName.OrgMembership)
|
const membership = await db
|
||||||
|
.replicaNode()(TableName.OrgMembership)
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||||
.join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`)
|
.join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`)
|
||||||
.where("userId", userId)
|
.where("userId", userId)
|
||||||
@ -28,7 +29,8 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const getOrgIdentityPermission = async (identityId: string, orgId: string) => {
|
const getOrgIdentityPermission = async (identityId: string, orgId: string) => {
|
||||||
try {
|
try {
|
||||||
const membership = await db(TableName.IdentityOrgMembership)
|
const membership = await db
|
||||||
|
.replicaNode()(TableName.IdentityOrgMembership)
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||||
.join(TableName.Organization, `${TableName.IdentityOrgMembership}.orgId`, `${TableName.Organization}.id`)
|
.join(TableName.Organization, `${TableName.IdentityOrgMembership}.orgId`, `${TableName.Organization}.id`)
|
||||||
.where("identityId", identityId)
|
.where("identityId", identityId)
|
||||||
@ -45,11 +47,13 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const getProjectPermission = async (userId: string, projectId: string) => {
|
const getProjectPermission = async (userId: string, projectId: string) => {
|
||||||
try {
|
try {
|
||||||
const groups: string[] = await db(TableName.GroupProjectMembership)
|
const groups: string[] = await db
|
||||||
|
.replicaNode()(TableName.GroupProjectMembership)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||||
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
||||||
|
|
||||||
const groupDocs = await db(TableName.UserGroupMembership)
|
const groupDocs = await db
|
||||||
|
.replicaNode()(TableName.UserGroupMembership)
|
||||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
||||||
.whereIn(`${TableName.UserGroupMembership}.groupId`, groups)
|
.whereIn(`${TableName.UserGroupMembership}.groupId`, groups)
|
||||||
.join(
|
.join(
|
||||||
@ -231,7 +235,8 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const getProjectIdentityPermission = async (identityId: string, projectId: string) => {
|
const getProjectIdentityPermission = async (identityId: string, projectId: string) => {
|
||||||
try {
|
try {
|
||||||
const docs = await db(TableName.IdentityProjectMembership)
|
const docs = await db
|
||||||
|
.replicaNode()(TableName.IdentityProjectMembership)
|
||||||
.join(
|
.join(
|
||||||
TableName.IdentityProjectMembershipRole,
|
TableName.IdentityProjectMembershipRole,
|
||||||
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
||||||
|
@ -10,7 +10,8 @@ export const samlConfigDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findEnforceableSamlCfg = async (orgId: string) => {
|
const findEnforceableSamlCfg = async (orgId: string) => {
|
||||||
try {
|
try {
|
||||||
const samlCfg = await db(TableName.SamlConfig)
|
const samlCfg = await db
|
||||||
|
.replicaNode()(TableName.SamlConfig)
|
||||||
.where({
|
.where({
|
||||||
orgId,
|
orgId,
|
||||||
isActive: true
|
isActive: true
|
||||||
|
@ -28,6 +28,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
|
|||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
@ -335,6 +336,13 @@ export const samlConfigServiceFactory = ({
|
|||||||
}: TSamlLoginDTO) => {
|
}: TSamlLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.SAML)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with SAML is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const userAlias = await userAliasDAL.findOne({
|
const userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
orgId,
|
orgId,
|
||||||
@ -380,6 +388,21 @@ export const samlConfigServiceFactory = ({
|
|||||||
return foundUser;
|
return foundUser;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
const plan = await licenseService.getPlan(orgId);
|
||||||
|
if (plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
|
||||||
|
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
|
||||||
|
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
user = await userDAL.transaction(async (tx) => {
|
user = await userDAL.transaction(async (tx) => {
|
||||||
let newUser: TUsers | undefined;
|
let newUser: TUsers | undefined;
|
||||||
if (serverCfg.trustSamlEmails) {
|
if (serverCfg.trustSamlEmails) {
|
||||||
|
@ -1,49 +1,59 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TSecretApprovalPolicies } from "@app/db/schemas";
|
import { SecretApprovalPoliciesSchema, TableName, TSecretApprovalPolicies } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex";
|
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
|
||||||
|
|
||||||
export type TSecretApprovalPolicyDALFactory = ReturnType<typeof secretApprovalPolicyDALFactory>;
|
export type TSecretApprovalPolicyDALFactory = ReturnType<typeof secretApprovalPolicyDALFactory>;
|
||||||
|
|
||||||
export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||||
const secretApprovalPolicyOrm = ormify(db, TableName.SecretApprovalPolicy);
|
const secretApprovalPolicyOrm = ormify(db, TableName.SecretApprovalPolicy);
|
||||||
|
|
||||||
const sapFindQuery = (tx: Knex, filter: TFindFilter<TSecretApprovalPolicies>) =>
|
const secretApprovalPolicyFindQuery = (tx: Knex, filter: TFindFilter<TSecretApprovalPolicies>) =>
|
||||||
tx(TableName.SecretApprovalPolicy)
|
tx(TableName.SecretApprovalPolicy)
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
.where(buildFindFilter(filter))
|
.where(buildFindFilter(filter))
|
||||||
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
.join(
|
.leftJoin(
|
||||||
TableName.SecretApprovalPolicyApprover,
|
TableName.SecretApprovalPolicyApprover,
|
||||||
`${TableName.SecretApprovalPolicy}.id`,
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
.select(tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover))
|
.select(tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover))
|
||||||
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
.select(
|
||||||
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
tx.ref("name").withSchema(TableName.Environment).as("envName"),
|
||||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
tx.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||||
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
tx.ref("id").withSchema(TableName.Environment).as("envId"),
|
||||||
|
tx.ref("projectId").withSchema(TableName.Environment)
|
||||||
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalPolicy))
|
.select(selectAllTableCols(TableName.SecretApprovalPolicy))
|
||||||
.orderBy("createdAt", "asc");
|
.orderBy("createdAt", "asc");
|
||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await sapFindQuery(tx || db, {
|
const doc = await secretApprovalPolicyFindQuery(tx || db.replicaNode(), {
|
||||||
[`${TableName.SecretApprovalPolicy}.id` as "id"]: id
|
[`${TableName.SecretApprovalPolicy}.id` as "id"]: id
|
||||||
});
|
});
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
const formatedDoc = sqlNestRelationships({
|
||||||
doc,
|
data: doc,
|
||||||
"id",
|
key: "id",
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
parentMapper: (data) => ({
|
||||||
...el,
|
environment: { id: data.envId, name: data.envName, slug: data.envSlug },
|
||||||
envId,
|
projectId: data.projectId,
|
||||||
environment: { id: envId, name, slug }
|
...SecretApprovalPoliciesSchema.parse(data)
|
||||||
}),
|
}),
|
||||||
({ approverId }) => approverId,
|
childrenMapper: [
|
||||||
"approvers"
|
{
|
||||||
);
|
key: "approverUserId",
|
||||||
|
label: "userApprovers" as const,
|
||||||
|
mapper: ({ approverUserId }) => ({
|
||||||
|
userId: approverUserId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
return formatedDoc?.[0];
|
return formatedDoc?.[0];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "FindById" });
|
throw new DatabaseError({ error, name: "FindById" });
|
||||||
@ -52,18 +62,25 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const find = async (filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
const find = async (filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await sapFindQuery(tx || db, filter);
|
const docs = await secretApprovalPolicyFindQuery(tx || db.replicaNode(), filter);
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
const formatedDoc = sqlNestRelationships({
|
||||||
docs,
|
data: docs,
|
||||||
"id",
|
key: "id",
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
parentMapper: (data) => ({
|
||||||
...el,
|
environment: { id: data.envId, name: data.envName, slug: data.envSlug },
|
||||||
envId,
|
projectId: data.projectId,
|
||||||
environment: { id: envId, name, slug }
|
...SecretApprovalPoliciesSchema.parse(data)
|
||||||
}),
|
}),
|
||||||
({ approverId }) => approverId,
|
childrenMapper: [
|
||||||
"approvers"
|
{
|
||||||
);
|
key: "approverUserId",
|
||||||
|
label: "userApprovers" as const,
|
||||||
|
mapper: ({ approverUserId }) => ({
|
||||||
|
userId: approverUserId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
return formatedDoc;
|
return formatedDoc;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Find" });
|
throw new DatabaseError({ error, name: "Find" });
|
||||||
|
@ -7,7 +7,6 @@ import { BadRequestError } from "@app/lib/errors";
|
|||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { containsGlobPatterns } from "@app/lib/picomatch";
|
import { containsGlobPatterns } from "@app/lib/picomatch";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
|
||||||
|
|
||||||
import { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
|
import { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
|
||||||
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
||||||
@ -29,7 +28,6 @@ type TSecretApprovalPolicyServiceFactoryDep = {
|
|||||||
secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory;
|
secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory;
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||||
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
||||||
@ -38,8 +36,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
secretApprovalPolicyDAL,
|
secretApprovalPolicyDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
secretApprovalPolicyApproverDAL,
|
secretApprovalPolicyApproverDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL
|
||||||
projectMembershipDAL
|
|
||||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||||
const createSecretApprovalPolicy = async ({
|
const createSecretApprovalPolicy = async ({
|
||||||
name,
|
name,
|
||||||
@ -48,12 +45,12 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
approvals,
|
approvals,
|
||||||
approvers,
|
approverUserIds,
|
||||||
projectId,
|
projectId,
|
||||||
secretPath,
|
secretPath,
|
||||||
environment
|
environment
|
||||||
}: TCreateSapDTO) => {
|
}: TCreateSapDTO) => {
|
||||||
if (approvals > approvers.length)
|
if (approvals > approverUserIds.length)
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
@ -70,13 +67,6 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
|
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
|
||||||
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
||||||
|
|
||||||
const secretApprovers = await projectMembershipDAL.find({
|
|
||||||
projectId,
|
|
||||||
$in: { id: approvers }
|
|
||||||
});
|
|
||||||
if (secretApprovers.length !== approvers.length)
|
|
||||||
throw new BadRequestError({ message: "Approver not found in project" });
|
|
||||||
|
|
||||||
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
const doc = await secretApprovalPolicyDAL.create(
|
const doc = await secretApprovalPolicyDAL.create(
|
||||||
{
|
{
|
||||||
@ -88,8 +78,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await secretApprovalPolicyApproverDAL.insertMany(
|
await secretApprovalPolicyApproverDAL.insertMany(
|
||||||
secretApprovers.map(({ id }) => ({
|
approverUserIds.map((approverUserId) => ({
|
||||||
approverId: id,
|
approverUserId,
|
||||||
policyId: doc.id
|
policyId: doc.id
|
||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
@ -100,7 +90,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateSecretApprovalPolicy = async ({
|
const updateSecretApprovalPolicy = async ({
|
||||||
approvers,
|
approverUserIds,
|
||||||
secretPath,
|
secretPath,
|
||||||
name,
|
name,
|
||||||
actorId,
|
actorId,
|
||||||
@ -132,22 +122,11 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
if (approvers) {
|
if (approverUserIds) {
|
||||||
const secretApprovers = await projectMembershipDAL.find(
|
|
||||||
{
|
|
||||||
projectId: secretApprovalPolicy.projectId,
|
|
||||||
$in: { id: approvers }
|
|
||||||
},
|
|
||||||
{ tx }
|
|
||||||
);
|
|
||||||
if (secretApprovers.length !== approvers.length)
|
|
||||||
throw new BadRequestError({ message: "Approver not found in project" });
|
|
||||||
if (doc.approvals > secretApprovers.length)
|
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
|
||||||
await secretApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
await secretApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
||||||
await secretApprovalPolicyApproverDAL.insertMany(
|
await secretApprovalPolicyApproverDAL.insertMany(
|
||||||
secretApprovers.map(({ id }) => ({
|
approverUserIds.map((approverUserId) => ({
|
||||||
approverId: id,
|
approverUserId,
|
||||||
policyId: doc.id
|
policyId: doc.id
|
||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
|
@ -4,7 +4,7 @@ export type TCreateSapDTO = {
|
|||||||
approvals: number;
|
approvals: number;
|
||||||
secretPath?: string | null;
|
secretPath?: string | null;
|
||||||
environment: string;
|
environment: string;
|
||||||
approvers: string[];
|
approverUserIds: string[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
name: string;
|
name: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
@ -13,7 +13,7 @@ export type TUpdateSapDTO = {
|
|||||||
secretPolicyId: string;
|
secretPolicyId: string;
|
||||||
approvals?: number;
|
approvals?: number;
|
||||||
secretPath?: string | null;
|
secretPath?: string | null;
|
||||||
approvers: string[];
|
approverUserIds: string[];
|
||||||
name?: string;
|
name?: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ import {
|
|||||||
SecretApprovalRequestsSchema,
|
SecretApprovalRequestsSchema,
|
||||||
TableName,
|
TableName,
|
||||||
TSecretApprovalRequests,
|
TSecretApprovalRequests,
|
||||||
TSecretApprovalRequestsSecrets
|
TSecretApprovalRequestsSecrets,
|
||||||
|
TUsers
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols, sqlNestRelationships, stripUndefinedInWhere, TFindFilter } from "@app/lib/knex";
|
import { ormify, selectAllTableCols, sqlNestRelationships, stripUndefinedInWhere, TFindFilter } from "@app/lib/knex";
|
||||||
@ -16,7 +17,7 @@ export type TSecretApprovalRequestDALFactory = ReturnType<typeof secretApprovalR
|
|||||||
|
|
||||||
type TFindQueryFilter = {
|
type TFindQueryFilter = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
membershipId: string;
|
userId: string;
|
||||||
status?: RequestState;
|
status?: RequestState;
|
||||||
environment?: string;
|
environment?: string;
|
||||||
committer?: string;
|
committer?: string;
|
||||||
@ -37,32 +38,68 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretApprovalRequest}.policyId`,
|
`${TableName.SecretApprovalRequest}.policyId`,
|
||||||
`${TableName.SecretApprovalPolicy}.id`
|
`${TableName.SecretApprovalPolicy}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin<TUsers>(
|
||||||
|
db(TableName.Users).as("statusChangedByUser"),
|
||||||
|
`${TableName.SecretApprovalRequest}.statusChangedByUserId`,
|
||||||
|
`statusChangedByUser.id`
|
||||||
|
)
|
||||||
|
.join<TUsers>(
|
||||||
|
db(TableName.Users).as("committerUser"),
|
||||||
|
`${TableName.SecretApprovalRequest}.committerUserId`,
|
||||||
|
`committerUser.id`
|
||||||
|
)
|
||||||
.join(
|
.join(
|
||||||
TableName.SecretApprovalPolicyApprover,
|
TableName.SecretApprovalPolicyApprover,
|
||||||
`${TableName.SecretApprovalPolicy}.id`,
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
|
.join<TUsers>(
|
||||||
|
db(TableName.Users).as("secretApprovalPolicyApproverUser"),
|
||||||
|
`${TableName.SecretApprovalPolicyApprover}.approverUserId`,
|
||||||
|
"secretApprovalPolicyApproverUser.id"
|
||||||
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretApprovalRequestReviewer,
|
TableName.SecretApprovalRequestReviewer,
|
||||||
`${TableName.SecretApprovalRequest}.id`,
|
`${TableName.SecretApprovalRequest}.id`,
|
||||||
`${TableName.SecretApprovalRequestReviewer}.requestId`
|
`${TableName.SecretApprovalRequestReviewer}.requestId`
|
||||||
)
|
)
|
||||||
|
.leftJoin<TUsers>(
|
||||||
|
db(TableName.Users).as("secretApprovalReviewerUser"),
|
||||||
|
`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`,
|
||||||
|
`secretApprovalReviewerUser.id`
|
||||||
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||||
.select(
|
.select(
|
||||||
tx.ref("member").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
|
||||||
|
tx.ref("email").withSchema("secretApprovalPolicyApproverUser").as("approverEmail"),
|
||||||
|
tx.ref("username").withSchema("secretApprovalPolicyApproverUser").as("approverUsername"),
|
||||||
|
tx.ref("firstName").withSchema("secretApprovalPolicyApproverUser").as("approverFirstName"),
|
||||||
|
tx.ref("lastName").withSchema("secretApprovalPolicyApproverUser").as("approverLastName"),
|
||||||
|
tx.ref("email").withSchema("statusChangedByUser").as("statusChangedByUserEmail"),
|
||||||
|
tx.ref("username").withSchema("statusChangedByUser").as("statusChangedByUserUsername"),
|
||||||
|
tx.ref("firstName").withSchema("statusChangedByUser").as("statusChangedByUserFirstName"),
|
||||||
|
tx.ref("lastName").withSchema("statusChangedByUser").as("statusChangedByUserLastName"),
|
||||||
|
tx.ref("email").withSchema("committerUser").as("committerUserEmail"),
|
||||||
|
tx.ref("username").withSchema("committerUser").as("committerUserUsername"),
|
||||||
|
tx.ref("firstName").withSchema("committerUser").as("committerUserFirstName"),
|
||||||
|
tx.ref("lastName").withSchema("committerUser").as("committerUserLastName"),
|
||||||
|
tx.ref("reviewerUserId").withSchema(TableName.SecretApprovalRequestReviewer),
|
||||||
tx.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
tx.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
||||||
|
tx.ref("email").withSchema("secretApprovalReviewerUser").as("reviewerEmail"),
|
||||||
|
tx.ref("username").withSchema("secretApprovalReviewerUser").as("reviewerUsername"),
|
||||||
|
tx.ref("firstName").withSchema("secretApprovalReviewerUser").as("reviewerFirstName"),
|
||||||
|
tx.ref("lastName").withSchema("secretApprovalReviewerUser").as("reviewerLastName"),
|
||||||
tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||||
tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
||||||
tx.ref("projectId").withSchema(TableName.Environment),
|
tx.ref("projectId").withSchema(TableName.Environment),
|
||||||
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||||
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||||
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals")
|
||||||
tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const sql = findQuery({ [`${TableName.SecretApprovalRequest}.id` as "id"]: id }, tx || db);
|
const sql = findQuery({ [`${TableName.SecretApprovalRequest}.id` as "id"]: id }, tx || db.replicaNode());
|
||||||
const docs = await sql;
|
const docs = await sql;
|
||||||
const formatedDoc = sqlNestRelationships({
|
const formatedDoc = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
@ -71,6 +108,22 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
...SecretApprovalRequestsSchema.parse(el),
|
...SecretApprovalRequestsSchema.parse(el),
|
||||||
projectId: el.projectId,
|
projectId: el.projectId,
|
||||||
environment: el.environment,
|
environment: el.environment,
|
||||||
|
statusChangedByUser: el.statusChangedByUserId
|
||||||
|
? {
|
||||||
|
userId: el.statusChangedByUserId,
|
||||||
|
email: el.statusChangedByUserEmail,
|
||||||
|
firstName: el.statusChangedByUserFirstName,
|
||||||
|
lastName: el.statusChangedByUserLastName,
|
||||||
|
username: el.statusChangedByUserUsername
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
committerUser: {
|
||||||
|
userId: el.committerUserId,
|
||||||
|
email: el.committerUserEmail,
|
||||||
|
firstName: el.committerUserFirstName,
|
||||||
|
lastName: el.committerUserLastName,
|
||||||
|
username: el.committerUserUsername
|
||||||
|
},
|
||||||
policy: {
|
policy: {
|
||||||
id: el.policyId,
|
id: el.policyId,
|
||||||
name: el.policyName,
|
name: el.policyName,
|
||||||
@ -80,11 +133,34 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
key: "reviewerMemberId",
|
key: "reviewerUserId",
|
||||||
label: "reviewers" as const,
|
label: "reviewers" as const,
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
mapper: ({
|
||||||
|
reviewerUserId: userId,
|
||||||
|
reviewerStatus: status,
|
||||||
|
reviewerEmail: email,
|
||||||
|
reviewerLastName: lastName,
|
||||||
|
reviewerUsername: username,
|
||||||
|
reviewerFirstName: firstName
|
||||||
|
}) => (userId ? { userId, status, email, firstName, lastName, username } : undefined)
|
||||||
},
|
},
|
||||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
{
|
||||||
|
key: "approverUserId",
|
||||||
|
label: "approvers" as const,
|
||||||
|
mapper: ({
|
||||||
|
approverUserId,
|
||||||
|
approverEmail: email,
|
||||||
|
approverUsername: username,
|
||||||
|
approverLastName: lastName,
|
||||||
|
approverFirstName: firstName
|
||||||
|
}) => ({
|
||||||
|
userId: approverUserId,
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
username
|
||||||
|
})
|
||||||
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
if (!formatedDoc?.[0]) return;
|
if (!formatedDoc?.[0]) return;
|
||||||
@ -97,12 +173,12 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const findProjectRequestCount = async (projectId: string, membershipId: string, tx?: Knex) => {
|
const findProjectRequestCount = async (projectId: string, userId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)
|
const docs = await (tx || db)
|
||||||
.with(
|
.with(
|
||||||
"temp",
|
"temp",
|
||||||
(tx || db)(TableName.SecretApprovalRequest)
|
(tx || db.replicaNode())(TableName.SecretApprovalRequest)
|
||||||
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
|
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
|
||||||
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||||
.join(
|
.join(
|
||||||
@ -114,8 +190,8 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
.andWhere(
|
.andWhere(
|
||||||
(bd) =>
|
(bd) =>
|
||||||
void bd
|
void bd
|
||||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, userId)
|
||||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, userId)
|
||||||
)
|
)
|
||||||
.select("status", `${TableName.SecretApprovalRequest}.id`)
|
.select("status", `${TableName.SecretApprovalRequest}.id`)
|
||||||
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
|
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
|
||||||
@ -142,13 +218,13 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const findByProjectId = async (
|
const findByProjectId = async (
|
||||||
{ status, limit = 20, offset = 0, projectId, committer, environment, membershipId }: TFindQueryFilter,
|
{ status, limit = 20, offset = 0, projectId, committer, environment, userId }: TFindQueryFilter,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
// akhilmhdh: If ever u wanted a 1 to so many relationship connected with pagination
|
// akhilmhdh: If ever u wanted a 1 to so many relationship connected with pagination
|
||||||
// this is the place u wanna look at.
|
// this is the place u wanna look at.
|
||||||
const query = (tx || db)(TableName.SecretApprovalRequest)
|
const query = (tx || db.replicaNode())(TableName.SecretApprovalRequest)
|
||||||
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
|
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
|
||||||
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||||
.join(
|
.join(
|
||||||
@ -161,6 +237,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretApprovalPolicy}.id`,
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
|
.join<TUsers>(
|
||||||
|
db(TableName.Users).as("committerUser"),
|
||||||
|
`${TableName.SecretApprovalRequest}.committerUserId`,
|
||||||
|
`committerUser.id`
|
||||||
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretApprovalRequestReviewer,
|
TableName.SecretApprovalRequestReviewer,
|
||||||
`${TableName.SecretApprovalRequest}.id`,
|
`${TableName.SecretApprovalRequest}.id`,
|
||||||
@ -176,20 +257,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
projectId,
|
projectId,
|
||||||
[`${TableName.Environment}.slug` as "slug"]: environment,
|
[`${TableName.Environment}.slug` as "slug"]: environment,
|
||||||
[`${TableName.SecretApprovalRequest}.status`]: status,
|
[`${TableName.SecretApprovalRequest}.status`]: status,
|
||||||
committerId: committer
|
committerUserId: committer
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.andWhere(
|
.andWhere(
|
||||||
(bd) =>
|
(bd) =>
|
||||||
void bd
|
void bd
|
||||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, userId)
|
||||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, userId)
|
||||||
)
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||||
.select(
|
.select(
|
||||||
db.ref("projectId").withSchema(TableName.Environment),
|
db.ref("projectId").withSchema(TableName.Environment),
|
||||||
db.ref("slug").withSchema(TableName.Environment).as("environment"),
|
db.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||||
db.ref("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
db.ref("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerId"),
|
||||||
|
db.ref("reviewerUserId").withSchema(TableName.SecretApprovalRequestReviewer),
|
||||||
db.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
db.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
||||||
db.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
db.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||||
db.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
db.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
||||||
@ -201,7 +283,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
),
|
),
|
||||||
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||||
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||||
db.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
|
db.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
|
||||||
|
db.ref("email").withSchema("committerUser").as("committerUserEmail"),
|
||||||
|
db.ref("username").withSchema("committerUser").as("committerUserUsername"),
|
||||||
|
db.ref("firstName").withSchema("committerUser").as("committerUserFirstName"),
|
||||||
|
db.ref("lastName").withSchema("committerUser").as("committerUserLastName")
|
||||||
)
|
)
|
||||||
.orderBy("createdAt", "desc");
|
.orderBy("createdAt", "desc");
|
||||||
|
|
||||||
@ -223,18 +309,26 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
name: el.policyName,
|
name: el.policyName,
|
||||||
approvals: el.policyApprovals,
|
approvals: el.policyApprovals,
|
||||||
secretPath: el.policySecretPath
|
secretPath: el.policySecretPath
|
||||||
|
},
|
||||||
|
committerUser: {
|
||||||
|
userId: el.committerUserId,
|
||||||
|
email: el.committerUserEmail,
|
||||||
|
firstName: el.committerUserFirstName,
|
||||||
|
lastName: el.committerUserLastName,
|
||||||
|
username: el.committerUserUsername
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
key: "reviewerMemberId",
|
key: "reviewerId",
|
||||||
label: "reviewers" as const,
|
label: "reviewers" as const,
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined)
|
mapper: ({ reviewerUserId, reviewerStatus: s }) =>
|
||||||
|
reviewerUserId ? { userId: reviewerUserId, status: s } : undefined
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "approverId",
|
key: "approverUserId",
|
||||||
label: "approvers" as const,
|
label: "approvers" as const,
|
||||||
mapper: ({ approverId }) => approverId
|
mapper: ({ approverUserId }) => approverUserId
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "commitId",
|
key: "commitId",
|
||||||
|
@ -47,7 +47,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findByRequestId = async (requestId: string, tx?: Knex) => {
|
const findByRequestId = async (requestId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)({
|
const doc = await (tx || db.replicaNode())({
|
||||||
secVerTag: TableName.SecretTag
|
secVerTag: TableName.SecretTag
|
||||||
})
|
})
|
||||||
.from(TableName.SecretApprovalRequestSecret)
|
.from(TableName.SecretApprovalRequestSecret)
|
||||||
|
@ -87,7 +87,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
await permissionService.getProjectPermission(
|
||||||
actor as ActorType.USER,
|
actor as ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
@ -95,7 +95,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
|
|
||||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id);
|
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId);
|
||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,19 +113,13 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}: TListApprovalsDTO) => {
|
}: TListApprovalsDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
await permissionService.getProjectPermission(actor, actorId, projectId, actorAuthMethod, actorOrgId);
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
const approvals = await secretApprovalRequestDAL.findByProjectId({
|
const approvals = await secretApprovalRequestDAL.findByProjectId({
|
||||||
projectId,
|
projectId,
|
||||||
committer,
|
committer,
|
||||||
environment,
|
environment,
|
||||||
status,
|
status,
|
||||||
membershipId: membership.id,
|
userId: actorId,
|
||||||
limit,
|
limit,
|
||||||
offset
|
offset
|
||||||
});
|
});
|
||||||
@ -145,7 +139,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||||
|
|
||||||
const { policy } = secretApprovalRequest;
|
const { policy } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
secretApprovalRequest.projectId,
|
||||||
@ -154,8 +148,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
@ -180,7 +174,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||||
|
|
||||||
const { policy } = secretApprovalRequest;
|
const { policy } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
ActorType.USER,
|
ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
secretApprovalRequest.projectId,
|
||||||
@ -189,8 +183,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
@ -198,7 +192,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const review = await secretApprovalRequestReviewerDAL.findOne(
|
const review = await secretApprovalRequestReviewerDAL.findOne(
|
||||||
{
|
{
|
||||||
requestId: secretApprovalRequest.id,
|
requestId: secretApprovalRequest.id,
|
||||||
member: membership.id
|
reviewerUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@ -207,7 +201,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
{
|
{
|
||||||
status,
|
status,
|
||||||
requestId: secretApprovalRequest.id,
|
requestId: secretApprovalRequest.id,
|
||||||
member: membership.id
|
reviewerUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@ -230,7 +224,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||||
|
|
||||||
const { policy } = secretApprovalRequest;
|
const { policy } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
ActorType.USER,
|
ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
secretApprovalRequest.projectId,
|
||||||
@ -239,8 +233,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
@ -253,7 +247,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
const updatedRequest = await secretApprovalRequestDAL.updateById(secretApprovalRequest.id, {
|
const updatedRequest = await secretApprovalRequestDAL.updateById(secretApprovalRequest.id, {
|
||||||
status,
|
status,
|
||||||
statusChangeBy: membership.id
|
statusChangedByUserId: actorId
|
||||||
});
|
});
|
||||||
return { ...secretApprovalRequest, ...updatedRequest };
|
return { ...secretApprovalRequest, ...updatedRequest };
|
||||||
};
|
};
|
||||||
@ -270,7 +264,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||||
|
|
||||||
const { policy, folderId, projectId } = secretApprovalRequest;
|
const { policy, folderId, projectId } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
ActorType.USER,
|
ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
@ -280,19 +274,19 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find(({ userId }) => userId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
const reviewers = secretApprovalRequest.reviewers.reduce<Record<string, ApprovalStatus>>(
|
const reviewers = secretApprovalRequest.reviewers.reduce<Record<string, ApprovalStatus>>(
|
||||||
(prev, curr) => ({ ...prev, [curr.member.toString()]: curr.status as ApprovalStatus }),
|
(prev, curr) => ({ ...prev, [curr.userId.toString()]: curr.status as ApprovalStatus }),
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const hasMinApproval =
|
const hasMinApproval =
|
||||||
secretApprovalRequest.policy.approvals <=
|
secretApprovalRequest.policy.approvals <=
|
||||||
secretApprovalRequest.policy.approvers.filter(
|
secretApprovalRequest.policy.approvers.filter(
|
||||||
(approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED
|
({ userId: approverId }) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
|
if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
|
||||||
@ -472,7 +466,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
conflicts: JSON.stringify(conflicts),
|
conflicts: JSON.stringify(conflicts),
|
||||||
hasMerged: true,
|
hasMerged: true,
|
||||||
status: RequestState.Closed,
|
status: RequestState.Closed,
|
||||||
statusChangeBy: membership.id
|
statusChangedByUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@ -509,7 +503,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}: TGenerateSecretApprovalRequestDTO) => {
|
}: TGenerateSecretApprovalRequestDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
@ -663,7 +657,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
policyId: policy.id,
|
policyId: policy.id,
|
||||||
status: "open",
|
status: "open",
|
||||||
hasMerged: false,
|
hasMerged: false,
|
||||||
committerId: membership.id
|
committerUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
@ -11,7 +11,6 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
|
|||||||
import { QueueName, TQueueServiceFactory } from "@app/queue";
|
import { QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
|
||||||
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
|
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
|
||||||
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
|
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
|
||||||
@ -46,7 +45,6 @@ type TSecretReplicationServiceFactoryDep = {
|
|||||||
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret" | "find">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret" | "find">;
|
||||||
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "create" | "transaction">;
|
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "create" | "transaction">;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findOne">;
|
|
||||||
secretApprovalRequestSecretDAL: Pick<
|
secretApprovalRequestSecretDAL: Pick<
|
||||||
TSecretApprovalRequestSecretDALFactory,
|
TSecretApprovalRequestSecretDALFactory,
|
||||||
"insertMany" | "insertApprovalSecretTags"
|
"insertMany" | "insertApprovalSecretTags"
|
||||||
@ -92,7 +90,6 @@ export const secretReplicationServiceFactory = ({
|
|||||||
secretApprovalRequestSecretDAL,
|
secretApprovalRequestSecretDAL,
|
||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
secretQueueService,
|
secretQueueService,
|
||||||
projectMembershipDAL,
|
|
||||||
projectBotService
|
projectBotService
|
||||||
}: TSecretReplicationServiceFactoryDep) => {
|
}: TSecretReplicationServiceFactoryDep) => {
|
||||||
const getReplicatedSecrets = (
|
const getReplicatedSecrets = (
|
||||||
@ -297,12 +294,6 @@ export const secretReplicationServiceFactory = ({
|
|||||||
);
|
);
|
||||||
// this means it should be a approval request rather than direct replication
|
// this means it should be a approval request rather than direct replication
|
||||||
if (policy && actor === ActorType.USER) {
|
if (policy && actor === ActorType.USER) {
|
||||||
const membership = await projectMembershipDAL.findOne({ projectId, userId: actorId });
|
|
||||||
if (!membership) {
|
|
||||||
logger.error("Project membership not found in %s for user %s", projectId, actorId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const localSecretsLatestVersions = destinationLocalSecrets.map(({ id }) => id);
|
const localSecretsLatestVersions = destinationLocalSecrets.map(({ id }) => id);
|
||||||
const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(
|
const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(
|
||||||
destinationReplicationFolderId,
|
destinationReplicationFolderId,
|
||||||
@ -316,7 +307,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
policyId: policy.id,
|
policyId: policy.id,
|
||||||
status: "open",
|
status: "open",
|
||||||
hasMerged: false,
|
hasMerged: false,
|
||||||
committerId: membership.id,
|
committerUserId: actorId,
|
||||||
isReplicated: true
|
isReplicated: true
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
|
@ -41,7 +41,7 @@ export const secretRotationDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const find = async (filter: TFindFilter<TSecretRotations & { projectId: string }>, tx?: Knex) => {
|
const find = async (filter: TFindFilter<TSecretRotations & { projectId: string }>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const data = await findQuery(filter, tx || db);
|
const data = await findQuery(filter, tx || db.replicaNode());
|
||||||
return sqlNestRelationships({
|
return sqlNestRelationships({
|
||||||
data,
|
data,
|
||||||
key: "id",
|
key: "id",
|
||||||
@ -93,7 +93,7 @@ export const secretRotationDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.SecretRotation)
|
const doc = await (tx || db.replicaNode())(TableName.SecretRotation)
|
||||||
.join(TableName.Environment, `${TableName.SecretRotation}.envId`, `${TableName.Environment}.id`)
|
.join(TableName.Environment, `${TableName.SecretRotation}.envId`, `${TableName.Environment}.id`)
|
||||||
.where({ [`${TableName.SecretRotation}.id` as "id"]: id })
|
.where({ [`${TableName.SecretRotation}.id` as "id"]: id })
|
||||||
.select(selectAllTableCols(TableName.SecretRotation))
|
.select(selectAllTableCols(TableName.SecretRotation))
|
||||||
|
@ -331,7 +331,7 @@ export const secretRotationQueueFactory = ({
|
|||||||
|
|
||||||
logger.info("Finished rotating: rotation id: ", rotationId);
|
logger.info("Finished rotating: rotation id: ", rotationId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error, "Failed to execute secret rotation");
|
||||||
if (error instanceof DisableRotationErrors) {
|
if (error instanceof DisableRotationErrors) {
|
||||||
if (job.id) {
|
if (job.id) {
|
||||||
await queue.stopRepeatableJobByJobId(QueueName.SecretRotation, job.id);
|
await queue.stopRepeatableJobByJobId(QueueName.SecretRotation, job.id);
|
||||||
|
@ -133,7 +133,7 @@ export const secretRotationServiceFactory = ({
|
|||||||
creds: []
|
creds: []
|
||||||
};
|
};
|
||||||
const encData = infisicalSymmetricEncypt(JSON.stringify(unencryptedData));
|
const encData = infisicalSymmetricEncypt(JSON.stringify(unencryptedData));
|
||||||
const secretRotation = secretRotationDAL.transaction(async (tx) => {
|
const secretRotation = await secretRotationDAL.transaction(async (tx) => {
|
||||||
const doc = await secretRotationDAL.create(
|
const doc = await secretRotationDAL.create(
|
||||||
{
|
{
|
||||||
provider,
|
provider,
|
||||||
@ -148,13 +148,13 @@ export const secretRotationServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await secretRotationQueue.addToQueue(doc.id, doc.interval);
|
|
||||||
const outputSecretMapping = await secretRotationDAL.secretOutputInsertMany(
|
const outputSecretMapping = await secretRotationDAL.secretOutputInsertMany(
|
||||||
Object.entries(outputs).map(([key, secretId]) => ({ key, secretId, rotationId: doc.id })),
|
Object.entries(outputs).map(([key, secretId]) => ({ key, secretId, rotationId: doc.id })),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
return { ...doc, outputs: outputSecretMapping, environment: folder.environment };
|
return { ...doc, outputs: outputSecretMapping, environment: folder.environment };
|
||||||
});
|
});
|
||||||
|
await secretRotationQueue.addToQueue(secretRotation.id, secretRotation.interval);
|
||||||
return secretRotation;
|
return secretRotation;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,9 +212,9 @@ export const secretRotationServiceFactory = ({
|
|||||||
);
|
);
|
||||||
const deletedDoc = await secretRotationDAL.transaction(async (tx) => {
|
const deletedDoc = await secretRotationDAL.transaction(async (tx) => {
|
||||||
const strat = await secretRotationDAL.deleteById(rotationId, tx);
|
const strat = await secretRotationDAL.deleteById(rotationId, tx);
|
||||||
await secretRotationQueue.removeFromQueue(strat.id, strat.interval);
|
|
||||||
return strat;
|
return strat;
|
||||||
});
|
});
|
||||||
|
await secretRotationQueue.removeFromQueue(deletedDoc.id, deletedDoc.interval);
|
||||||
return { ...doc, ...deletedDoc };
|
return { ...doc, ...deletedDoc };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const data = await (tx || db)(TableName.Snapshot)
|
const data = await (tx || db.replicaNode())(TableName.Snapshot)
|
||||||
.where(`${TableName.Snapshot}.id`, id)
|
.where(`${TableName.Snapshot}.id`, id)
|
||||||
.join(TableName.Environment, `${TableName.Snapshot}.envId`, `${TableName.Environment}.id`)
|
.join(TableName.Environment, `${TableName.Snapshot}.envId`, `${TableName.Environment}.id`)
|
||||||
.select(selectAllTableCols(TableName.Snapshot))
|
.select(selectAllTableCols(TableName.Snapshot))
|
||||||
@ -43,7 +43,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const countOfSnapshotsByFolderId = async (folderId: string, tx?: Knex) => {
|
const countOfSnapshotsByFolderId = async (folderId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.Snapshot)
|
const doc = await (tx || db.replicaNode())(TableName.Snapshot)
|
||||||
.where({ folderId })
|
.where({ folderId })
|
||||||
.groupBy(["folderId"])
|
.groupBy(["folderId"])
|
||||||
.count("folderId")
|
.count("folderId")
|
||||||
@ -56,7 +56,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findSecretSnapshotDataById = async (snapshotId: string, tx?: Knex) => {
|
const findSecretSnapshotDataById = async (snapshotId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const data = await (tx || db)(TableName.Snapshot)
|
const data = await (tx || db.replicaNode())(TableName.Snapshot)
|
||||||
.where(`${TableName.Snapshot}.id`, snapshotId)
|
.where(`${TableName.Snapshot}.id`, snapshotId)
|
||||||
.join(TableName.Environment, `${TableName.Snapshot}.envId`, `${TableName.Environment}.id`)
|
.join(TableName.Environment, `${TableName.Snapshot}.envId`, `${TableName.Environment}.id`)
|
||||||
.leftJoin(TableName.SnapshotSecret, `${TableName.Snapshot}.id`, `${TableName.SnapshotSecret}.snapshotId`)
|
.leftJoin(TableName.SnapshotSecret, `${TableName.Snapshot}.id`, `${TableName.SnapshotSecret}.snapshotId`)
|
||||||
@ -309,7 +309,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
// when we need to rollback we will pull from these snapshots
|
// when we need to rollback we will pull from these snapshots
|
||||||
const findLatestSnapshotByFolderId = async (folderId: string, tx?: Knex) => {
|
const findLatestSnapshotByFolderId = async (folderId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)(TableName.Snapshot)
|
const docs = await (tx || db.replicaNode())(TableName.Snapshot)
|
||||||
.where(`${TableName.Snapshot}.folderId`, folderId)
|
.where(`${TableName.Snapshot}.folderId`, folderId)
|
||||||
.join<TSecretSnapshots>(
|
.join<TSecretSnapshots>(
|
||||||
(tx || db)(TableName.Snapshot).groupBy("folderId").max("createdAt").select("folderId").as("latestVersion"),
|
(tx || db)(TableName.Snapshot).groupBy("folderId").max("createdAt").select("folderId").as("latestVersion"),
|
||||||
|
@ -42,6 +42,13 @@ export const IDENTITIES = {
|
|||||||
},
|
},
|
||||||
DELETE: {
|
DELETE: {
|
||||||
identityId: "The ID of the identity to delete."
|
identityId: "The ID of the identity to delete."
|
||||||
|
},
|
||||||
|
GET_BY_ID: {
|
||||||
|
identityId: "The ID of the identity to get details.",
|
||||||
|
orgId: "The ID of the org of the identity"
|
||||||
|
},
|
||||||
|
LIST: {
|
||||||
|
orgId: "The ID of the organization to list identities."
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -65,6 +72,9 @@ export const UNIVERSAL_AUTH = {
|
|||||||
RETRIEVE: {
|
RETRIEVE: {
|
||||||
identityId: "The ID of the identity to retrieve."
|
identityId: "The ID of the identity to retrieve."
|
||||||
},
|
},
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke."
|
||||||
|
},
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
identityId: "The ID of the identity to update.",
|
identityId: "The ID of the identity to update.",
|
||||||
clientSecretTrustedIps: "The new list of IPs or CIDR ranges that the Client Secret can be used from.",
|
clientSecretTrustedIps: "The new list of IPs or CIDR ranges that the Client Secret can be used from.",
|
||||||
@ -83,6 +93,10 @@ export const UNIVERSAL_AUTH = {
|
|||||||
LIST_CLIENT_SECRETS: {
|
LIST_CLIENT_SECRETS: {
|
||||||
identityId: "The ID of the identity to list client secrets for."
|
identityId: "The ID of the identity to list client secrets for."
|
||||||
},
|
},
|
||||||
|
GET_CLIENT_SECRET: {
|
||||||
|
identityId: "The ID of the identity to get the client secret from.",
|
||||||
|
clientSecretId: "The ID of the client secret to get details."
|
||||||
|
},
|
||||||
REVOKE_CLIENT_SECRET: {
|
REVOKE_CLIENT_SECRET: {
|
||||||
identityId: "The ID of the identity to revoke the client secret from.",
|
identityId: "The ID of the identity to revoke the client secret from.",
|
||||||
clientSecretId: "The ID of the client secret to revoke."
|
clientSecretId: "The ID of the client secret to revoke."
|
||||||
@ -104,6 +118,27 @@ export const AWS_AUTH = {
|
|||||||
iamRequestBody:
|
iamRequestBody:
|
||||||
"The base64-encoded body of the signed request. Most likely, the base64-encoding of Action=GetCallerIdentity&Version=2011-06-15.",
|
"The base64-encoded body of the signed request. Most likely, the base64-encoding of Action=GetCallerIdentity&Version=2011-06-15.",
|
||||||
iamRequestHeaders: "The base64-encoded headers of the sts:GetCallerIdentity signed request."
|
iamRequestHeaders: "The base64-encoded headers of the sts:GetCallerIdentity signed request."
|
||||||
|
},
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke."
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const AZURE_AUTH = {
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke."
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const GCP_AUTH = {
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke."
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const KUBERNETES_AUTH = {
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke."
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -657,6 +692,7 @@ export const INTEGRATION_AUTH = {
|
|||||||
integration: "The slug of integration for the auth object.",
|
integration: "The slug of integration for the auth object.",
|
||||||
accessId: "The unique authorized access id of the external integration provider.",
|
accessId: "The unique authorized access id of the external integration provider.",
|
||||||
accessToken: "The unique authorized access token of the external integration provider.",
|
accessToken: "The unique authorized access token of the external integration provider.",
|
||||||
|
awsAssumeIamRoleArn: "The AWS IAM Role to be assumed by Infisical",
|
||||||
url: "",
|
url: "",
|
||||||
namespace: "",
|
namespace: "",
|
||||||
refreshToken: "The refresh token for integration authorization."
|
refreshToken: "The refresh token for integration authorization."
|
||||||
|
@ -5,14 +5,25 @@ import { zpStr } from "../zod";
|
|||||||
|
|
||||||
export const GITLAB_URL = "https://gitlab.com";
|
export const GITLAB_URL = "https://gitlab.com";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- If `process.pkg` is set, and it's true, then it means that the app is currently running in a packaged environment (a binary)
|
||||||
|
export const IS_PACKAGED = (process as any)?.pkg !== undefined;
|
||||||
|
|
||||||
const zodStrBool = z
|
const zodStrBool = z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
.optional()
|
.optional()
|
||||||
.transform((val) => val === "true");
|
.transform((val) => val === "true");
|
||||||
|
|
||||||
|
const databaseReadReplicaSchema = z
|
||||||
|
.object({
|
||||||
|
DB_CONNECTION_URI: z.string().describe("Postgres read replica database connection string"),
|
||||||
|
DB_ROOT_CERT: zpStr(z.string().optional().describe("Postgres read replica database certificate string"))
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.optional();
|
||||||
|
|
||||||
const envSchema = z
|
const envSchema = z
|
||||||
.object({
|
.object({
|
||||||
PORT: z.coerce.number().default(4000),
|
PORT: z.coerce.number().default(IS_PACKAGED ? 8080 : 4000),
|
||||||
DISABLE_SECRET_SCANNING: z
|
DISABLE_SECRET_SCANNING: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
.default("false")
|
.default("false")
|
||||||
@ -29,6 +40,7 @@ const envSchema = z
|
|||||||
DB_USER: zpStr(z.string().describe("Postgres database username").optional()),
|
DB_USER: zpStr(z.string().describe("Postgres database username").optional()),
|
||||||
DB_PASSWORD: zpStr(z.string().describe("Postgres database password").optional()),
|
DB_PASSWORD: zpStr(z.string().describe("Postgres database password").optional()),
|
||||||
DB_NAME: zpStr(z.string().describe("Postgres database name").optional()),
|
DB_NAME: zpStr(z.string().describe("Postgres database name").optional()),
|
||||||
|
DB_READ_REPLICAS: zpStr(z.string().describe("Postgres read replicas").optional()),
|
||||||
BCRYPT_SALT_ROUND: z.number().default(12),
|
BCRYPT_SALT_ROUND: z.number().default(12),
|
||||||
NODE_ENV: z.enum(["development", "test", "production"]).default("production"),
|
NODE_ENV: z.enum(["development", "test", "production"]).default("production"),
|
||||||
SALT_ROUNDS: z.coerce.number().default(10),
|
SALT_ROUNDS: z.coerce.number().default(10),
|
||||||
@ -101,6 +113,9 @@ const envSchema = z
|
|||||||
// azure
|
// azure
|
||||||
CLIENT_ID_AZURE: zpStr(z.string().optional()),
|
CLIENT_ID_AZURE: zpStr(z.string().optional()),
|
||||||
CLIENT_SECRET_AZURE: zpStr(z.string().optional()),
|
CLIENT_SECRET_AZURE: zpStr(z.string().optional()),
|
||||||
|
// aws
|
||||||
|
CLIENT_ID_AWS_INTEGRATION: zpStr(z.string().optional()),
|
||||||
|
CLIENT_SECRET_AWS_INTEGRATION: zpStr(z.string().optional()),
|
||||||
// gitlab
|
// gitlab
|
||||||
CLIENT_ID_GITLAB: zpStr(z.string().optional()),
|
CLIENT_ID_GITLAB: zpStr(z.string().optional()),
|
||||||
CLIENT_SECRET_GITLAB: zpStr(z.string().optional()),
|
CLIENT_SECRET_GITLAB: zpStr(z.string().optional()),
|
||||||
@ -119,19 +134,24 @@ const envSchema = z
|
|||||||
// GENERIC
|
// GENERIC
|
||||||
STANDALONE_MODE: z
|
STANDALONE_MODE: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
.transform((val) => val === "true")
|
.transform((val) => val === "true" || IS_PACKAGED)
|
||||||
.optional(),
|
.optional(),
|
||||||
INFISICAL_CLOUD: zodStrBool.default("false"),
|
INFISICAL_CLOUD: zodStrBool.default("false"),
|
||||||
MAINTENANCE_MODE: zodStrBool.default("false"),
|
MAINTENANCE_MODE: zodStrBool.default("false"),
|
||||||
CAPTCHA_SECRET: zpStr(z.string().optional())
|
CAPTCHA_SECRET: zpStr(z.string().optional()),
|
||||||
|
PLAIN_API_KEY: zpStr(z.string().optional()),
|
||||||
|
PLAIN_WISH_LABEL_IDS: zpStr(z.string().optional())
|
||||||
})
|
})
|
||||||
.transform((data) => ({
|
.transform((data) => ({
|
||||||
...data,
|
...data,
|
||||||
|
DB_READ_REPLICAS: data.DB_READ_REPLICAS
|
||||||
|
? databaseReadReplicaSchema.parse(JSON.parse(data.DB_READ_REPLICAS))
|
||||||
|
: undefined,
|
||||||
isCloud: Boolean(data.LICENSE_SERVER_KEY),
|
isCloud: Boolean(data.LICENSE_SERVER_KEY),
|
||||||
isSmtpConfigured: Boolean(data.SMTP_HOST),
|
isSmtpConfigured: Boolean(data.SMTP_HOST),
|
||||||
isRedisConfigured: Boolean(data.REDIS_URL),
|
isRedisConfigured: Boolean(data.REDIS_URL),
|
||||||
isDevelopmentMode: data.NODE_ENV === "development",
|
isDevelopmentMode: data.NODE_ENV === "development",
|
||||||
isProductionMode: data.NODE_ENV === "production",
|
isProductionMode: data.NODE_ENV === "production" || IS_PACKAGED,
|
||||||
isSecretScanningConfigured:
|
isSecretScanningConfigured:
|
||||||
Boolean(data.SECRET_SCANNING_GIT_APP_ID) &&
|
Boolean(data.SECRET_SCANNING_GIT_APP_ID) &&
|
||||||
Boolean(data.SECRET_SCANNING_PRIVATE_KEY) &&
|
Boolean(data.SECRET_SCANNING_PRIVATE_KEY) &&
|
||||||
|
1
backend/src/lib/fn/argv.ts
Normal file
1
backend/src/lib/fn/argv.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const isMigrationMode = () => !!process.argv.slice(2).find((arg) => arg === "migration:latest"); // example -> ./binary migration:latest
|
@ -1,6 +1,7 @@
|
|||||||
// Some of the functions are taken from https://github.com/rayepps/radash
|
// Some of the functions are taken from https://github.com/rayepps/radash
|
||||||
// Full credits goes to https://github.com/rayapps to those functions
|
// Full credits goes to https://github.com/rayapps to those functions
|
||||||
// Code taken to keep in in house and to adjust somethings for our needs
|
// Code taken to keep in in house and to adjust somethings for our needs
|
||||||
|
export * from "./argv";
|
||||||
export * from "./array";
|
export * from "./array";
|
||||||
export * from "./dates";
|
export * from "./dates";
|
||||||
export * from "./object";
|
export * from "./object";
|
||||||
|
@ -50,7 +50,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(db: Kne
|
|||||||
}),
|
}),
|
||||||
findById: async (id: string, tx?: Knex) => {
|
findById: async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const result = await (tx || db)(tableName)
|
const result = await (tx || db.replicaNode())(tableName)
|
||||||
.where({ id } as never)
|
.where({ id } as never)
|
||||||
.first("*");
|
.first("*");
|
||||||
return result;
|
return result;
|
||||||
@ -60,7 +60,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(db: Kne
|
|||||||
},
|
},
|
||||||
findOne: async (filter: Partial<Tables[Tname]["base"]>, tx?: Knex) => {
|
findOne: async (filter: Partial<Tables[Tname]["base"]>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const res = await (tx || db)(tableName).where(filter).first("*");
|
const res = await (tx || db.replicaNode())(tableName).where(filter).first("*");
|
||||||
return res;
|
return res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Find one" });
|
throw new DatabaseError({ error, name: "Find one" });
|
||||||
@ -71,7 +71,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(db: Kne
|
|||||||
{ offset, limit, sort, tx }: TFindOpt<Tables[Tname]["base"]> = {}
|
{ offset, limit, sort, tx }: TFindOpt<Tables[Tname]["base"]> = {}
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const query = (tx || db)(tableName).where(buildFindFilter(filter));
|
const query = (tx || db.replicaNode())(tableName).where(buildFindFilter(filter));
|
||||||
if (limit) void query.limit(limit);
|
if (limit) void query.limit(limit);
|
||||||
if (offset) void query.offset(offset);
|
if (offset) void query.offset(offset);
|
||||||
if (sort) {
|
if (sort) {
|
||||||
|
@ -58,7 +58,8 @@ const redactedKeys = [
|
|||||||
"decryptedSecret",
|
"decryptedSecret",
|
||||||
"secrets",
|
"secrets",
|
||||||
"key",
|
"key",
|
||||||
"password"
|
"password",
|
||||||
|
"config"
|
||||||
];
|
];
|
||||||
|
|
||||||
export const initLogger = async () => {
|
export const initLogger = async () => {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
import { initDbConnection } from "./db";
|
import { initDbConnection } from "./db";
|
||||||
import { keyStoreFactory } from "./keystore/keystore";
|
import { keyStoreFactory } from "./keystore/keystore";
|
||||||
import { formatSmtpConfig, initEnvConfig } from "./lib/config/env";
|
import { formatSmtpConfig, initEnvConfig, IS_PACKAGED } from "./lib/config/env";
|
||||||
|
import { isMigrationMode } from "./lib/fn";
|
||||||
import { initLogger } from "./lib/logger";
|
import { initLogger } from "./lib/logger";
|
||||||
import { queueServiceFactory } from "./queue";
|
import { queueServiceFactory } from "./queue";
|
||||||
import { main } from "./server/app";
|
import { main } from "./server/app";
|
||||||
@ -10,20 +12,43 @@ import { bootstrapCheck } from "./server/boot-strap-check";
|
|||||||
import { smtpServiceFactory } from "./services/smtp/smtp-service";
|
import { smtpServiceFactory } from "./services/smtp/smtp-service";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
const logger = await initLogger();
|
const logger = await initLogger();
|
||||||
const appCfg = initEnvConfig(logger);
|
const appCfg = initEnvConfig(logger);
|
||||||
const db = initDbConnection({
|
const db = initDbConnection({
|
||||||
dbConnectionUri: appCfg.DB_CONNECTION_URI,
|
dbConnectionUri: appCfg.DB_CONNECTION_URI,
|
||||||
dbRootCert: appCfg.DB_ROOT_CERT
|
dbRootCert: appCfg.DB_ROOT_CERT,
|
||||||
|
readReplicas: appCfg.DB_READ_REPLICAS?.map((el) => ({
|
||||||
|
dbRootCert: el.DB_ROOT_CERT,
|
||||||
|
dbConnectionUri: el.DB_CONNECTION_URI
|
||||||
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Case: App is running in packaged mode (binary), and migration mode is enabled.
|
||||||
|
// Run the migrations and exit the process after completion.
|
||||||
|
if (IS_PACKAGED && isMigrationMode()) {
|
||||||
|
try {
|
||||||
|
logger.info("Running Postgres migrations..");
|
||||||
|
await db.migrate.latest({
|
||||||
|
directory: path.join(__dirname, "./db/migrations")
|
||||||
|
});
|
||||||
|
logger.info("Postgres migrations completed");
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err, "Failed to run migrations");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const smtp = smtpServiceFactory(formatSmtpConfig());
|
const smtp = smtpServiceFactory(formatSmtpConfig());
|
||||||
const queue = queueServiceFactory(appCfg.REDIS_URL);
|
const queue = queueServiceFactory(appCfg.REDIS_URL);
|
||||||
const keyStore = keyStoreFactory(appCfg.REDIS_URL);
|
const keyStore = keyStoreFactory(appCfg.REDIS_URL);
|
||||||
|
|
||||||
const server = await main({ db, smtp, logger, queue, keyStore });
|
const server = await main({ db, smtp, logger, queue, keyStore });
|
||||||
const bootstrap = await bootstrapCheck({ db });
|
const bootstrap = await bootstrapCheck({ db });
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
process.on("SIGINT", async () => {
|
process.on("SIGINT", async () => {
|
||||||
await server.close();
|
await server.close();
|
||||||
|
@ -15,7 +15,7 @@ import { Knex } from "knex";
|
|||||||
import { Logger } from "pino";
|
import { Logger } from "pino";
|
||||||
|
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig, IS_PACKAGED } from "@app/lib/config/env";
|
||||||
import { TQueueServiceFactory } from "@app/queue";
|
import { TQueueServiceFactory } from "@app/queue";
|
||||||
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
|
|
||||||
@ -80,8 +80,8 @@ export const main = async ({ db, smtp, logger, queue, keyStore }: TMain) => {
|
|||||||
|
|
||||||
if (appCfg.isProductionMode) {
|
if (appCfg.isProductionMode) {
|
||||||
await server.register(registerExternalNextjs, {
|
await server.register(registerExternalNextjs, {
|
||||||
standaloneMode: appCfg.STANDALONE_MODE,
|
standaloneMode: appCfg.STANDALONE_MODE || IS_PACKAGED,
|
||||||
dir: path.join(__dirname, "../../"),
|
dir: path.join(__dirname, IS_PACKAGED ? "../../../" : "../../"),
|
||||||
port: appCfg.PORT
|
port: appCfg.PORT
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -82,3 +82,9 @@ export const publicSecretShareCreationLimit: RateLimitOptions = {
|
|||||||
max: 5,
|
max: 5,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const userEngagementLimit: RateLimitOptions = {
|
||||||
|
timeWindow: 60 * 1000,
|
||||||
|
max: 5,
|
||||||
|
keyGenerator: (req) => req.realIp
|
||||||
|
};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
// this plugins allows to run infisical in standalone mode
|
// this plugins allows to run infisical in standalone mode
|
||||||
// standalone mode = infisical backend and nextjs frontend in one server
|
// standalone mode = infisical backend and nextjs frontend in one server
|
||||||
// this way users don't need to deploy two things
|
// this way users don't need to deploy two things
|
||||||
|
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
|
import { IS_PACKAGED } from "@app/lib/config/env";
|
||||||
|
|
||||||
// to enabled this u need to set standalone mode to true
|
// to enabled this u need to set standalone mode to true
|
||||||
export const registerExternalNextjs = async (
|
export const registerExternalNextjs = async (
|
||||||
server: FastifyZodProvider,
|
server: FastifyZodProvider,
|
||||||
@ -18,20 +19,33 @@ export const registerExternalNextjs = async (
|
|||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
if (standaloneMode) {
|
if (standaloneMode) {
|
||||||
const nextJsBuildPath = path.join(dir, "frontend-build");
|
const frontendName = IS_PACKAGED ? "frontend" : "frontend-build";
|
||||||
|
const nextJsBuildPath = path.join(dir, frontendName);
|
||||||
|
|
||||||
const { default: conf } = (await import(
|
const { default: conf } = (await import(
|
||||||
path.join(dir, "frontend-build/.next/required-server-files.json"),
|
path.join(dir, `${frontendName}/.next/required-server-files.json`),
|
||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
{
|
{
|
||||||
assert: { type: "json" }
|
assert: { type: "json" }
|
||||||
}
|
}
|
||||||
)) as { default: { config: string } };
|
)) as { default: { config: string } };
|
||||||
|
|
||||||
/* eslint-disable */
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const { default: NextServer } = (
|
let NextServer: any;
|
||||||
await import(path.join(dir, "frontend-build/node_modules/next/dist/server/next-server.js"))
|
|
||||||
).default;
|
if (!IS_PACKAGED) {
|
||||||
|
/* eslint-disable */
|
||||||
|
const { default: nextServer } = (
|
||||||
|
await import(path.join(dir, `${frontendName}/node_modules/next/dist/server/next-server.js`))
|
||||||
|
).default;
|
||||||
|
|
||||||
|
NextServer = nextServer;
|
||||||
|
} else {
|
||||||
|
/* eslint-disable */
|
||||||
|
const nextServer = await import(path.join(dir, `${frontendName}/node_modules/next/dist/server/next-server.js`));
|
||||||
|
|
||||||
|
NextServer = nextServer.default;
|
||||||
|
}
|
||||||
|
|
||||||
const nextApp = new NextServer({
|
const nextApp = new NextServer({
|
||||||
dev: false,
|
dev: false,
|
||||||
|
@ -164,6 +164,7 @@ import { telemetryServiceFactory } from "@app/services/telemetry/telemetry-servi
|
|||||||
import { userDALFactory } from "@app/services/user/user-dal";
|
import { userDALFactory } from "@app/services/user/user-dal";
|
||||||
import { userServiceFactory } from "@app/services/user/user-service";
|
import { userServiceFactory } from "@app/services/user/user-service";
|
||||||
import { userAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { userAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
|
import { userEngagementServiceFactory } from "@app/services/user-engagement/user-engagement-service";
|
||||||
import { webhookDALFactory } from "@app/services/webhook/webhook-dal";
|
import { webhookDALFactory } from "@app/services/webhook/webhook-dal";
|
||||||
import { webhookServiceFactory } from "@app/services/webhook/webhook-service";
|
import { webhookServiceFactory } from "@app/services/webhook/webhook-service";
|
||||||
|
|
||||||
@ -319,7 +320,6 @@ export const registerRoutes = async (
|
|||||||
auditLogStreamDAL
|
auditLogStreamDAL
|
||||||
});
|
});
|
||||||
const secretApprovalPolicyService = secretApprovalPolicyServiceFactory({
|
const secretApprovalPolicyService = secretApprovalPolicyServiceFactory({
|
||||||
projectMembershipDAL,
|
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
secretApprovalPolicyApproverDAL: sapApproverDAL,
|
secretApprovalPolicyApproverDAL: sapApproverDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@ -415,8 +415,10 @@ export const registerRoutes = async (
|
|||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
orgMembershipDAL,
|
orgMembershipDAL,
|
||||||
tokenService,
|
tokenService,
|
||||||
smtpService
|
smtpService,
|
||||||
|
projectMembershipDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const loginService = authLoginServiceFactory({ userDAL, smtpService, tokenService, orgDAL, tokenDAL: authTokenDAL });
|
const loginService = authLoginServiceFactory({ userDAL, smtpService, tokenService, orgDAL, tokenDAL: authTokenDAL });
|
||||||
const passwordService = authPaswordServiceFactory({
|
const passwordService = authPaswordServiceFactory({
|
||||||
tokenService,
|
tokenService,
|
||||||
@ -766,7 +768,6 @@ export const registerRoutes = async (
|
|||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
secretApprovalRequestSecretDAL,
|
secretApprovalRequestSecretDAL,
|
||||||
secretQueueService,
|
secretQueueService,
|
||||||
projectMembershipDAL,
|
|
||||||
projectBotService
|
projectBotService
|
||||||
});
|
});
|
||||||
const secretRotationQueue = secretRotationQueueFactory({
|
const secretRotationQueue = secretRotationQueueFactory({
|
||||||
@ -806,7 +807,8 @@ export const registerRoutes = async (
|
|||||||
const identityService = identityServiceFactory({
|
const identityService = identityServiceFactory({
|
||||||
permissionService,
|
permissionService,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
identityOrgMembershipDAL
|
identityOrgMembershipDAL,
|
||||||
|
licenseService
|
||||||
});
|
});
|
||||||
const identityAccessTokenService = identityAccessTokenServiceFactory({
|
const identityAccessTokenService = identityAccessTokenServiceFactory({
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
@ -921,6 +923,10 @@ export const registerRoutes = async (
|
|||||||
oidcConfigDAL
|
oidcConfigDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const userEngagementService = userEngagementServiceFactory({
|
||||||
|
userDAL
|
||||||
|
});
|
||||||
|
|
||||||
await superAdminService.initServerCfg();
|
await superAdminService.initServerCfg();
|
||||||
//
|
//
|
||||||
// setup the communication with license key server
|
// setup the communication with license key server
|
||||||
@ -992,7 +998,8 @@ export const registerRoutes = async (
|
|||||||
telemetry: telemetryService,
|
telemetry: telemetryService,
|
||||||
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService,
|
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService,
|
||||||
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService,
|
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService,
|
||||||
secretSharing: secretSharingService
|
secretSharing: secretSharingService,
|
||||||
|
userEngagement: userEngagementService
|
||||||
});
|
});
|
||||||
|
|
||||||
const cronJobs: CronJob[] = [];
|
const cronJobs: CronJob[] = [];
|
||||||
|
@ -8,6 +8,7 @@ import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||||
@ -54,7 +55,14 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
trustSamlEmails: z.boolean().optional(),
|
trustSamlEmails: z.boolean().optional(),
|
||||||
trustLdapEmails: z.boolean().optional(),
|
trustLdapEmails: z.boolean().optional(),
|
||||||
trustOidcEmails: z.boolean().optional(),
|
trustOidcEmails: z.boolean().optional(),
|
||||||
defaultAuthOrgId: z.string().optional().nullable()
|
defaultAuthOrgId: z.string().optional().nullable(),
|
||||||
|
enabledLoginMethods: z
|
||||||
|
.nativeEnum(LoginMethod)
|
||||||
|
.array()
|
||||||
|
.optional()
|
||||||
|
.refine((methods) => !methods || methods.length > 0, {
|
||||||
|
message: "At least one login method should be enabled."
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -70,7 +78,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const config = await server.services.superAdmin.updateServerCfg(req.body);
|
const config = await server.services.superAdmin.updateServerCfg(req.body, req.permission.id);
|
||||||
return { config };
|
return { config };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -266,4 +266,51 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider)
|
|||||||
return { identityAwsAuth };
|
return { identityAwsAuth };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/aws-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Delete AWS Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(AWS_AUTH.REVOKE.identityId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityAwsAuth: IdentityAwsAuthsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityAwsAuth = await server.services.identityAwsAuth.revokeIdentityAwsAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityAwsAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REVOKE_IDENTITY_AWS_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityAwsAuth.identityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityAwsAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { IdentityAzureAuthsSchema } from "@app/db/schemas";
|
import { IdentityAzureAuthsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { AZURE_AUTH } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
@ -259,4 +260,51 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider
|
|||||||
return { identityAzureAuth };
|
return { identityAzureAuth };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/azure-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Delete Azure Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(AZURE_AUTH.REVOKE.identityId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityAzureAuth: IdentityAzureAuthsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityAzureAuth = await server.services.identityAzureAuth.revokeIdentityAzureAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityAzureAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REVOKE_IDENTITY_AZURE_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityAzureAuth.identityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityAzureAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { IdentityGcpAuthsSchema } from "@app/db/schemas";
|
import { IdentityGcpAuthsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { GCP_AUTH } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
@ -265,4 +266,51 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider)
|
|||||||
return { identityGcpAuth };
|
return { identityGcpAuth };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/gcp-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Delete GCP Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(GCP_AUTH.REVOKE.identityId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityGcpAuth: IdentityGcpAuthsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityGcpAuth = await server.services.identityGcpAuth.revokeIdentityGcpAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityGcpAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REVOKE_IDENTITY_GCP_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityGcpAuth.identityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityGcpAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { IdentityKubernetesAuthsSchema } from "@app/db/schemas";
|
import { IdentityKubernetesAuthsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { KUBERNETES_AUTH } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
@ -280,4 +281,54 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
|
|||||||
return { identityKubernetesAuth: IdentityKubernetesAuthResponseSchema.parse(identityKubernetesAuth) };
|
return { identityKubernetesAuth: IdentityKubernetesAuthResponseSchema.parse(identityKubernetesAuth) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/kubernetes-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Delete Kubernetes Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(KUBERNETES_AUTH.REVOKE.identityId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityKubernetesAuth: IdentityKubernetesAuthResponseSchema.omit({
|
||||||
|
caCert: true,
|
||||||
|
tokenReviewerJwt: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityKubernetesAuth = await server.services.identityKubernetesAuth.revokeIdentityKubernetesAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityKubernetesAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REVOKE_IDENTITY_KUBERNETES_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityKubernetesAuth.identityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityKubernetesAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
|
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgMembershipRole, OrgRolesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { IDENTITIES } from "@app/lib/api-docs";
|
import { IDENTITIES } from "@app/lib/api-docs";
|
||||||
import { creationLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { creationLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
@ -170,4 +170,94 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
return { identity };
|
return { identity };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Get an identity by id",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(IDENTITIES.GET_BY_ID.identityId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identity: IdentityOrgMembershipsSchema.extend({
|
||||||
|
customRole: OrgRolesSchema.pick({
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
permissions: true,
|
||||||
|
description: true
|
||||||
|
}).optional(),
|
||||||
|
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identity = await server.services.identity.getIdentityById({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
id: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identity };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "List identities",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
querystring: z.object({
|
||||||
|
orgId: z.string().describe(IDENTITIES.LIST.orgId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identities: IdentityOrgMembershipsSchema.extend({
|
||||||
|
customRole: OrgRolesSchema.pick({
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
permissions: true,
|
||||||
|
description: true
|
||||||
|
}).optional(),
|
||||||
|
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
||||||
|
}).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identities = await server.services.identity.listOrgIdentities({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
orgId: req.query.orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identities };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -134,7 +134,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const identityUniversalAuth = await server.services.identityUa.attachUa({
|
const identityUniversalAuth = await server.services.identityUa.attachUniversalAuth({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
@ -219,7 +219,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const identityUniversalAuth = await server.services.identityUa.updateUa({
|
const identityUniversalAuth = await server.services.identityUa.updateUniversalAuth({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
@ -272,7 +272,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const identityUniversalAuth = await server.services.identityUa.getIdentityUa({
|
const identityUniversalAuth = await server.services.identityUa.getIdentityUniversalAuth({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
@ -295,6 +295,53 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/universal-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Delete Universal Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(UNIVERSAL_AUTH.REVOKE.identityId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityUniversalAuth: IdentityUniversalAuthsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityUniversalAuth = await server.services.identityUa.revokeIdentityUniversalAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityUniversalAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityUniversalAuth.identityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityUniversalAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/universal-auth/identities/:identityId/client-secrets",
|
url: "/universal-auth/identities/:identityId/client-secrets",
|
||||||
@ -325,14 +372,15 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { clientSecret, clientSecretData, orgId } = await server.services.identityUa.createUaClientSecret({
|
const { clientSecret, clientSecretData, orgId } =
|
||||||
actor: req.permission.type,
|
await server.services.identityUa.createUniversalAuthClientSecret({
|
||||||
actorId: req.permission.id,
|
actor: req.permission.type,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorId: req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
identityId: req.params.identityId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.body
|
identityId: req.params.identityId,
|
||||||
});
|
...req.body
|
||||||
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
@ -374,13 +422,15 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUaClientSecrets({
|
const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUniversalAuthClientSecrets(
|
||||||
actor: req.permission.type,
|
{
|
||||||
actorId: req.permission.id,
|
actor: req.permission.type,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorId: req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
identityId: req.params.identityId
|
actorOrgId: req.permission.orgId,
|
||||||
});
|
identityId: req.params.identityId
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
@ -396,6 +446,56 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/universal-auth/identities/:identityId/client-secrets/:clientSecretId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Get Universal Auth Client Secret for identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(UNIVERSAL_AUTH.GET_CLIENT_SECRET.identityId),
|
||||||
|
clientSecretId: z.string().describe(UNIVERSAL_AUTH.GET_CLIENT_SECRET.clientSecretId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
clientSecretData: sanitizedClientSecretSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const clientSecretData = await server.services.identityUa.getUniversalAuthClientSecretById({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
clientSecretId: req.params.clientSecretId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: clientSecretData.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET,
|
||||||
|
metadata: {
|
||||||
|
identityId: clientSecretData.identityId,
|
||||||
|
clientSecretId: clientSecretData.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { clientSecretData };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
|
url: "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
|
||||||
@ -421,7 +521,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const clientSecretData = await server.services.identityUa.revokeUaClientSecret({
|
const clientSecretData = await server.services.identityUa.revokeUniversalAuthClientSecret({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
@ -9,7 +9,7 @@ import { registerIdentityAzureAuthRouter } from "./identity-azure-auth-router";
|
|||||||
import { registerIdentityGcpAuthRouter } from "./identity-gcp-auth-router";
|
import { registerIdentityGcpAuthRouter } from "./identity-gcp-auth-router";
|
||||||
import { registerIdentityKubernetesRouter } from "./identity-kubernetes-auth-router";
|
import { registerIdentityKubernetesRouter } from "./identity-kubernetes-auth-router";
|
||||||
import { registerIdentityRouter } from "./identity-router";
|
import { registerIdentityRouter } from "./identity-router";
|
||||||
import { registerIdentityUaRouter } from "./identity-ua";
|
import { registerIdentityUaRouter } from "./identity-universal-auth-router";
|
||||||
import { registerIntegrationAuthRouter } from "./integration-auth-router";
|
import { registerIntegrationAuthRouter } from "./integration-auth-router";
|
||||||
import { registerIntegrationRouter } from "./integration-router";
|
import { registerIntegrationRouter } from "./integration-router";
|
||||||
import { registerInviteOrgRouter } from "./invite-org-router";
|
import { registerInviteOrgRouter } from "./invite-org-router";
|
||||||
@ -25,6 +25,7 @@ import { registerSecretSharingRouter } from "./secret-sharing-router";
|
|||||||
import { registerSecretTagRouter } from "./secret-tag-router";
|
import { registerSecretTagRouter } from "./secret-tag-router";
|
||||||
import { registerSsoRouter } from "./sso-router";
|
import { registerSsoRouter } from "./sso-router";
|
||||||
import { registerUserActionRouter } from "./user-action-router";
|
import { registerUserActionRouter } from "./user-action-router";
|
||||||
|
import { registerUserEngagementRouter } from "./user-engagement-router";
|
||||||
import { registerUserRouter } from "./user-router";
|
import { registerUserRouter } from "./user-router";
|
||||||
import { registerWebhookRouter } from "./webhook-router";
|
import { registerWebhookRouter } from "./webhook-router";
|
||||||
|
|
||||||
@ -77,4 +78,5 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
|
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
|
||||||
await server.register(registerIdentityRouter, { prefix: "/identities" });
|
await server.register(registerIdentityRouter, { prefix: "/identities" });
|
||||||
await server.register(registerSecretSharingRouter, { prefix: "/secret-sharing" });
|
await server.register(registerSecretSharingRouter, { prefix: "/secret-sharing" });
|
||||||
|
await server.register(registerUserEngagementRouter, { prefix: "/user-engagement" });
|
||||||
};
|
};
|
||||||
|
@ -240,6 +240,12 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
|||||||
integration: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.integration),
|
integration: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.integration),
|
||||||
accessId: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessId),
|
accessId: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessId),
|
||||||
accessToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessToken),
|
accessToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessToken),
|
||||||
|
awsAssumeIamRoleArn: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.trim()
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.awsAssumeIamRoleArn),
|
||||||
url: z.string().url().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.url),
|
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),
|
namespace: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.namespace),
|
||||||
refreshToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.refreshToken)
|
refreshToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.refreshToken)
|
||||||
|
@ -372,6 +372,44 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PUT",
|
||||||
|
url: "/:workspaceSlug/audit-logs-retention",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
workspaceSlug: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
auditLogsRetentionDays: z.number().min(0)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
workspace: ProjectsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const workspace = await server.services.project.updateAuditLogsRetention({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
workspaceSlug: req.params.workspaceSlug,
|
||||||
|
auditLogsRetentionDays: req.body.auditLogsRetentionDays
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: "Successfully updated project's audit logs retention period",
|
||||||
|
workspace
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:workspaceId/integrations",
|
url: "/:workspaceId/integrations",
|
||||||
|
27
backend/src/server/routes/v1/user-engagement-router.ts
Normal file
27
backend/src/server/routes/v1/user-engagement-router.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { userEngagementLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerUserEngagementRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/me/wish",
|
||||||
|
config: {
|
||||||
|
rateLimit: userEngagementLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
text: z.string().min(1)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
return server.services.userEngagement.createUserWish(req.permission.id, req.body.text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -3,7 +3,7 @@ import { z } from "zod";
|
|||||||
import { UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
import { UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { authRateLimit, readLimit } from "@app/server/config/rateLimiter";
|
import { authRateLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -90,4 +90,48 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
|||||||
return res.redirect(`${appCfg.SITE_URL}/login`);
|
return res.redirect(`${appCfg.SITE_URL}/login`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/me/project-favorites",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
orgId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
projectFavorites: z.string().array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
return server.services.user.getUserProjectFavorites(req.permission.id, req.query.orgId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PUT",
|
||||||
|
url: "/me/project-favorites",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
orgId: z.string().trim(),
|
||||||
|
projectFavorites: z.string().array()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
return server.services.user.updateUserProjectFavorites(
|
||||||
|
req.permission.id,
|
||||||
|
req.body.orgId,
|
||||||
|
req.body.projectFavorites
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -6,13 +6,17 @@ import { removeTrailingSlash } from "@app/lib/fn";
|
|||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { WebhookType } from "@app/services/webhook/webhook-types";
|
||||||
|
|
||||||
export const sanitizedWebhookSchema = WebhooksSchema.omit({
|
export const sanitizedWebhookSchema = WebhooksSchema.omit({
|
||||||
encryptedSecretKey: true,
|
encryptedSecretKey: true,
|
||||||
iv: true,
|
iv: true,
|
||||||
tag: true,
|
tag: true,
|
||||||
algorithm: true,
|
algorithm: true,
|
||||||
keyEncoding: true
|
keyEncoding: true,
|
||||||
|
urlCipherText: true,
|
||||||
|
urlIV: true,
|
||||||
|
urlTag: true
|
||||||
}).merge(
|
}).merge(
|
||||||
z.object({
|
z.object({
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
@ -33,13 +37,24 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z
|
||||||
workspaceId: z.string().trim(),
|
.object({
|
||||||
environment: z.string().trim(),
|
type: z.nativeEnum(WebhookType).default(WebhookType.GENERAL),
|
||||||
webhookUrl: z.string().url().trim(),
|
workspaceId: z.string().trim(),
|
||||||
webhookSecretKey: z.string().trim().optional(),
|
environment: z.string().trim(),
|
||||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash)
|
webhookUrl: z.string().url().trim(),
|
||||||
}),
|
webhookSecretKey: z.string().trim().optional(),
|
||||||
|
secretPath: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
if (data.type === WebhookType.SLACK && !data.webhookUrl.includes("hooks.slack.com")) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "Incoming Webhook URL is invalid.",
|
||||||
|
path: ["webhookUrl"]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
@ -66,8 +81,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
|||||||
environment: webhook.environment.slug,
|
environment: webhook.environment.slug,
|
||||||
webhookId: webhook.id,
|
webhookId: webhook.id,
|
||||||
isDisabled: webhook.isDisabled,
|
isDisabled: webhook.isDisabled,
|
||||||
secretPath: webhook.secretPath,
|
secretPath: webhook.secretPath
|
||||||
webhookUrl: webhook.url
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -116,8 +130,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
|||||||
environment: webhook.environment.slug,
|
environment: webhook.environment.slug,
|
||||||
webhookId: webhook.id,
|
webhookId: webhook.id,
|
||||||
isDisabled: webhook.isDisabled,
|
isDisabled: webhook.isDisabled,
|
||||||
secretPath: webhook.secretPath,
|
secretPath: webhook.secretPath
|
||||||
webhookUrl: webhook.url
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -156,8 +169,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
|||||||
environment: webhook.environment.slug,
|
environment: webhook.environment.slug,
|
||||||
webhookId: webhook.id,
|
webhookId: webhook.id,
|
||||||
isDisabled: webhook.isDisabled,
|
isDisabled: webhook.isDisabled,
|
||||||
secretPath: webhook.secretPath,
|
secretPath: webhook.secretPath
|
||||||
webhookUrl: webhook.url
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -949,7 +949,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
event: {
|
event: {
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||||
metadata: {
|
metadata: {
|
||||||
committedBy: approval.committerId,
|
committedBy: approval.committerUserId,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
}
|
}
|
||||||
@ -1133,7 +1133,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
event: {
|
event: {
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||||
metadata: {
|
metadata: {
|
||||||
committedBy: approval.committerId,
|
committedBy: approval.committerUserId,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
}
|
}
|
||||||
@ -1271,7 +1271,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
event: {
|
event: {
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||||
metadata: {
|
metadata: {
|
||||||
committedBy: approval.committerId,
|
committedBy: approval.committerUserId,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
}
|
}
|
||||||
@ -1397,7 +1397,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
event: {
|
event: {
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||||
metadata: {
|
metadata: {
|
||||||
committedBy: approval.committerId,
|
committedBy: approval.committerUserId,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
}
|
}
|
||||||
@ -1524,7 +1524,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
event: {
|
event: {
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||||
metadata: {
|
metadata: {
|
||||||
committedBy: approval.committerId,
|
committedBy: approval.committerUserId,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
}
|
}
|
||||||
@ -1638,7 +1638,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
event: {
|
event: {
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||||
metadata: {
|
metadata: {
|
||||||
committedBy: approval.committerId,
|
committedBy: approval.committerUserId,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ export const tokenDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findOneTokenSession = async (filter: Partial<TAuthTokenSessions>): Promise<TAuthTokenSessions | undefined> => {
|
const findOneTokenSession = async (filter: Partial<TAuthTokenSessions>): Promise<TAuthTokenSessions | undefined> => {
|
||||||
try {
|
try {
|
||||||
const doc = await db(TableName.AuthTokenSession).where(filter).first();
|
const doc = await db.replicaNode()(TableName.AuthTokenSession).where(filter).first();
|
||||||
return doc;
|
return doc;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "FindOneTokenSession" });
|
throw new DatabaseError({ error, name: "FindOneTokenSession" });
|
||||||
@ -44,7 +44,7 @@ export const tokenDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findTokenSessions = async (filter: Partial<TAuthTokenSessions>, tx?: Knex) => {
|
const findTokenSessions = async (filter: Partial<TAuthTokenSessions>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const sessions = await (tx || db)(TableName.AuthTokenSession).where(filter);
|
const sessions = await (tx || db.replicaNode())(TableName.AuthTokenSession).where(filter);
|
||||||
return sessions;
|
return sessions;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ name: "Find all token session", error });
|
throw new DatabaseError({ name: "Find all token session", error });
|
||||||
|
@ -17,6 +17,7 @@ import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
|||||||
import { TokenType } from "../auth-token/auth-token-types";
|
import { TokenType } from "../auth-token/auth-token-types";
|
||||||
import { TOrgDALFactory } from "../org/org-dal";
|
import { TOrgDALFactory } from "../org/org-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||||
|
import { LoginMethod } from "../super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { enforceUserLockStatus, validateProviderAuthToken } from "./auth-fns";
|
import { enforceUserLockStatus, validateProviderAuthToken } from "./auth-fns";
|
||||||
import {
|
import {
|
||||||
@ -158,9 +159,22 @@ export const authLoginServiceFactory = ({
|
|||||||
const userEnc = await userDAL.findUserEncKeyByUsername({
|
const userEnc = await userDAL.findUserEncKeyByUsername({
|
||||||
username: email
|
username: email
|
||||||
});
|
});
|
||||||
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (
|
||||||
|
serverCfg.enabledLoginMethods &&
|
||||||
|
!serverCfg.enabledLoginMethods.includes(LoginMethod.EMAIL) &&
|
||||||
|
!providerAuthToken
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with email is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
|
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
|
||||||
throw new Error("Failed to find user");
|
throw new Error("Failed to find user");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
|
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
|
||||||
validateProviderAuthToken(providerAuthToken as string, email);
|
validateProviderAuthToken(providerAuthToken as string, email);
|
||||||
}
|
}
|
||||||
@ -507,6 +521,40 @@ export const authLoginServiceFactory = ({
|
|||||||
let user = await userDAL.findUserByUsername(email);
|
let user = await userDAL.findUserByUsername(email);
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods) {
|
||||||
|
switch (authMethod) {
|
||||||
|
case AuthMethod.GITHUB: {
|
||||||
|
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GITHUB)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with Github is disabled by administrator.",
|
||||||
|
name: "Oauth 2 login"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AuthMethod.GOOGLE: {
|
||||||
|
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GOOGLE)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with Google is disabled by administrator.",
|
||||||
|
name: "Oauth 2 login"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AuthMethod.GITLAB: {
|
||||||
|
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GITLAB)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with Gitlab is disabled by administrator.",
|
||||||
|
name: "Oauth 2 login"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
@ -364,7 +364,7 @@ export const authSignupServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))];
|
const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))];
|
||||||
await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)));
|
await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId, tx)));
|
||||||
|
|
||||||
await convertPendingGroupAdditionsToGroupMemberships({
|
await convertPendingGroupAdditionsToGroupMemberships({
|
||||||
userIds: [user.id],
|
userIds: [user.id],
|
||||||
|
@ -16,6 +16,7 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
|
|||||||
parentCaId?: string;
|
parentCaId?: string;
|
||||||
encryptedCertificate: Buffer;
|
encryptedCertificate: Buffer;
|
||||||
}[] = await db
|
}[] = await db
|
||||||
|
.replicaNode()
|
||||||
.withRecursive("cte", (cte) => {
|
.withRecursive("cte", (cte) => {
|
||||||
void cte
|
void cte
|
||||||
.select("ca.id as caId", "ca.parentCaId", "cert.encryptedCertificate")
|
.select("ca.id as caId", "ca.parentCaId", "cert.encryptedCertificate")
|
||||||
|
@ -14,7 +14,8 @@ export const certificateDALFactory = (db: TDbClient) => {
|
|||||||
count: string;
|
count: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const count = await db(TableName.Certificate)
|
const count = await db
|
||||||
|
.replicaNode()(TableName.Certificate)
|
||||||
.join(TableName.CertificateAuthority, `${TableName.Certificate}.caId`, `${TableName.CertificateAuthority}.id`)
|
.join(TableName.CertificateAuthority, `${TableName.Certificate}.caId`, `${TableName.CertificateAuthority}.id`)
|
||||||
.join(TableName.Project, `${TableName.CertificateAuthority}.projectId`, `${TableName.Project}.id`)
|
.join(TableName.Project, `${TableName.CertificateAuthority}.projectId`, `${TableName.Project}.id`)
|
||||||
.where(`${TableName.Project}.id`, projectId)
|
.where(`${TableName.Project}.id`, projectId)
|
||||||
|
@ -12,7 +12,7 @@ export const groupProjectDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)(TableName.GroupProjectMembership)
|
const docs = await (tx || db.replicaNode())(TableName.GroupProjectMembership)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||||
.join(TableName.Groups, `${TableName.GroupProjectMembership}.groupId`, `${TableName.Groups}.id`)
|
.join(TableName.Groups, `${TableName.GroupProjectMembership}.groupId`, `${TableName.Groups}.id`)
|
||||||
.join(
|
.join(
|
||||||
|
@ -12,7 +12,7 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findOne = async (filter: Partial<TIdentityAccessTokens>, tx?: Knex) => {
|
const findOne = async (filter: Partial<TIdentityAccessTokens>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.IdentityAccessToken)
|
const doc = await (tx || db.replicaNode())(TableName.IdentityAccessToken)
|
||||||
.where(filter)
|
.where(filter)
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
|
||||||
.leftJoin(TableName.IdentityUaClientSecret, (qb) => {
|
.leftJoin(TableName.IdentityUaClientSecret, (qb) => {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user