mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-23 03:03:05 +00:00
Compare commits
225 Commits
ldap
...
daniel/sam
Author | SHA1 | Date | |
---|---|---|---|
88e2eff7eb | |||
cd192ee228 | |||
1e657968f6 | |||
b8ba51512c | |||
1ac8ddbd92 | |||
a257743fa5 | |||
b5a7240375 | |||
5c2a108c52 | |||
b78d8d28ed | |||
9c9ade52db | |||
4d229ec745 | |||
605dad29ca | |||
bebdad8159 | |||
b547309ae4 | |||
d1ebdbcc03 | |||
c94caa6fb5 | |||
f53fa46c51 | |||
c42d407cda | |||
80b4bc18ec | |||
1dbf80d4e6 | |||
700a072ec5 | |||
8f42914df5 | |||
831da10073 | |||
6904cd3bda | |||
52fd09b87b | |||
0081bbdf9e | |||
c9e5f2bb75 | |||
73cc97cf17 | |||
0c1d37cc75 | |||
60fbd8ac44 | |||
36efa6ba63 | |||
961a73f712 | |||
6e2f3800d4 | |||
258c9e45d4 | |||
8573263379 | |||
9a724db6ab | |||
60a37e784b | |||
14c60bd075 | |||
de715c03ad | |||
ddb1d5a1ab | |||
41323f205d | |||
771498b817 | |||
22b2fb4c98 | |||
9bbba92768 | |||
46eea972f7 | |||
9eb2a74bdf | |||
b80579fdef | |||
214894c88b | |||
8ff37e3ec9 | |||
926f719967 | |||
c3a56f469a | |||
2bd9914373 | |||
354bac486a | |||
ba22a7fca6 | |||
4aef8ab8ee | |||
e89503f00f | |||
f5f20fbdca | |||
4d4887059a | |||
c11c5ec85e | |||
f0e3c9a4b2 | |||
eace4f1bdc | |||
0bd3f32c6e | |||
ad0504e957 | |||
1e20d780ec | |||
7e2685d604 | |||
92fd2d080d | |||
6d60413593 | |||
f59a75d790 | |||
835c36d161 | |||
e4dba6d5c8 | |||
b9986be387 | |||
5f5d62a285 | |||
667fa7a9e3 | |||
27dcb06083 | |||
9b1a15331a | |||
65776b7ab9 | |||
a9c1f278a1 | |||
900facdb36 | |||
fe638ce2c1 | |||
750a43c978 | |||
08b5975f26 | |||
885d1fbd7f | |||
bb2413d659 | |||
dac5529b6c | |||
bd92e35729 | |||
5b7562a76d | |||
edbf459d04 | |||
560274bde8 | |||
7df614a018 | |||
47287be5bf | |||
6e96f2338c | |||
7fd6b63b5d | |||
995777d76f | |||
2a6032a8cf | |||
ec4d1dd1b2 | |||
143de12d67 | |||
52cf937826 | |||
dbd7561037 | |||
d287c3e152 | |||
8fc081973d | |||
c42bbbea8b | |||
29b2b12ec7 | |||
4f80234afa | |||
a1fa0c652d | |||
8327f41b8e | |||
c2bfeb89e8 | |||
4a0668e92e | |||
716e705c2a | |||
f860fd3abe | |||
30e7fe8a45 | |||
307b89e799 | |||
dbf498b44a | |||
5eb3258311 | |||
bd3cbb3c7b | |||
96abbd9f80 | |||
92441e018f | |||
a9bba02f44 | |||
aaca3ac229 | |||
f0383dd55c | |||
a766329de5 | |||
c0b0c0754b | |||
34618041ca | |||
f36a056c62 | |||
e7b11eac2b | |||
0f14fab915 | |||
12a6fba645 | |||
ce057f44ac | |||
2032063c24 | |||
bbceb37d06 | |||
e917b744f4 | |||
7438c114dd | |||
8e3fc044ca | |||
9473de2212 | |||
744c510a51 | |||
f845749a4d | |||
56fc5a2a8c | |||
7edebbabaa | |||
0e698e9355 | |||
00b99e7255 | |||
2b7784718d | |||
5f99e58674 | |||
f77942c702 | |||
2d3fddd0e9 | |||
519b92d592 | |||
c3d5e882f8 | |||
4c354eb3ea | |||
97eff2b480 | |||
c621592807 | |||
bd400a6196 | |||
a93c2d9236 | |||
11dfeda501 | |||
70bd64d54b | |||
0c88a5466c | |||
36266b30d5 | |||
288577b455 | |||
5194be14fd | |||
bab8f95fde | |||
b4f372f883 | |||
b13365ecf5 | |||
bb6e09a895 | |||
715b193a8e | |||
57be493da8 | |||
cc731fe031 | |||
70618420d7 | |||
7feb7ef9c6 | |||
ab1b9fb164 | |||
8c028889a6 | |||
7dc366baf0 | |||
2124d2669f | |||
af83fbea14 | |||
9657b64ab2 | |||
90b55a94e1 | |||
d83d249f29 | |||
151787c60a | |||
ce443b114c | |||
2ca03abec2 | |||
c8bb690736 | |||
6efbdaef9c | |||
7e90493cce | |||
1330c0455a | |||
407248c616 | |||
a6d7d32156 | |||
0f0e2b360c | |||
47906c4dd4 | |||
fc57884035 | |||
4152b3a524 | |||
f1f18e81cd | |||
929f91a738 | |||
fa41b8bb47 | |||
edbb7e2b1e | |||
1d53e0f21b | |||
a232450f20 | |||
6f65f2a63d | |||
9545960e6f | |||
cfa42017b1 | |||
1b74fdb232 | |||
ad1cae6aac | |||
e5d4328e2a | |||
635948c4f4 | |||
d6231d4649 | |||
041535bb47 | |||
3f0c4f0ca9 | |||
624fb3d46a | |||
8a27b1b5e6 | |||
972b80e790 | |||
6cc0d79d8a | |||
f9b6f78e8d | |||
2852a495c8 | |||
6305300b12 | |||
b4ae1e8f3b | |||
db9f21be87 | |||
449617d271 | |||
6b92a5f4db | |||
81e961e8bc | |||
6a7a6ce942 | |||
1695412278 | |||
b4fa07334d | |||
29c244c635 | |||
b80a5989a8 | |||
dc696f8932 | |||
c8f0796952 | |||
9282dd08d9 | |||
df459d456a | |||
c8cfb43316 | |||
541fa10964 |
backend
e2e-test
package-lock.jsonpackage.jsonsrc
@types
db
ee
routes/v1
ldap-router.tslicense-router.tsorg-role-router.tsproject-role-router.tsproject-router.tssaml-router.tsscim-router.tssecret-approval-policy-router.tssecret-approval-request-router.tssecret-rotation-provider-router.tssecret-rotation-router.tssecret-scanning-router.tssecret-version-router.tssnapshot-router.tstrusted-ip-router.ts
services
audit-log
ldap-config
license
permission
saml-config
scim
secret-approval-policy
secret-approval-request
secret-rotation
secret-scanning
secret-snapshot
trusted-ip
lib
server
plugins/auth
routes
index.ts
v1
auth-router.tsbot-router.tsidentity-access-token-router.tsidentity-router.tsidentity-ua.tsintegration-auth-router.tsintegration-router.tsinvite-org-router.tsorganization-router.tsproject-env-router.tsproject-key-router.tsproject-membership-router.tsproject-router.tssecret-folder-router.tssecret-import-router.tssecret-tag-router.tsuser-router.tswebhook-router.ts
v2
identity-org-router.tsidentity-project-router.tsmfa-router.tsorganization-router.tsproject-membership-router.tsproject-router.tsservice-token-router.tsuser-router.ts
v3
services
auth
identity-access-token
identity-project
identity-project-dal.tsidentity-project-membership-role-dal.tsidentity-project-service.tsidentity-project-types.ts
identity-ua
identity
integration-auth
integration-app-list.tsintegration-auth-service.tsintegration-auth-types.tsintegration-sync-secret.ts
integration
org
project-bot
project-env
project-key
project-membership
project-membership-dal.tsproject-membership-service.tsproject-membership-types.tsproject-user-membership-role-dal.ts
project-role
project
secret-blind-index
secret-folder
secret-import
secret-tag
secret
service-token
super-admin
webhook
cli/packages
docs
api-reference/endpoints/folders
cli
documentation
images
integrations/github
integrations-github-scope-env.pngintegrations-github-scope-org.pngintegrations-github-scope-repo.pngintegrations-github.png
platform/secret-rotation/aws-iam
rotation-config-1.pngrotation-config-2.pngrotation-config-secrets.pngrotation-manager-access-key-third-party.pngrotation-manager-access-keys.pngrotation-manager-attach-policy.pngrotation-manager-create-access-key.pngrotation-manager-create-policy.pngrotation-manager-create-user.pngrotation-manager-policy-review.pngrotation-manager-user-review.pngrotation-manager-username.pngrotations-aws-iam-user.pngrotations-select-aws-iam-user.png
infisical-agent
integrations/cicd
mint.jsonsdks/languages
self-hosting
frontend
public/images/secretRotation
src
components
navigation
permissions
signup
utilities
v2
config
helpers
hooks/api
auth
identities
integrationAuth
integrations
organization
users
workspace
layouts/AppLayout
lib/fn
pages
services
views
IntegrationsPage
Login
Org/components
Project/MembersPage
SecretOverviewPage
SecretRotationPage
Settings
OrgSettingsPage
OrgSettingsPage.tsx
components
OrgAuthTab
LDAPModal.tsxOrgAuthTab.tsxOrgGeneralAuthSection.tsxOrgLDAPSection.tsxOrgSCIMSection.tsxOrgSSOSection.tsxSSOModal.tsx
OrgDeleteSection
OrgGeneralTab
OrgIncidentContactsSection
OrgNameChangeSection
OrgSlugChangeSection
OrgTabGroup
PersonalSettingsPage
APIKeySection
APIKeyV2Section
PersonalAPIKeyTab
PersonalTabGroup
ProjectSettingsPage/components
Signup/components/UserInfoSSOStep
admin/SignUpPage
@ -10,7 +10,7 @@ import { seedData1 } from "@app/db/seed-data";
|
||||
import { initEnvConfig } from "@app/lib/config/env";
|
||||
import { initLogger } from "@app/lib/logger";
|
||||
import { main } from "@app/server/app";
|
||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
|
||||
import { mockQueue } from "./mocks/queue";
|
||||
import { mockSmtpServer } from "./mocks/smtp";
|
||||
@ -52,6 +52,8 @@ export default {
|
||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||
userId: seedData1.id,
|
||||
tokenVersionId: seedData1.token.id,
|
||||
authMethod: AuthMethod.EMAIL,
|
||||
organizationId: seedData1.organization.id,
|
||||
accessVersion: 1
|
||||
},
|
||||
cfg.AUTH_SECRET,
|
||||
|
867
backend/package-lock.json
generated
867
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -70,6 +70,7 @@
|
||||
"vitest": "^1.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-iam": "^3.525.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
||||
"@casl/ability": "^6.5.0",
|
||||
"@fastify/cookie": "^9.3.1",
|
||||
@ -106,6 +107,7 @@
|
||||
"knex": "^3.0.1",
|
||||
"libsodium-wrappers": "^0.7.13",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"ms": "^2.1.3",
|
||||
"mysql2": "^3.9.1",
|
||||
"nanoid": "^5.0.4",
|
||||
"nodemailer": "^6.9.9",
|
||||
@ -115,6 +117,7 @@
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-ldapauth": "^3.0.1",
|
||||
"pg": "^8.11.3",
|
||||
"pg-query-stream": "^4.5.3",
|
||||
"picomatch": "^3.0.1",
|
||||
"pino": "^8.16.2",
|
||||
"posthog-node": "^3.6.2",
|
||||
|
3
backend/src/@types/fastify.d.ts
vendored
3
backend/src/@types/fastify.d.ts
vendored
@ -19,7 +19,7 @@ import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||
import { TAuthLoginFactory } from "@app/services/auth/auth-login-service";
|
||||
import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
|
||||
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
||||
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
||||
@ -59,6 +59,7 @@ declare module "fastify" {
|
||||
// identity injection. depending on which kinda of token the information is filled in auth
|
||||
auth: TAuthMode;
|
||||
permission: {
|
||||
authMethod: ActorAuthMethod;
|
||||
type: ActorType;
|
||||
id: string;
|
||||
orgId?: string;
|
||||
|
16
backend/src/@types/knex.d.ts
vendored
16
backend/src/@types/knex.d.ts
vendored
@ -32,6 +32,9 @@ import {
|
||||
TIdentityOrgMemberships,
|
||||
TIdentityOrgMembershipsInsert,
|
||||
TIdentityOrgMembershipsUpdate,
|
||||
TIdentityProjectMembershipRole,
|
||||
TIdentityProjectMembershipRoleInsert,
|
||||
TIdentityProjectMembershipRoleUpdate,
|
||||
TIdentityProjectMemberships,
|
||||
TIdentityProjectMembershipsInsert,
|
||||
TIdentityProjectMembershipsUpdate,
|
||||
@ -83,6 +86,9 @@ import {
|
||||
TProjects,
|
||||
TProjectsInsert,
|
||||
TProjectsUpdate,
|
||||
TProjectUserMembershipRoles,
|
||||
TProjectUserMembershipRolesInsert,
|
||||
TProjectUserMembershipRolesUpdate,
|
||||
TSamlConfigs,
|
||||
TSamlConfigsInsert,
|
||||
TSamlConfigsUpdate,
|
||||
@ -221,6 +227,11 @@ declare module "knex/types/tables" {
|
||||
TProjectEnvironmentsUpdate
|
||||
>;
|
||||
[TableName.ProjectBot]: Knex.CompositeTableType<TProjectBots, TProjectBotsInsert, TProjectBotsUpdate>;
|
||||
[TableName.ProjectUserMembershipRole]: Knex.CompositeTableType<
|
||||
TProjectUserMembershipRoles,
|
||||
TProjectUserMembershipRolesInsert,
|
||||
TProjectUserMembershipRolesUpdate
|
||||
>;
|
||||
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
|
||||
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
||||
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
||||
@ -272,6 +283,11 @@ declare module "knex/types/tables" {
|
||||
TIdentityProjectMembershipsInsert,
|
||||
TIdentityProjectMembershipsUpdate
|
||||
>;
|
||||
[TableName.IdentityProjectMembershipRole]: Knex.CompositeTableType<
|
||||
TIdentityProjectMembershipRole,
|
||||
TIdentityProjectMembershipRoleInsert,
|
||||
TIdentityProjectMembershipRoleUpdate
|
||||
>;
|
||||
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
||||
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
||||
TSecretApprovalPolicies,
|
||||
|
@ -6,6 +6,13 @@ export const initDbConnection = ({ dbConnectionUri, dbRootCert }: { dbConnection
|
||||
client: "pg",
|
||||
connection: {
|
||||
connectionString: dbConnectionUri,
|
||||
host: process.env.DB_HOST,
|
||||
// @ts-expect-error I have no clue why only for the port there is a type error
|
||||
// eslint-disable-next-line
|
||||
port: process.env.DB_PORT,
|
||||
user: process.env.DB_USER,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
ssl: dbRootCert
|
||||
? {
|
||||
rejectUnauthorized: true,
|
||||
|
@ -7,18 +7,22 @@ import path from "path";
|
||||
|
||||
// Update with your config settings. .
|
||||
dotenv.config({
|
||||
path: path.join(__dirname, "../../../.env.migration"),
|
||||
debug: true
|
||||
path: path.join(__dirname, "../../../.env.migration")
|
||||
});
|
||||
dotenv.config({
|
||||
path: path.join(__dirname, "../../../.env"),
|
||||
debug: true
|
||||
path: path.join(__dirname, "../../../.env")
|
||||
});
|
||||
|
||||
export default {
|
||||
development: {
|
||||
client: "postgres",
|
||||
connection: {
|
||||
connectionString: process.env.DB_CONNECTION_URI,
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
user: process.env.DB_USER,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
ssl: process.env.DB_ROOT_CERT
|
||||
? {
|
||||
rejectUnauthorized: true,
|
||||
@ -41,6 +45,11 @@ export default {
|
||||
client: "postgres",
|
||||
connection: {
|
||||
connectionString: process.env.DB_CONNECTION_URI,
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
user: process.env.DB_USER,
|
||||
database: process.env.DB_NAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
ssl: process.env.DB_ROOT_CERT
|
||||
? {
|
||||
rejectUnauthorized: true,
|
||||
|
50
backend/src/db/migrations/20240312162549_temp-roles.ts
Normal file
50
backend/src/db/migrations/20240312162549_temp-roles.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const doesTableExist = await knex.schema.hasTable(TableName.ProjectUserMembershipRole);
|
||||
if (!doesTableExist) {
|
||||
await knex.schema.createTable(TableName.ProjectUserMembershipRole, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("role").notNullable();
|
||||
t.uuid("projectMembershipId").notNullable();
|
||||
t.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||
// until role is changed/removed the role should not deleted
|
||||
t.uuid("customRoleId");
|
||||
t.foreign("customRoleId").references("id").inTable(TableName.ProjectRoles);
|
||||
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||
t.string("temporaryMode");
|
||||
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||
t.datetime("temporaryAccessStartTime");
|
||||
t.datetime("temporaryAccessEndTime");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.ProjectUserMembershipRole);
|
||||
|
||||
const projectMemberships = await knex(TableName.ProjectMembership).select(
|
||||
"id",
|
||||
"role",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
knex.ref("roleId").withSchema(TableName.ProjectMembership).as("customRoleId")
|
||||
);
|
||||
if (projectMemberships.length)
|
||||
await knex.batchInsert(
|
||||
TableName.ProjectUserMembershipRole,
|
||||
projectMemberships.map((data) => ({ ...data, projectMembershipId: data.id }))
|
||||
);
|
||||
// will be dropped later
|
||||
// await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
|
||||
// t.dropColumn("roleId");
|
||||
// t.dropColumn("role");
|
||||
// });
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.ProjectUserMembershipRole);
|
||||
await dropOnUpdateTrigger(knex, TableName.ProjectUserMembershipRole);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const doesTableExist = await knex.schema.hasTable(TableName.IdentityProjectMembershipRole);
|
||||
if (!doesTableExist) {
|
||||
await knex.schema.createTable(TableName.IdentityProjectMembershipRole, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("role").notNullable();
|
||||
t.uuid("projectMembershipId").notNullable();
|
||||
t.foreign("projectMembershipId")
|
||||
.references("id")
|
||||
.inTable(TableName.IdentityProjectMembership)
|
||||
.onDelete("CASCADE");
|
||||
// until role is changed/removed the role should not deleted
|
||||
t.uuid("customRoleId");
|
||||
t.foreign("customRoleId").references("id").inTable(TableName.ProjectRoles);
|
||||
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||
t.string("temporaryMode");
|
||||
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||
t.datetime("temporaryAccessStartTime");
|
||||
t.datetime("temporaryAccessEndTime");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.IdentityProjectMembershipRole);
|
||||
|
||||
const identityMemberships = await knex(TableName.IdentityProjectMembership).select(
|
||||
"id",
|
||||
"role",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
knex.ref("roleId").withSchema(TableName.IdentityProjectMembership).as("customRoleId")
|
||||
);
|
||||
if (identityMemberships.length)
|
||||
await knex.batchInsert(
|
||||
TableName.IdentityProjectMembershipRole,
|
||||
identityMemberships.map((data) => ({ ...data, projectMembershipId: data.id }))
|
||||
);
|
||||
// await knex.schema.alterTable(TableName.IdentityProjectMembership, (t) => {
|
||||
// t.dropColumn("roleId");
|
||||
// t.dropColumn("role");
|
||||
// });
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.IdentityProjectMembershipRole);
|
||||
await dropOnUpdateTrigger(knex, TableName.IdentityProjectMembershipRole);
|
||||
}
|
31
backend/src/db/schemas/identity-project-membership-role.ts
Normal file
31
backend/src/db/schemas/identity-project-membership-role.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const IdentityProjectMembershipRoleSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
role: z.string(),
|
||||
projectMembershipId: z.string().uuid(),
|
||||
customRoleId: z.string().uuid().nullable().optional(),
|
||||
isTemporary: z.boolean().default(false),
|
||||
temporaryMode: z.string().nullable().optional(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TIdentityProjectMembershipRole = z.infer<typeof IdentityProjectMembershipRoleSchema>;
|
||||
export type TIdentityProjectMembershipRoleInsert = Omit<
|
||||
z.input<typeof IdentityProjectMembershipRoleSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TIdentityProjectMembershipRoleUpdate = Partial<
|
||||
Omit<z.input<typeof IdentityProjectMembershipRoleSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -8,6 +8,7 @@ export * from "./git-app-org";
|
||||
export * from "./identities";
|
||||
export * from "./identity-access-tokens";
|
||||
export * from "./identity-org-memberships";
|
||||
export * from "./identity-project-membership-role";
|
||||
export * from "./identity-project-memberships";
|
||||
export * from "./identity-ua-client-secrets";
|
||||
export * from "./identity-universal-auths";
|
||||
@ -25,6 +26,7 @@ export * from "./project-environments";
|
||||
export * from "./project-keys";
|
||||
export * from "./project-memberships";
|
||||
export * from "./project-roles";
|
||||
export * from "./project-user-membership-roles";
|
||||
export * from "./projects";
|
||||
export * from "./saml-configs";
|
||||
export * from "./scim-tokens";
|
||||
|
@ -20,6 +20,7 @@ export enum TableName {
|
||||
Environment = "project_environments",
|
||||
ProjectMembership = "project_memberships",
|
||||
ProjectRoles = "project_roles",
|
||||
ProjectUserMembershipRole = "project_user_membership_roles",
|
||||
ProjectKeys = "project_keys",
|
||||
Secret = "secrets",
|
||||
SecretBlindIndex = "secret_blind_indexes",
|
||||
@ -41,6 +42,7 @@ export enum TableName {
|
||||
IdentityUaClientSecret = "identity_ua_client_secrets",
|
||||
IdentityOrgMembership = "identity_org_memberships",
|
||||
IdentityProjectMembership = "identity_project_memberships",
|
||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||
ScimToken = "scim_tokens",
|
||||
SecretApprovalPolicy = "secret_approval_policies",
|
||||
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
||||
|
31
backend/src/db/schemas/project-user-membership-roles.ts
Normal file
31
backend/src/db/schemas/project-user-membership-roles.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const ProjectUserMembershipRolesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
role: z.string(),
|
||||
projectMembershipId: z.string().uuid(),
|
||||
customRoleId: z.string().uuid().nullable().optional(),
|
||||
isTemporary: z.boolean().default(false),
|
||||
temporaryMode: z.string().nullable().optional(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TProjectUserMembershipRoles = z.infer<typeof ProjectUserMembershipRolesSchema>;
|
||||
export type TProjectUserMembershipRolesInsert = Omit<
|
||||
z.input<typeof ProjectUserMembershipRolesSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TProjectUserMembershipRolesUpdate = Partial<
|
||||
Omit<z.input<typeof ProjectUserMembershipRolesSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -4,7 +4,7 @@ import { Knex } from "knex";
|
||||
|
||||
import { encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
|
||||
import { OrgMembershipRole, SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
|
||||
import { ProjectMembershipRole, SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
|
||||
import { buildUserProjectKey, getUserPrivateKey, seedData1 } from "../seed-data";
|
||||
|
||||
export const DEFAULT_PROJECT_ENVS = [
|
||||
@ -30,10 +30,16 @@ export async function seed(knex: Knex): Promise<void> {
|
||||
})
|
||||
.returning("*");
|
||||
|
||||
await knex(TableName.ProjectMembership).insert({
|
||||
projectId: project.id,
|
||||
role: OrgMembershipRole.Admin,
|
||||
userId: seedData1.id
|
||||
const projectMembership = await knex(TableName.ProjectMembership)
|
||||
.insert({
|
||||
projectId: project.id,
|
||||
userId: seedData1.id,
|
||||
role: ProjectMembershipRole.Admin
|
||||
})
|
||||
.returning("*");
|
||||
await knex(TableName.ProjectUserMembershipRole).insert({
|
||||
role: ProjectMembershipRole.Admin,
|
||||
projectMembershipId: projectMembership[0].id
|
||||
});
|
||||
|
||||
const user = await knex(TableName.UserEncryptionKey).where({ userId: seedData1.id }).first();
|
||||
|
@ -75,9 +75,16 @@ export async function seed(knex: Knex): Promise<void> {
|
||||
}
|
||||
]);
|
||||
|
||||
await knex(TableName.IdentityProjectMembership).insert({
|
||||
identityId: seedData1.machineIdentity.id,
|
||||
const identityProjectMembership = await knex(TableName.IdentityProjectMembership)
|
||||
.insert({
|
||||
identityId: seedData1.machineIdentity.id,
|
||||
projectId: seedData1.project.id,
|
||||
role: ProjectMembershipRole.Admin
|
||||
})
|
||||
.returning("*");
|
||||
|
||||
await knex(TableName.IdentityProjectMembershipRole).insert({
|
||||
role: ProjectMembershipRole.Admin,
|
||||
projectId: seedData1.project.id
|
||||
projectMembershipId: identityProjectMembership[0].id
|
||||
});
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.query.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
return ldap;
|
||||
@ -151,6 +152,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.body.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body
|
||||
});
|
||||
@ -164,15 +166,17 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
method: "PATCH",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
organizationId: z.string().trim(),
|
||||
isActive: z.boolean().optional(),
|
||||
url: z.string().trim().optional(),
|
||||
bindDN: z.string().trim().optional(),
|
||||
bindPass: z.string().trim().optional(),
|
||||
searchBase: z.string().trim().optional(),
|
||||
caCert: z.string().trim().optional()
|
||||
}),
|
||||
body: z
|
||||
.object({
|
||||
isActive: z.boolean(),
|
||||
url: z.string().trim(),
|
||||
bindDN: z.string().trim(),
|
||||
bindPass: z.string().trim(),
|
||||
searchBase: z.string().trim(),
|
||||
caCert: z.string().trim()
|
||||
})
|
||||
.partial()
|
||||
.merge(z.object({ organizationId: z.string() })),
|
||||
response: {
|
||||
200: LdapConfigsSchema
|
||||
}
|
||||
@ -182,6 +186,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.body.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
billingCycle: req.query.billingCycle
|
||||
});
|
||||
return data;
|
||||
@ -45,6 +46,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return { plan };
|
||||
@ -66,6 +68,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
const data = await server.services.license.getOrgPlan({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return data;
|
||||
@ -89,6 +93,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
success_url: req.body.success_url
|
||||
});
|
||||
return data;
|
||||
@ -110,6 +115,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return data;
|
||||
@ -131,6 +137,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return data;
|
||||
@ -152,6 +159,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return data;
|
||||
@ -173,6 +181,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return data;
|
||||
@ -198,6 +207,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId,
|
||||
name: req.body.name,
|
||||
email: req.body.email
|
||||
@ -221,6 +231,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return data;
|
||||
@ -246,6 +257,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId,
|
||||
success_url: req.body.success_url,
|
||||
cancel_url: req.body.cancel_url
|
||||
@ -271,6 +283,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
const data = await server.services.license.delOrgPmtMethods({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId,
|
||||
pmtMethodId: req.params.pmtMethodId
|
||||
@ -295,6 +308,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
const data = await server.services.license.getOrgTaxIds({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
@ -322,6 +336,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
const data = await server.services.license.addOrgTaxId({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId,
|
||||
type: req.body.type,
|
||||
@ -348,6 +363,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
const data = await server.services.license.delOrgTaxId({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId,
|
||||
taxId: req.params.taxId
|
||||
@ -373,7 +389,8 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId
|
||||
orgId: req.params.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod
|
||||
});
|
||||
return data;
|
||||
}
|
||||
@ -396,6 +413,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
return data;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { z } from "zod";
|
||||
|
||||
import { OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -13,7 +14,17 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
slug: z.string().trim(),
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.trim()
|
||||
.refine(
|
||||
(val) => Object.keys(OrgMembershipRole).includes(val),
|
||||
"Please choose a different slug, the slug you have entered is reserved"
|
||||
)
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid"
|
||||
}),
|
||||
name: z.string().trim(),
|
||||
description: z.string().trim().optional(),
|
||||
permissions: z.any().array()
|
||||
@ -30,6 +41,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.body,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
@ -45,7 +57,17 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
roleId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
slug: z.string().trim().optional(),
|
||||
slug: z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.refine(
|
||||
(val) => typeof val === "undefined" || Object.keys(OrgMembershipRole).includes(val),
|
||||
"Please choose a different slug, the slug you have entered is reserved."
|
||||
)
|
||||
.refine((val) => typeof val === "undefined" || slugify(val) === val, {
|
||||
message: "Slug must be a valid"
|
||||
}),
|
||||
name: z.string().trim().optional(),
|
||||
description: z.string().trim().optional(),
|
||||
permissions: z.any().array()
|
||||
@ -63,6 +85,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.params.organizationId,
|
||||
req.params.roleId,
|
||||
req.body,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
@ -89,6 +112,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.params.roleId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
@ -117,6 +141,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
const roles = await server.services.orgRole.listRoles(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { data: { roles } };
|
||||
@ -142,6 +167,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
const { permissions, membership } = await server.services.orgRole.getUserPermission(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { permissions, membership };
|
||||
|
@ -31,6 +31,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.body,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
@ -65,6 +66,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.params.projectId,
|
||||
req.params.roleId,
|
||||
req.body,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
@ -92,6 +94,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.params.roleId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
@ -121,6 +124,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.type,
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { data: { roles } };
|
||||
@ -148,6 +152,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
const { permissions, membership } = await server.services.projectRole.getUserPermission(
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { data: { permissions, membership } };
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
|
||||
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -19,13 +20,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(PROJECTS.GET_SNAPSHOTS.workspaceId)
|
||||
}),
|
||||
querystring: z.object({
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
offset: z.coerce.number().default(0),
|
||||
limit: z.coerce.number().default(20)
|
||||
environment: z.string().trim().describe(PROJECTS.GET_SNAPSHOTS.environment),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(PROJECTS.GET_SNAPSHOTS.path),
|
||||
offset: z.coerce.number().default(0).describe(PROJECTS.GET_SNAPSHOTS.offset),
|
||||
limit: z.coerce.number().default(20).describe(PROJECTS.GET_SNAPSHOTS.limit)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -37,6 +38,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
handler: async (req) => {
|
||||
const secretSnapshots = await server.services.snapshot.listSnapshots({
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
@ -68,6 +70,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
const count = await server.services.snapshot.projectSecretSnapshotCount({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
environment: req.query.environment,
|
||||
@ -89,16 +92,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(AUDIT_LOGS.EXPORT.workspaceId)
|
||||
}),
|
||||
querystring: z.object({
|
||||
eventType: z.nativeEnum(EventType).optional(),
|
||||
userAgentType: z.nativeEnum(UserAgentType).optional(),
|
||||
startDate: z.string().datetime().optional(),
|
||||
endDate: z.string().datetime().optional(),
|
||||
offset: z.coerce.number().default(0),
|
||||
limit: z.coerce.number().default(20),
|
||||
actor: z.string().optional()
|
||||
eventType: z.nativeEnum(EventType).optional().describe(AUDIT_LOGS.EXPORT.eventType),
|
||||
userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType),
|
||||
startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate),
|
||||
endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate),
|
||||
offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset),
|
||||
limit: z.coerce.number().default(20).describe(AUDIT_LOGS.EXPORT.limit),
|
||||
actor: z.string().optional().describe(AUDIT_LOGS.EXPORT.actor)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -129,6 +132,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
const auditLogs = await server.services.auditLog.listProjectAuditLogs({
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.params.workspaceId,
|
||||
...req.query,
|
||||
auditLogActor: req.query.actor,
|
||||
|
@ -231,6 +231,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.query.organizationId,
|
||||
type: "org"
|
||||
});
|
||||
@ -259,6 +260,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
const saml = await server.services.saml.createSamlCfg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.body.organizationId,
|
||||
...req.body
|
||||
@ -290,6 +292,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
const saml = await server.services.saml.updateSamlCfg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.body.organizationId,
|
||||
...req.body
|
||||
|
@ -39,6 +39,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.body.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
description: req.body.description,
|
||||
ttlDays: req.body.ttlDays
|
||||
});
|
||||
@ -65,6 +66,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
const scimTokens = await server.services.scim.listScimTokens({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.query.organizationId
|
||||
});
|
||||
@ -92,6 +94,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
scimTokenId: req.params.scimTokenId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
|
@ -34,6 +34,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
const approval = await server.services.secretApprovalPolicy.createSecretApprovalPolicy({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.body.workspaceId,
|
||||
...req.body,
|
||||
@ -72,6 +73,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
const approval = await server.services.secretApprovalPolicy.updateSecretApprovalPolicy({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
secretPolicyId: req.params.sapId
|
||||
@ -98,6 +100,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
const approval = await server.services.secretApprovalPolicy.deleteSecretApprovalPolicy({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPolicyId: req.params.sapId
|
||||
});
|
||||
@ -123,6 +126,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
const approvals = await server.services.secretApprovalPolicy.getSecretApprovalPolicyByProjectId({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.query.workspaceId
|
||||
});
|
||||
@ -150,6 +154,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.query.workspaceId,
|
||||
...req.query
|
||||
|
@ -52,6 +52,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
const approvals = await server.services.secretApprovalRequest.getSecretApprovals({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query,
|
||||
projectId: req.query.workspaceId
|
||||
@ -81,6 +82,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
const approvals = await server.services.secretApprovalRequest.requestCount({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.query.workspaceId
|
||||
});
|
||||
@ -106,6 +108,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
const { approval } = await server.services.secretApprovalRequest.mergeSecretApprovalRequest({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
approvalId: req.params.id
|
||||
});
|
||||
@ -134,6 +137,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
const review = await server.services.secretApprovalRequest.reviewApproval({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
approvalId: req.params.id,
|
||||
status: req.body.status
|
||||
@ -163,6 +167,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
const approval = await server.services.secretApprovalRequest.updateApprovalStatus({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
approvalId: req.params.id,
|
||||
status: req.body.status
|
||||
@ -271,6 +276,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
const approval = await server.services.secretApprovalRequest.getSecretApprovalDetails({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.id
|
||||
});
|
||||
|
@ -30,6 +30,7 @@ export const registerSecretRotationProviderRouter = async (server: FastifyZodPro
|
||||
const providers = await server.services.secretRotation.getProviderTemplates({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
|
@ -39,6 +39,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
||||
handler: async (req) => {
|
||||
const secretRotation = await server.services.secretRotation.createRotation({
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
@ -74,6 +75,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
||||
const secretRotation = await server.services.secretRotation.restartById({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
rotationId: req.body.id
|
||||
});
|
||||
@ -125,6 +127,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
||||
const secretRotations = await server.services.secretRotation.getByProjectId({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.query.workspaceId
|
||||
});
|
||||
@ -158,6 +161,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
||||
const secretRotation = await server.services.secretRotation.deleteById({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
rotationId: req.params.id
|
||||
});
|
||||
|
@ -22,6 +22,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
const session = await server.services.secretScanning.createInstallationSession({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.body.organizationId
|
||||
});
|
||||
@ -46,6 +47,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
const { installatedApp } = await server.services.secretScanning.linkInstallationToOrg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body
|
||||
});
|
||||
@ -67,6 +69,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
const appInstallationCompleted = await server.services.secretScanning.getOrgInstallationStatus({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
@ -88,6 +91,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
const { risks } = await server.services.secretScanning.getRisksByOrg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
@ -110,6 +114,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
const { risk } = await server.services.secretScanning.updateRiskStatus({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.organizationId,
|
||||
riskId: req.params.riskId,
|
||||
|
@ -27,6 +27,7 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) =>
|
||||
const secretVersions = await server.services.secret.getSecretVersions({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
limit: req.query.limit,
|
||||
offset: req.query.offset,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -46,6 +47,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
||||
const secretSnapshot = await server.services.snapshot.getSnapshotData({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.secretSnapshotId
|
||||
});
|
||||
@ -65,7 +67,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
secretSnapshotId: z.string().trim()
|
||||
secretSnapshotId: z.string().trim().describe(PROJECTS.ROLLBACK_TO_SNAPSHOT.secretSnapshotId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -78,6 +80,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
||||
const secretSnapshot = await server.services.snapshot.rollbackSnapshot({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.secretSnapshotId
|
||||
});
|
||||
|
@ -22,6 +22,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const trustedIps = await server.services.trustedIp.listIpsByProjectId({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.params.workspaceId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
@ -52,6 +53,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { trustedIp, project } = await server.services.trustedIp.addProjectIp({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.params.workspaceId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
@ -99,6 +101,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.params.workspaceId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
trustedIpId: req.params.trustedIpId,
|
||||
...req.body
|
||||
@ -140,6 +143,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.params.workspaceId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
trustedIpId: req.params.trustedIpId
|
||||
});
|
||||
|
@ -31,10 +31,17 @@ export const auditLogServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
projectId,
|
||||
auditLogActor
|
||||
}: TListProjectAuditLogDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||
const auditLogs = await auditLogDAL.find({
|
||||
startDate,
|
||||
|
@ -55,6 +55,7 @@ export const ldapConfigServiceFactory = ({
|
||||
actorId,
|
||||
orgId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
isActive,
|
||||
url,
|
||||
bindDN,
|
||||
@ -62,7 +63,7 @@ export const ldapConfigServiceFactory = ({
|
||||
searchBase,
|
||||
caCert
|
||||
}: TCreateLdapCfgDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
||||
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
@ -149,13 +150,14 @@ export const ldapConfigServiceFactory = ({
|
||||
orgId,
|
||||
actorOrgId,
|
||||
isActive,
|
||||
actorAuthMethod,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}: TUpdateLdapCfgDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
|
||||
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
@ -180,21 +182,21 @@ export const ldapConfigServiceFactory = ({
|
||||
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
if (bindDN) {
|
||||
if (bindDN !== undefined) {
|
||||
const { ciphertext: encryptedBindDN, iv: bindDNIV, tag: bindDNTag } = encryptSymmetric(bindDN, key);
|
||||
updateQuery.encryptedBindDN = encryptedBindDN;
|
||||
updateQuery.bindDNIV = bindDNIV;
|
||||
updateQuery.bindDNTag = bindDNTag;
|
||||
}
|
||||
|
||||
if (bindPass) {
|
||||
if (bindPass !== undefined) {
|
||||
const { ciphertext: encryptedBindPass, iv: bindPassIV, tag: bindPassTag } = encryptSymmetric(bindPass, key);
|
||||
updateQuery.encryptedBindPass = encryptedBindPass;
|
||||
updateQuery.bindPassIV = bindPassIV;
|
||||
updateQuery.bindPassTag = bindPassTag;
|
||||
}
|
||||
|
||||
if (caCert) {
|
||||
if (caCert !== undefined) {
|
||||
const { ciphertext: encryptedCACert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key);
|
||||
updateQuery.encryptedCACert = encryptedCACert;
|
||||
updateQuery.caCertIV = caCertIV;
|
||||
@ -274,8 +276,14 @@ export const ldapConfigServiceFactory = ({
|
||||
};
|
||||
};
|
||||
|
||||
const getLdapCfgWithPermissionCheck = async ({ actor, actorId, orgId, actorOrgId }: TOrgPermission) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getLdapCfgWithPermissionCheck = async ({
|
||||
actor,
|
||||
actorId,
|
||||
orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TOrgPermission) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
|
||||
return getLdapCfg({
|
||||
orgId
|
||||
|
@ -8,6 +8,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { verifyOfflineLicense } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
@ -26,6 +27,7 @@ import {
|
||||
TFeatureSet,
|
||||
TGetOrgBillInfoDTO,
|
||||
TGetOrgTaxIdDTO,
|
||||
TOfflineLicenseContents,
|
||||
TOrgInvoiceDTO,
|
||||
TOrgLicensesDTO,
|
||||
TOrgPlanDTO,
|
||||
@ -96,6 +98,36 @@ export const licenseServiceFactory = ({
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (appCfg.LICENSE_KEY_OFFLINE) {
|
||||
let isValidOfflineLicense = true;
|
||||
const contents: TOfflineLicenseContents = JSON.parse(
|
||||
Buffer.from(appCfg.LICENSE_KEY_OFFLINE, "base64").toString("utf8")
|
||||
);
|
||||
const isVerified = await verifyOfflineLicense(JSON.stringify(contents.license), contents.signature);
|
||||
|
||||
if (!isVerified) {
|
||||
isValidOfflineLicense = false;
|
||||
logger.warn(`Infisical EE offline license verification failed`);
|
||||
}
|
||||
|
||||
if (contents.license.terminatesAt) {
|
||||
const terminationDate = new Date(contents.license.terminatesAt);
|
||||
if (terminationDate < new Date()) {
|
||||
isValidOfflineLicense = false;
|
||||
logger.warn(`Infisical EE offline license has expired`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isValidOfflineLicense) {
|
||||
onPremFeatures = contents.license.features;
|
||||
instanceType = InstanceType.EnterpriseOnPrem;
|
||||
logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`);
|
||||
isValidLicense = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this means this is self hosted oss version
|
||||
// else it would reach catch statement
|
||||
isValidLicense = true;
|
||||
@ -192,9 +224,10 @@ export const licenseServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
billingCycle
|
||||
}: TOrgPlansTableDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
const { data } = await licenseServerCloudApi.request.get(
|
||||
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
||||
@ -202,15 +235,22 @@ export const licenseServiceFactory = ({
|
||||
return data;
|
||||
};
|
||||
|
||||
const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, projectId }: TOrgPlanDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, actorAuthMethod, projectId }: TOrgPlanDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
const plan = await getPlan(orgId, projectId);
|
||||
return plan;
|
||||
};
|
||||
|
||||
const startOrgTrial = async ({ orgId, actorId, actor, actorOrgId, success_url }: TStartOrgTrialDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const startOrgTrial = async ({
|
||||
orgId,
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
success_url
|
||||
}: TStartOrgTrialDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
|
||||
|
||||
@ -231,8 +271,14 @@ export const licenseServiceFactory = ({
|
||||
return { url };
|
||||
};
|
||||
|
||||
const createOrganizationPortalSession = async ({ orgId, actorId, actor, actorOrgId }: TCreateOrgPortalSession) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const createOrganizationPortalSession = async ({
|
||||
orgId,
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TCreateOrgPortalSession) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
|
||||
|
||||
@ -278,8 +324,8 @@ export const licenseServiceFactory = ({
|
||||
return { url };
|
||||
};
|
||||
|
||||
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -295,8 +341,8 @@ export const licenseServiceFactory = ({
|
||||
};
|
||||
|
||||
// returns org current plan feature table
|
||||
const getOrgPlanTable = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -311,8 +357,8 @@ export const licenseServiceFactory = ({
|
||||
return data;
|
||||
};
|
||||
|
||||
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -332,11 +378,12 @@ export const licenseServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
orgId,
|
||||
name,
|
||||
email
|
||||
}: TUpdateOrgBillingDetailsDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -355,8 +402,8 @@ export const licenseServiceFactory = ({
|
||||
return data;
|
||||
};
|
||||
|
||||
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorOrgId }: TOrgPmtMethodsDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -378,11 +425,12 @@ export const licenseServiceFactory = ({
|
||||
orgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
success_url,
|
||||
cancel_url
|
||||
}: TAddOrgPmtMethodDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -403,8 +451,15 @@ export const licenseServiceFactory = ({
|
||||
return { url };
|
||||
};
|
||||
|
||||
const delOrgPmtMethods = async ({ actorId, actor, actorOrgId, orgId, pmtMethodId }: TDelOrgPmtMethodDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const delOrgPmtMethods = async ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
orgId,
|
||||
pmtMethodId
|
||||
}: TDelOrgPmtMethodDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -420,8 +475,8 @@ export const licenseServiceFactory = ({
|
||||
return data;
|
||||
};
|
||||
|
||||
const getOrgTaxIds = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgTaxIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -438,8 +493,8 @@ export const licenseServiceFactory = ({
|
||||
return taxIds;
|
||||
};
|
||||
|
||||
const addOrgTaxId = async ({ actorId, actor, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -459,8 +514,8 @@ export const licenseServiceFactory = ({
|
||||
return data;
|
||||
};
|
||||
|
||||
const delOrgTaxId = async ({ orgId, actor, actorId, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -476,8 +531,8 @@ export const licenseServiceFactory = ({
|
||||
return data;
|
||||
};
|
||||
|
||||
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, orgId }: TOrgInvoiceDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@ -493,8 +548,8 @@ export const licenseServiceFactory = ({
|
||||
return invoices;
|
||||
};
|
||||
|
||||
const getOrgLicenses = async ({ orgId, actor, actorId, actorOrgId }: TOrgLicensesDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
|
@ -6,6 +6,21 @@ export enum InstanceType {
|
||||
Cloud = "cloud"
|
||||
}
|
||||
|
||||
export type TOfflineLicenseContents = {
|
||||
license: TOfflineLicense;
|
||||
signature: string;
|
||||
};
|
||||
|
||||
export type TOfflineLicense = {
|
||||
issuedTo: string;
|
||||
licenseId: string;
|
||||
customerId: string | null;
|
||||
issuedAt: string;
|
||||
expiresAt: string | null;
|
||||
terminatesAt: string | null;
|
||||
features: TFeatureSet;
|
||||
};
|
||||
|
||||
export type TFeatureSet = {
|
||||
_id: null;
|
||||
slug: null;
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { IdentityProjectMembershipRoleSchema, ProjectUserMembershipRolesSchema, TableName } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { selectAllTableCols } from "@app/lib/knex";
|
||||
import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||
|
||||
export type TPermissionDALFactory = ReturnType<typeof permissionDALFactory>;
|
||||
|
||||
@ -43,21 +45,72 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
|
||||
const getProjectPermission = async (userId: string, projectId: string) => {
|
||||
try {
|
||||
const membership = await db(TableName.ProjectMembership)
|
||||
.leftJoin(TableName.ProjectRoles, `${TableName.ProjectMembership}.roleId`, `${TableName.ProjectRoles}.id`)
|
||||
const docs = await db(TableName.ProjectMembership)
|
||||
.join(
|
||||
TableName.ProjectUserMembershipRole,
|
||||
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
||||
`${TableName.ProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.ProjectUserMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
|
||||
.where("userId", userId)
|
||||
.where(`${TableName.ProjectMembership}.projectId`, projectId)
|
||||
.select(selectAllTableCols(TableName.ProjectMembership))
|
||||
.select(selectAllTableCols(TableName.ProjectUserMembershipRole))
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.ProjectMembership).as("membershipId"),
|
||||
// TODO(roll-forward-migration): remove this field when we drop this in next migration after a week
|
||||
db.ref("role").withSchema(TableName.ProjectMembership).as("oldRoleField"),
|
||||
db.ref("createdAt").withSchema(TableName.ProjectMembership).as("membershipCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"),
|
||||
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
||||
db.ref("orgId").withSchema(TableName.Project)
|
||||
db.ref("orgId").withSchema(TableName.Project),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
|
||||
)
|
||||
.select("permissions")
|
||||
.first();
|
||||
.select("permissions");
|
||||
|
||||
return membership;
|
||||
const permission = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "membershipId",
|
||||
parentMapper: ({
|
||||
orgId,
|
||||
orgAuthEnforced,
|
||||
membershipId,
|
||||
membershipCreatedAt,
|
||||
membershipUpdatedAt,
|
||||
oldRoleField
|
||||
}) => ({
|
||||
orgId,
|
||||
orgAuthEnforced,
|
||||
userId,
|
||||
role: oldRoleField,
|
||||
id: membershipId,
|
||||
projectId,
|
||||
createdAt: membershipCreatedAt,
|
||||
updatedAt: membershipUpdatedAt
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "id",
|
||||
label: "roles" as const,
|
||||
mapper: (data) =>
|
||||
ProjectUserMembershipRolesSchema.extend({
|
||||
permissions: z.unknown(),
|
||||
customRoleSlug: z.string().optional().nullable()
|
||||
}).parse(data)
|
||||
}
|
||||
]
|
||||
});
|
||||
// when introducting cron mode change it here
|
||||
const activeRoles = permission?.[0]?.roles.filter(
|
||||
({ isTemporary, temporaryAccessEndTime }) =>
|
||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||
);
|
||||
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
||||
}
|
||||
@ -65,18 +118,69 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
|
||||
const getProjectIdentityPermission = async (identityId: string, projectId: string) => {
|
||||
try {
|
||||
const membership = await db(TableName.IdentityProjectMembership)
|
||||
const docs = await db(TableName.IdentityProjectMembership)
|
||||
.join(
|
||||
TableName.IdentityProjectMembershipRole,
|
||||
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
||||
`${TableName.IdentityProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.IdentityProjectMembership}.roleId`,
|
||||
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.join(
|
||||
// Join the Project table to later select orgId
|
||||
TableName.Project,
|
||||
`${TableName.IdentityProjectMembership}.projectId`,
|
||||
`${TableName.Project}.id`
|
||||
)
|
||||
.where("identityId", identityId)
|
||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||
.select(selectAllTableCols(TableName.IdentityProjectMembership))
|
||||
.select("permissions")
|
||||
.first();
|
||||
return membership;
|
||||
.select(selectAllTableCols(TableName.IdentityProjectMembershipRole))
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership).as("membershipId"),
|
||||
db.ref("orgId").withSchema(TableName.Project).as("orgId"), // Now you can select orgId from Project
|
||||
db.ref("role").withSchema(TableName.IdentityProjectMembership).as("oldRoleField"),
|
||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership).as("membershipCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership).as("membershipUpdatedAt"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
|
||||
)
|
||||
.select("permissions");
|
||||
|
||||
const permission = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "membershipId",
|
||||
parentMapper: ({ membershipId, membershipCreatedAt, membershipUpdatedAt, oldRoleField, orgId }) => ({
|
||||
id: membershipId,
|
||||
identityId,
|
||||
projectId,
|
||||
role: oldRoleField,
|
||||
createdAt: membershipCreatedAt,
|
||||
updatedAt: membershipUpdatedAt,
|
||||
orgId,
|
||||
// just a prefilled value
|
||||
orgAuthEnforced: false
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "id",
|
||||
label: "roles" as const,
|
||||
mapper: (data) =>
|
||||
IdentityProjectMembershipRoleSchema.extend({
|
||||
permissions: z.unknown(),
|
||||
customRoleSlug: z.string().optional().nullable()
|
||||
}).parse(data)
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// when introducting cron mode change it here
|
||||
const activeRoles = permission?.[0]?.roles.filter(
|
||||
({ isTemporary, temporaryAccessEndTime }) =>
|
||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||
);
|
||||
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "GetProjectIdentityPermission" });
|
||||
}
|
||||
|
23
backend/src/ee/services/permission/permission-fns.ts
Normal file
23
backend/src/ee/services/permission/permission-fns.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { TOrganizations } from "@app/db/schemas";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { ActorAuthMethod, AuthMethod } from "@app/services/auth/auth-type";
|
||||
|
||||
function isAuthMethodSaml(actorAuthMethod: ActorAuthMethod) {
|
||||
if (!actorAuthMethod) return false;
|
||||
|
||||
return [AuthMethod.AZURE_SAML, AuthMethod.OKTA_SAML, AuthMethod.JUMPCLOUD_SAML, AuthMethod.GOOGLE_SAML].includes(
|
||||
actorAuthMethod
|
||||
);
|
||||
}
|
||||
|
||||
function validateOrgSAML(actorAuthMethod: ActorAuthMethod, isSamlEnforced: TOrganizations["authEnforced"]) {
|
||||
if (actorAuthMethod === undefined) {
|
||||
throw new UnauthorizedError({ name: "No auth method defined" });
|
||||
}
|
||||
|
||||
if (isSamlEnforced && actorAuthMethod !== null && !isAuthMethodSaml(actorAuthMethod)) {
|
||||
throw new UnauthorizedError({ name: "Cannot access org-scoped resource" });
|
||||
}
|
||||
}
|
||||
|
||||
export { isAuthMethodSaml, validateOrgSAML };
|
@ -11,13 +11,16 @@ import {
|
||||
} from "@app/db/schemas";
|
||||
import { conditionsMatcher } from "@app/lib/casl";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||
import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
||||
import { TServiceTokenDALFactory } from "@app/services/service-token/service-token-dal";
|
||||
|
||||
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
||||
import { TPermissionDALFactory } from "./permission-dal";
|
||||
import { validateOrgSAML } from "./permission-fns";
|
||||
import { TBuildProjectPermissionDTO } from "./permission-types";
|
||||
import {
|
||||
buildServiceTokenProjectPermission,
|
||||
projectAdminPermissions,
|
||||
@ -31,6 +34,7 @@ type TPermissionServiceFactoryDep = {
|
||||
orgRoleDAL: Pick<TOrgRoleDALFactory, "findOne">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "findOne">;
|
||||
serviceTokenDAL: Pick<TServiceTokenDALFactory, "findById">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">;
|
||||
permissionDAL: TPermissionDALFactory;
|
||||
};
|
||||
|
||||
@ -40,7 +44,8 @@ export const permissionServiceFactory = ({
|
||||
permissionDAL,
|
||||
orgRoleDAL,
|
||||
projectRoleDAL,
|
||||
serviceTokenDAL
|
||||
serviceTokenDAL,
|
||||
projectDAL
|
||||
}: TPermissionServiceFactoryDep) => {
|
||||
const buildOrgPermission = (role: string, permission?: unknown) => {
|
||||
switch (role) {
|
||||
@ -64,45 +69,63 @@ export const permissionServiceFactory = ({
|
||||
}
|
||||
};
|
||||
|
||||
const buildProjectPermission = (role: string, permission?: unknown) => {
|
||||
switch (role) {
|
||||
case ProjectMembershipRole.Admin:
|
||||
return projectAdminPermissions;
|
||||
case ProjectMembershipRole.Member:
|
||||
return projectMemberPermissions;
|
||||
case ProjectMembershipRole.Viewer:
|
||||
return projectViewerPermission;
|
||||
case ProjectMembershipRole.NoAccess:
|
||||
return projectNoAccessPermissions;
|
||||
case ProjectMembershipRole.Custom:
|
||||
return createMongoAbility<ProjectPermissionSet>(
|
||||
unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
|
||||
permission as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
|
||||
),
|
||||
{
|
||||
conditionsMatcher
|
||||
const buildProjectPermission = (projectUserRoles: TBuildProjectPermissionDTO) => {
|
||||
const rules = projectUserRoles
|
||||
.map(({ role, permissions }) => {
|
||||
switch (role) {
|
||||
case ProjectMembershipRole.Admin:
|
||||
return projectAdminPermissions;
|
||||
case ProjectMembershipRole.Member:
|
||||
return projectMemberPermissions;
|
||||
case ProjectMembershipRole.Viewer:
|
||||
return projectViewerPermission;
|
||||
case ProjectMembershipRole.NoAccess:
|
||||
return projectNoAccessPermissions;
|
||||
case ProjectMembershipRole.Custom: {
|
||||
return unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
|
||||
permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
|
||||
);
|
||||
}
|
||||
);
|
||||
default:
|
||||
throw new BadRequestError({
|
||||
name: "ProjectRoleInvalid",
|
||||
message: "Project role not found"
|
||||
});
|
||||
}
|
||||
default:
|
||||
throw new BadRequestError({
|
||||
name: "ProjectRoleInvalid",
|
||||
message: "Project role not found"
|
||||
});
|
||||
}
|
||||
})
|
||||
.reduce((curr, prev) => prev.concat(curr), []);
|
||||
|
||||
return createMongoAbility<ProjectPermissionSet>(rules, {
|
||||
conditionsMatcher
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Get user permission in an organization
|
||||
* */
|
||||
const getUserOrgPermission = async (userId: string, orgId: string, userOrgId?: string) => {
|
||||
*/
|
||||
const getUserOrgPermission = async (
|
||||
userId: string,
|
||||
orgId: string,
|
||||
authMethod: ActorAuthMethod,
|
||||
userOrgId?: string
|
||||
) => {
|
||||
const membership = await permissionDAL.getOrgPermission(userId, orgId);
|
||||
if (!membership) throw new UnauthorizedError({ name: "User not in org" });
|
||||
if (membership.role === OrgMembershipRole.Custom && !membership.permissions) {
|
||||
throw new BadRequestError({ name: "Custom permission not found" });
|
||||
}
|
||||
if (membership.orgAuthEnforced && membership.orgId !== userOrgId) {
|
||||
throw new BadRequestError({ name: "Cannot access org-scoped resource" });
|
||||
|
||||
// If the org ID is API_KEY, the request is being made with an API Key.
|
||||
// Since we can't scope API keys to an organization, we'll need to do an arbitrary check to see if the user is a member of the organization.
|
||||
|
||||
// Extra: This means that when users are using API keys to make requests, they can't use slug-based routes.
|
||||
// Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization.
|
||||
if (userOrgId !== "API_KEY" && membership.orgId !== userOrgId) {
|
||||
throw new UnauthorizedError({ name: "You are not logged into this organization" });
|
||||
}
|
||||
|
||||
validateOrgSAML(authMethod, membership.orgAuthEnforced);
|
||||
|
||||
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
|
||||
};
|
||||
|
||||
@ -115,10 +138,16 @@ export const permissionServiceFactory = ({
|
||||
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
|
||||
};
|
||||
|
||||
const getOrgPermission = async (type: ActorType, id: string, orgId: string, actorOrgId?: string) => {
|
||||
const getOrgPermission = async (
|
||||
type: ActorType,
|
||||
id: string,
|
||||
orgId: string,
|
||||
authMethod: ActorAuthMethod,
|
||||
actorOrgId: string | undefined
|
||||
) => {
|
||||
switch (type) {
|
||||
case ActorType.USER:
|
||||
return getUserOrgPermission(id, orgId, actorOrgId);
|
||||
return getUserOrgPermission(id, orgId, authMethod, actorOrgId);
|
||||
case ActorType.IDENTITY:
|
||||
return getIdentityOrgPermission(id, orgId);
|
||||
default:
|
||||
@ -145,44 +174,94 @@ export const permissionServiceFactory = ({
|
||||
};
|
||||
|
||||
// user permission for a project in an organization
|
||||
const getUserProjectPermission = async (userId: string, projectId: string, userOrgId?: string) => {
|
||||
const getUserProjectPermission = async (
|
||||
userId: string,
|
||||
projectId: string,
|
||||
authMethod: ActorAuthMethod,
|
||||
userOrgId?: string
|
||||
): Promise<TProjectPermissionRT<ActorType.USER>> => {
|
||||
const membership = await permissionDAL.getProjectPermission(userId, projectId);
|
||||
if (!membership) throw new UnauthorizedError({ name: "User not in project" });
|
||||
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
|
||||
|
||||
if (membership.roles.some(({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions)) {
|
||||
throw new BadRequestError({ name: "Custom permission not found" });
|
||||
}
|
||||
|
||||
if (membership.orgAuthEnforced && membership.orgId !== userOrgId) {
|
||||
throw new BadRequestError({ name: "Cannot access org-scoped resource" });
|
||||
// If the org ID is API_KEY, the request is being made with an API Key.
|
||||
// Since we can't scope API keys to an organization, we'll need to do an arbitrary check to see if the user is a member of the organization.
|
||||
|
||||
// Extra: This means that when users are using API keys to make requests, they can't use slug-based routes.
|
||||
// Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization.
|
||||
if (userOrgId !== "API_KEY" && membership.orgId !== userOrgId) {
|
||||
throw new UnauthorizedError({ name: "You are not logged into this organization" });
|
||||
}
|
||||
|
||||
validateOrgSAML(authMethod, membership.orgAuthEnforced);
|
||||
|
||||
return {
|
||||
permission: buildProjectPermission(membership.role, membership.permissions),
|
||||
membership
|
||||
permission: buildProjectPermission(membership.roles),
|
||||
membership,
|
||||
hasRole: (role: string) =>
|
||||
membership.roles.findIndex(({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug) !== -1
|
||||
};
|
||||
};
|
||||
|
||||
const getIdentityProjectPermission = async (identityId: string, projectId: string) => {
|
||||
const membership = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
|
||||
if (!membership) throw new UnauthorizedError({ name: "Identity not in project" });
|
||||
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
|
||||
const getIdentityProjectPermission = async (
|
||||
identityId: string,
|
||||
projectId: string,
|
||||
identityOrgId: string | undefined
|
||||
): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
|
||||
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
|
||||
if (!identityProjectPermission) throw new UnauthorizedError({ name: "Identity not in project" });
|
||||
|
||||
if (
|
||||
identityProjectPermission.roles.some(
|
||||
({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions
|
||||
)
|
||||
) {
|
||||
throw new BadRequestError({ name: "Custom permission not found" });
|
||||
}
|
||||
|
||||
if (identityProjectPermission.orgId !== identityOrgId) {
|
||||
throw new UnauthorizedError({ name: "You are not a member of this organization" });
|
||||
}
|
||||
|
||||
return {
|
||||
permission: buildProjectPermission(membership.role, membership.permissions),
|
||||
membership
|
||||
permission: buildProjectPermission(identityProjectPermission.roles),
|
||||
membership: identityProjectPermission,
|
||||
hasRole: (role: string) =>
|
||||
identityProjectPermission.roles.findIndex(
|
||||
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
|
||||
) !== -1
|
||||
};
|
||||
};
|
||||
|
||||
const getServiceTokenProjectPermission = async (serviceTokenId: string, projectId: string) => {
|
||||
const getServiceTokenProjectPermission = async (
|
||||
serviceTokenId: string,
|
||||
projectId: string,
|
||||
actorOrgId: string | undefined
|
||||
) => {
|
||||
const serviceToken = await serviceTokenDAL.findById(serviceTokenId);
|
||||
if (!serviceToken) throw new BadRequestError({ message: "Service token not found" });
|
||||
|
||||
const serviceTokenProject = await projectDAL.findById(serviceToken.projectId);
|
||||
|
||||
if (!serviceTokenProject) throw new BadRequestError({ message: "Service token not linked to a project" });
|
||||
|
||||
if (serviceTokenProject.orgId !== actorOrgId) {
|
||||
throw new UnauthorizedError({ message: "Service token not a part of this organization" });
|
||||
}
|
||||
|
||||
if (serviceToken.projectId !== projectId)
|
||||
throw new UnauthorizedError({
|
||||
message: "Failed to find service authorization for given project"
|
||||
});
|
||||
|
||||
if (serviceTokenProject.orgId !== actorOrgId)
|
||||
throw new UnauthorizedError({
|
||||
message: "Failed to find service authorization for given project"
|
||||
});
|
||||
|
||||
const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []);
|
||||
return {
|
||||
permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions),
|
||||
@ -191,29 +270,35 @@ export const permissionServiceFactory = ({
|
||||
};
|
||||
|
||||
type TProjectPermissionRT<T extends ActorType> = T extends ActorType.SERVICE
|
||||
? { permission: MongoAbility<ProjectPermissionSet, MongoQuery>; membership: undefined }
|
||||
? {
|
||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||
membership: undefined;
|
||||
hasRole: (arg: string) => boolean;
|
||||
} // service token doesn't have both membership and roles
|
||||
: {
|
||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||
membership: (T extends ActorType.USER ? TProjectMemberships : TIdentityProjectMemberships) & {
|
||||
orgAuthEnforced: boolean;
|
||||
orgAuthEnforced: boolean | null | undefined;
|
||||
orgId: string;
|
||||
permissions?: unknown;
|
||||
roles: Array<{ role: string }>;
|
||||
};
|
||||
hasRole: (role: string) => boolean;
|
||||
};
|
||||
|
||||
const getProjectPermission = async <T extends ActorType>(
|
||||
type: T,
|
||||
id: string,
|
||||
projectId: string,
|
||||
actorOrgId?: string
|
||||
actorAuthMethod: ActorAuthMethod,
|
||||
actorOrgId: string | undefined
|
||||
): Promise<TProjectPermissionRT<T>> => {
|
||||
switch (type) {
|
||||
case ActorType.USER:
|
||||
return getUserProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
|
||||
return getUserProjectPermission(id, projectId, actorAuthMethod, actorOrgId) as Promise<TProjectPermissionRT<T>>;
|
||||
case ActorType.SERVICE:
|
||||
return getServiceTokenProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>;
|
||||
return getServiceTokenProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
|
||||
case ActorType.IDENTITY:
|
||||
return getIdentityProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>;
|
||||
return getIdentityProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
|
||||
default:
|
||||
throw new UnauthorizedError({
|
||||
message: "Permission not defined",
|
||||
@ -228,11 +313,13 @@ export const permissionServiceFactory = ({
|
||||
const projectRole = await projectRoleDAL.findOne({ slug: role, projectId });
|
||||
if (!projectRole) throw new BadRequestError({ message: "Role not found" });
|
||||
return {
|
||||
permission: buildProjectPermission(ProjectMembershipRole.Custom, projectRole.permissions),
|
||||
permission: buildProjectPermission([
|
||||
{ role: ProjectMembershipRole.Custom, permissions: projectRole.permissions }
|
||||
]),
|
||||
role: projectRole
|
||||
};
|
||||
}
|
||||
return { permission: buildProjectPermission(role, []) };
|
||||
return { permission: buildProjectPermission([{ role, permissions: [] }]) };
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -0,0 +1,4 @@
|
||||
export type TBuildProjectPermissionDTO = {
|
||||
permissions?: unknown;
|
||||
role: string;
|
||||
}[];
|
||||
|
@ -56,8 +56,8 @@ export type ProjectPermissionSet =
|
||||
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
|
||||
| [ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback];
|
||||
|
||||
const buildAdminPermission = () => {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
const buildAdminPermissionRules = () => {
|
||||
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
|
||||
@ -135,13 +135,13 @@ const buildAdminPermission = () => {
|
||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Project);
|
||||
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Project);
|
||||
|
||||
return build({ conditionsMatcher });
|
||||
return rules;
|
||||
};
|
||||
|
||||
export const projectAdminPermissions = buildAdminPermission();
|
||||
export const projectAdminPermissions = buildAdminPermissionRules();
|
||||
|
||||
const buildMemberPermission = () => {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
const buildMemberPermissionRules = () => {
|
||||
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
|
||||
@ -196,13 +196,13 @@ const buildMemberPermission = () => {
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||
|
||||
return build({ conditionsMatcher });
|
||||
return rules;
|
||||
};
|
||||
|
||||
export const projectMemberPermissions = buildMemberPermission();
|
||||
export const projectMemberPermissions = buildMemberPermissionRules();
|
||||
|
||||
const buildViewerPermission = () => {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
const buildViewerPermissionRules = () => {
|
||||
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||
@ -220,14 +220,14 @@ const buildViewerPermission = () => {
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||
|
||||
return build({ conditionsMatcher });
|
||||
return rules;
|
||||
};
|
||||
|
||||
export const projectViewerPermission = buildViewerPermission();
|
||||
export const projectViewerPermission = buildViewerPermissionRules();
|
||||
|
||||
const buildNoAccessProjectPermission = () => {
|
||||
const { build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
return build({ conditionsMatcher });
|
||||
const { rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||
return rules;
|
||||
};
|
||||
|
||||
export const buildServiceTokenProjectPermission = (
|
||||
|
@ -55,6 +55,7 @@ export const samlConfigServiceFactory = ({
|
||||
const createSamlCfg = async ({
|
||||
cert,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
orgId,
|
||||
issuer,
|
||||
@ -63,7 +64,7 @@ export const samlConfigServiceFactory = ({
|
||||
entryPoint,
|
||||
authProvider
|
||||
}: TCreateSamlCfgDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
|
||||
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
@ -146,6 +147,7 @@ export const samlConfigServiceFactory = ({
|
||||
orgId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
cert,
|
||||
actorId,
|
||||
issuer,
|
||||
@ -153,7 +155,7 @@ export const samlConfigServiceFactory = ({
|
||||
entryPoint,
|
||||
authProvider
|
||||
}: TUpdateSamlCfgDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
if (!plan.samlSSO)
|
||||
@ -172,7 +174,7 @@ export const samlConfigServiceFactory = ({
|
||||
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
if (entryPoint) {
|
||||
if (entryPoint !== undefined) {
|
||||
const {
|
||||
ciphertext: encryptedEntryPoint,
|
||||
iv: entryPointIV,
|
||||
@ -182,18 +184,19 @@ export const samlConfigServiceFactory = ({
|
||||
updateQuery.entryPointIV = entryPointIV;
|
||||
updateQuery.entryPointTag = entryPointTag;
|
||||
}
|
||||
if (issuer) {
|
||||
if (issuer !== undefined) {
|
||||
const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key);
|
||||
updateQuery.encryptedIssuer = encryptedIssuer;
|
||||
updateQuery.issuerIV = issuerIV;
|
||||
updateQuery.issuerTag = issuerTag;
|
||||
}
|
||||
if (cert) {
|
||||
if (cert !== undefined) {
|
||||
const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key);
|
||||
updateQuery.encryptedCert = encryptedCert;
|
||||
updateQuery.certIV = certIV;
|
||||
updateQuery.certTag = certTag;
|
||||
}
|
||||
|
||||
const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery);
|
||||
await orgDAL.updateById(orgId, { authEnforced: false, scimEnabled: false });
|
||||
|
||||
@ -237,6 +240,7 @@ export const samlConfigServiceFactory = ({
|
||||
dto.actor,
|
||||
dto.actorId,
|
||||
ssoConfig.orgId,
|
||||
dto.actorAuthMethod,
|
||||
dto.actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { TOrgPermission } from "@app/lib/types";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
export enum SamlProviders {
|
||||
OKTA_SAML = "okta-saml",
|
||||
@ -26,7 +26,14 @@ export type TUpdateSamlCfgDTO = Partial<{
|
||||
TOrgPermission;
|
||||
|
||||
export type TGetSamlCfgDTO =
|
||||
| { type: "org"; orgId: string; actor: ActorType; actorId: string; actorOrgId?: string }
|
||||
| {
|
||||
type: "org";
|
||||
orgId: string;
|
||||
actor: ActorType;
|
||||
actorId: string;
|
||||
actorAuthMethod: ActorAuthMethod;
|
||||
actorOrgId: string | undefined;
|
||||
}
|
||||
| {
|
||||
type: "orgSlug";
|
||||
orgSlug: string;
|
||||
|
@ -56,8 +56,16 @@ export const scimServiceFactory = ({
|
||||
permissionService,
|
||||
smtpService
|
||||
}: TScimServiceFactoryDep) => {
|
||||
const createScimToken = async ({ actor, actorId, actorOrgId, orgId, description, ttlDays }: TCreateScimTokenDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const createScimToken = async ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
orgId,
|
||||
description,
|
||||
ttlDays
|
||||
}: TCreateScimTokenDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Scim);
|
||||
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
@ -85,8 +93,8 @@ export const scimServiceFactory = ({
|
||||
return { scimToken };
|
||||
};
|
||||
|
||||
const listScimTokens = async ({ actor, actorId, actorOrgId, orgId }: TOrgPermission) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const listScimTokens = async ({ actor, actorId, actorOrgId, actorAuthMethod, orgId }: TOrgPermission) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Scim);
|
||||
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
@ -99,11 +107,17 @@ export const scimServiceFactory = ({
|
||||
return scimTokens;
|
||||
};
|
||||
|
||||
const deleteScimToken = async ({ scimTokenId, actor, actorId, actorOrgId }: TDeleteScimTokenDTO) => {
|
||||
const deleteScimToken = async ({ scimTokenId, actor, actorId, actorAuthMethod, actorOrgId }: TDeleteScimTokenDTO) => {
|
||||
let scimToken = await scimDAL.findById(scimTokenId);
|
||||
if (!scimToken) throw new BadRequestError({ message: "Failed to find SCIM token to delete" });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, scimToken.orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
scimToken.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Scim);
|
||||
|
||||
const plan = await licenseService.getPlan(scimToken.orgId);
|
||||
|
@ -45,6 +45,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
approvals,
|
||||
approvers,
|
||||
projectId,
|
||||
@ -54,7 +55,13 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
if (approvals > approvers.length)
|
||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.SecretApproval
|
||||
@ -98,6 +105,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
approvals,
|
||||
secretPolicyId
|
||||
}: TUpdateSapDTO) => {
|
||||
@ -108,6 +116,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
secretApprovalPolicy.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||
@ -152,7 +161,13 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
};
|
||||
};
|
||||
|
||||
const deleteSecretApprovalPolicy = async ({ secretPolicyId, actor, actorId, actorOrgId }: TDeleteSapDTO) => {
|
||||
const deleteSecretApprovalPolicy = async ({
|
||||
secretPolicyId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TDeleteSapDTO) => {
|
||||
const sapPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId);
|
||||
if (!sapPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
|
||||
|
||||
@ -160,6 +175,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
sapPolicy.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
@ -171,8 +187,20 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
return sapPolicy;
|
||||
};
|
||||
|
||||
const getSecretApprovalPolicyByProjectId = async ({ actorId, actor, actorOrgId, projectId }: TListSapDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const getSecretApprovalPolicyByProjectId = async ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
projectId
|
||||
}: TListSapDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||
|
||||
const sapPolicies = await secretApprovalPolicyDAL.find({ projectId });
|
||||
@ -201,10 +229,17 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
environment,
|
||||
secretPath
|
||||
}: TGetBoardSapDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, { secretPath, environment })
|
||||
|
@ -82,13 +82,14 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
secretVersionDAL,
|
||||
secretQueueService
|
||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId }: TApprovalRequestCountDTO) => {
|
||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
actor as ActorType.USER,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
@ -100,6 +101,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
projectId,
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
status,
|
||||
environment,
|
||||
@ -109,7 +111,13 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
}: TListApprovalsDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
const approvals = await secretApprovalRequestDAL.findByProjectId({
|
||||
projectId,
|
||||
committer,
|
||||
@ -122,21 +130,28 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
return approvals;
|
||||
};
|
||||
|
||||
const getSecretApprovalDetails = async ({ actor, actorId, actorOrgId, id }: TSecretApprovalDetailsDTO) => {
|
||||
const getSecretApprovalDetails = async ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
id
|
||||
}: TSecretApprovalDetailsDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const secretApprovalRequest = await secretApprovalRequestDAL.findById(id);
|
||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
if (
|
||||
membership.role !== ProjectMembershipRole.Admin &&
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
) {
|
||||
@ -150,20 +165,28 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
return { ...secretApprovalRequest, secretPath: secretPath?.[0]?.path || "/", commits: secrets };
|
||||
};
|
||||
|
||||
const reviewApproval = async ({ approvalId, actor, status, actorId, actorOrgId }: TReviewRequestDTO) => {
|
||||
const reviewApproval = async ({
|
||||
approvalId,
|
||||
actor,
|
||||
status,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TReviewRequestDTO) => {
|
||||
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
|
||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
if (
|
||||
membership.role !== ProjectMembershipRole.Admin &&
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
) {
|
||||
@ -192,20 +215,28 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
return reviewStatus;
|
||||
};
|
||||
|
||||
const updateApprovalStatus = async ({ actorId, status, approvalId, actor, actorOrgId }: TStatusChangeDTO) => {
|
||||
const updateApprovalStatus = async ({
|
||||
actorId,
|
||||
status,
|
||||
approvalId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod
|
||||
}: TStatusChangeDTO) => {
|
||||
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
|
||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
if (
|
||||
membership.role !== ProjectMembershipRole.Admin &&
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
) {
|
||||
@ -229,16 +260,24 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
approvalId,
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
actorAuthMethod
|
||||
}: TMergeSecretApprovalRequestDTO) => {
|
||||
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
|
||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy, folderId, projectId } = secretApprovalRequest;
|
||||
const { membership } = await permissionService.getProjectPermission(ActorType.USER, actorId, projectId, actorOrgId);
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
if (
|
||||
membership.role !== ProjectMembershipRole.Admin &&
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
) {
|
||||
@ -433,6 +472,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
policy,
|
||||
projectId,
|
||||
secretPath,
|
||||
@ -444,6 +484,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
|
@ -1,3 +1,10 @@
|
||||
import {
|
||||
CreateAccessKeyCommand,
|
||||
DeleteAccessKeyCommand,
|
||||
GetAccessKeyLastUsedCommand,
|
||||
IAMClient
|
||||
} from "@aws-sdk/client-iam";
|
||||
|
||||
import { SecretKeyEncoding, SecretType } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import {
|
||||
@ -18,7 +25,12 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||
|
||||
import { TSecretRotationDALFactory } from "../secret-rotation-dal";
|
||||
import { rotationTemplates } from "../templates";
|
||||
import { TDbProviderClients, TProviderFunctionTypes, TSecretRotationProviderTemplate } from "../templates/types";
|
||||
import {
|
||||
TAwsProviderSystems,
|
||||
TDbProviderClients,
|
||||
TProviderFunctionTypes,
|
||||
TSecretRotationProviderTemplate
|
||||
} from "../templates/types";
|
||||
import {
|
||||
getDbSetQuery,
|
||||
secretRotationDbFn,
|
||||
@ -127,7 +139,10 @@ export const secretRotationQueueFactory = ({
|
||||
internal: {}
|
||||
};
|
||||
|
||||
// when its a database we keep cycling the variables accordingly
|
||||
/* Rotation Function For Database
|
||||
* A database like sql cannot have multiple password for a user
|
||||
* thus we ask users to create two users with required permission and then we keep cycling between these two db users
|
||||
*/
|
||||
if (provider.template.type === TProviderFunctionTypes.DB) {
|
||||
const lastCred = variables.creds.at(-1);
|
||||
if (lastCred && variables.creds.length === 1) {
|
||||
@ -170,6 +185,65 @@ export const secretRotationQueueFactory = ({
|
||||
if (variables.creds.length === 2) variables.creds.pop();
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotation Function For AWS Services
|
||||
* Due to complexity in AWS Authorization hashing signature process we keep it as seperate entity instead of http template mode
|
||||
* We first delete old key before creating a new one because aws iam has a quota limit of 2 keys
|
||||
* */
|
||||
if (provider.template.type === TProviderFunctionTypes.AWS) {
|
||||
if (provider.template.client === TAwsProviderSystems.IAM) {
|
||||
const client = new IAMClient({
|
||||
region: newCredential.inputs.manager_user_aws_region as string,
|
||||
credentials: {
|
||||
accessKeyId: newCredential.inputs.manager_user_access_key as string,
|
||||
secretAccessKey: newCredential.inputs.manager_user_secret_key as string
|
||||
}
|
||||
});
|
||||
|
||||
const iamUserName = newCredential.inputs.iam_username as string;
|
||||
|
||||
if (variables.creds.length === 2) {
|
||||
const deleteCycleCredential = variables.creds.pop();
|
||||
if (deleteCycleCredential) {
|
||||
const deletedIamAccessKey = await client.send(
|
||||
new DeleteAccessKeyCommand({
|
||||
UserName: iamUserName,
|
||||
AccessKeyId: deleteCycleCredential.outputs.iam_user_access_key as string
|
||||
})
|
||||
);
|
||||
|
||||
if (
|
||||
!deletedIamAccessKey?.$metadata?.httpStatusCode ||
|
||||
deletedIamAccessKey?.$metadata?.httpStatusCode > 300
|
||||
) {
|
||||
throw new DisableRotationErrors({
|
||||
message: "Failed to delete aws iam access key. Check managed iam user policy"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newIamAccessKey = await client.send(new CreateAccessKeyCommand({ UserName: iamUserName }));
|
||||
if (!newIamAccessKey.AccessKey)
|
||||
throw new DisableRotationErrors({ message: "Failed to create access key. Check managed iam user policy" });
|
||||
|
||||
// test
|
||||
const testAccessKey = await client.send(
|
||||
new GetAccessKeyLastUsedCommand({ AccessKeyId: newIamAccessKey.AccessKey.AccessKeyId })
|
||||
);
|
||||
if (testAccessKey?.UserName !== iamUserName)
|
||||
throw new DisableRotationErrors({ message: "Failed to create access key. Check managed iam user policy" });
|
||||
|
||||
newCredential.outputs.iam_user_access_key = newIamAccessKey.AccessKey.AccessKeyId;
|
||||
newCredential.outputs.iam_user_secret_key = newIamAccessKey.AccessKey.SecretAccessKey;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rotation function of HTTP infisical template
|
||||
* This is a generic http based template system for rotation
|
||||
* we use this for sendgrid and for custom secret rotation
|
||||
* This will ensure user provided rotation is easier to make
|
||||
* */
|
||||
if (provider.template.type === TProviderFunctionTypes.HTTP) {
|
||||
if (provider.template.functions.set?.pre) {
|
||||
secretRotationPreSetFn(provider.template.functions.set.pre, newCredential);
|
||||
@ -185,6 +259,9 @@ export const secretRotationQueueFactory = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// insert the new variables to start
|
||||
// encrypt the data - save it
|
||||
variables.creds.unshift({
|
||||
outputs: newCredential.outputs,
|
||||
internal: newCredential.internal
|
||||
@ -200,6 +277,7 @@ export const secretRotationQueueFactory = ({
|
||||
key
|
||||
)
|
||||
}));
|
||||
// map the final values to output keys in the board
|
||||
await secretRotationDAL.transaction(async (tx) => {
|
||||
await secretRotationDAL.updateById(
|
||||
rotationId,
|
||||
|
@ -39,8 +39,20 @@ export const secretRotationServiceFactory = ({
|
||||
folderDAL,
|
||||
secretDAL
|
||||
}: TSecretRotationServiceFactoryDep) => {
|
||||
const getProviderTemplates = async ({ actor, actorId, actorOrgId, projectId }: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const getProviderTemplates = async ({
|
||||
actor,
|
||||
actorId,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
projectId
|
||||
}: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
||||
|
||||
return {
|
||||
@ -54,6 +66,7 @@ export const secretRotationServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
inputs,
|
||||
outputs,
|
||||
interval,
|
||||
@ -61,7 +74,13 @@ export const secretRotationServiceFactory = ({
|
||||
secretPath,
|
||||
environment
|
||||
}: TCreateSecretRotationDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.SecretRotation
|
||||
@ -139,14 +158,20 @@ export const secretRotationServiceFactory = ({
|
||||
return secretRotation;
|
||||
};
|
||||
|
||||
const getByProjectId = async ({ actorId, projectId, actor, actorOrgId }: TListByProjectIdDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const getByProjectId = async ({ actorId, projectId, actor, actorOrgId, actorAuthMethod }: TListByProjectIdDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
||||
const doc = await secretRotationDAL.find({ projectId });
|
||||
return doc;
|
||||
};
|
||||
|
||||
const restartById = async ({ actor, actorId, actorOrgId, rotationId }: TRestartDTO) => {
|
||||
const restartById = async ({ actor, actorId, actorOrgId, actorAuthMethod, rotationId }: TRestartDTO) => {
|
||||
const doc = await secretRotationDAL.findById(rotationId);
|
||||
if (!doc) throw new BadRequestError({ message: "Rotation not found" });
|
||||
|
||||
@ -157,18 +182,30 @@ export const secretRotationServiceFactory = ({
|
||||
message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation."
|
||||
});
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
doc.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation);
|
||||
await secretRotationQueue.removeFromQueue(doc.id, doc.interval);
|
||||
await secretRotationQueue.addToQueue(doc.id, doc.interval);
|
||||
return doc;
|
||||
};
|
||||
|
||||
const deleteById = async ({ actor, actorId, actorOrgId, rotationId }: TDeleteDTO) => {
|
||||
const deleteById = async ({ actor, actorId, actorOrgId, actorAuthMethod, rotationId }: TDeleteDTO) => {
|
||||
const doc = await secretRotationDAL.findById(rotationId);
|
||||
if (!doc) throw new BadRequestError({ message: "Rotation not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
doc.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.SecretRotation
|
||||
|
21
backend/src/ee/services/secret-rotation/templates/aws-iam.ts
Normal file
21
backend/src/ee/services/secret-rotation/templates/aws-iam.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { TAwsProviderSystems, TProviderFunctionTypes } from "./types";
|
||||
|
||||
export const AWS_IAM_TEMPLATE = {
|
||||
type: TProviderFunctionTypes.AWS as const,
|
||||
client: TAwsProviderSystems.IAM,
|
||||
inputs: {
|
||||
type: "object" as const,
|
||||
properties: {
|
||||
manager_user_access_key: { type: "string" as const },
|
||||
manager_user_secret_key: { type: "string" as const },
|
||||
manager_user_aws_region: { type: "string" as const },
|
||||
iam_username: { type: "string" as const }
|
||||
},
|
||||
required: ["manager_user_access_key", "manager_user_secret_key", "manager_user_aws_region", "iam_username"],
|
||||
additionalProperties: false
|
||||
},
|
||||
outputs: {
|
||||
iam_user_access_key: { type: "string" },
|
||||
iam_user_secret_key: { type: "string" }
|
||||
}
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
import { AWS_IAM_TEMPLATE } from "./aws-iam";
|
||||
import { MYSQL_TEMPLATE } from "./mysql";
|
||||
import { POSTGRES_TEMPLATE } from "./postgres";
|
||||
import { SENDGRID_TEMPLATE } from "./sendgrid";
|
||||
@ -24,5 +25,12 @@ export const rotationTemplates: TSecretRotationProviderTemplate[] = [
|
||||
image: "mysql.png",
|
||||
description: "Rotate MySQL@7/MariaDB user credentials",
|
||||
template: MYSQL_TEMPLATE
|
||||
},
|
||||
{
|
||||
name: "aws-iam",
|
||||
title: "AWS IAM",
|
||||
image: "aws-iam.svg",
|
||||
description: "Rotate AWS IAM User credentials",
|
||||
template: AWS_IAM_TEMPLATE
|
||||
}
|
||||
];
|
||||
|
@ -1,6 +1,7 @@
|
||||
export enum TProviderFunctionTypes {
|
||||
HTTP = "http",
|
||||
DB = "database"
|
||||
DB = "database",
|
||||
AWS = "aws"
|
||||
}
|
||||
|
||||
export enum TDbProviderClients {
|
||||
@ -10,6 +11,10 @@ export enum TDbProviderClients {
|
||||
MySql = "mysql"
|
||||
}
|
||||
|
||||
export enum TAwsProviderSystems {
|
||||
IAM = "iam"
|
||||
}
|
||||
|
||||
export enum TAssignOp {
|
||||
Direct = "direct",
|
||||
JmesPath = "jmesopath"
|
||||
@ -42,7 +47,7 @@ export type TSecretRotationProviderTemplate = {
|
||||
title: string;
|
||||
image?: string;
|
||||
description?: string;
|
||||
template: THttpProviderTemplate | TDbProviderTemplate;
|
||||
template: THttpProviderTemplate | TDbProviderTemplate | TAwsProviderTemplate;
|
||||
};
|
||||
|
||||
export type THttpProviderTemplate = {
|
||||
@ -70,3 +75,14 @@ export type TDbProviderTemplate = {
|
||||
};
|
||||
outputs: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type TAwsProviderTemplate = {
|
||||
type: TProviderFunctionTypes.AWS;
|
||||
client: TAwsProviderSystems;
|
||||
inputs: {
|
||||
type: "object";
|
||||
properties: Record<string, { type: string; [x: string]: unknown; desc?: string }>;
|
||||
required?: string[];
|
||||
};
|
||||
outputs: Record<string, unknown>;
|
||||
};
|
||||
|
@ -39,8 +39,14 @@ export const secretScanningServiceFactory = ({
|
||||
permissionService,
|
||||
secretScanningQueue
|
||||
}: TSecretScanningServiceFactoryDep) => {
|
||||
const createInstallationSession = async ({ actor, orgId, actorId, actorOrgId }: TInstallAppSessionDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const createInstallationSession = async ({
|
||||
actor,
|
||||
orgId,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TInstallAppSessionDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
||||
|
||||
const sessionId = crypto.randomBytes(16).toString("hex");
|
||||
@ -53,12 +59,19 @@ export const secretScanningServiceFactory = ({
|
||||
actorId,
|
||||
installationId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TLinkInstallSessionDTO) => {
|
||||
const session = await gitAppInstallSessionDAL.findOne({ sessionId });
|
||||
if (!session) throw new UnauthorizedError({ message: "Session not found" });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId, actorOrgId);
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
session.orgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
||||
const installatedApp = await gitAppOrgDAL.transaction(async (tx) => {
|
||||
await gitAppInstallSessionDAL.deleteById(session.id, tx);
|
||||
@ -89,23 +102,37 @@ export const secretScanningServiceFactory = ({
|
||||
return { installatedApp };
|
||||
};
|
||||
|
||||
const getOrgInstallationStatus = async ({ actorId, orgId, actor, actorOrgId }: TGetOrgInstallStatusDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getOrgInstallationStatus = async ({
|
||||
actorId,
|
||||
orgId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TGetOrgInstallStatusDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||
|
||||
const appInstallation = await gitAppOrgDAL.findOne({ orgId });
|
||||
return Boolean(appInstallation);
|
||||
};
|
||||
|
||||
const getRisksByOrg = async ({ actor, orgId, actorId, actorOrgId }: TGetOrgRisksDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const getRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId }: TGetOrgRisksDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
|
||||
return { risks };
|
||||
};
|
||||
|
||||
const updateRiskStatus = async ({ actorId, orgId, actor, actorOrgId, riskId, status }: TUpdateRiskStatusDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
|
||||
const updateRiskStatus = async ({
|
||||
actorId,
|
||||
orgId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
riskId,
|
||||
status
|
||||
}: TUpdateRiskStatusDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
|
||||
|
||||
const isRiskResolved = Boolean(
|
||||
|
@ -59,9 +59,16 @@ export const secretSnapshotServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
path
|
||||
}: TProjectSnapshotCountDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||
|
||||
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
|
||||
@ -77,11 +84,18 @@ export const secretSnapshotServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
path,
|
||||
limit = 20,
|
||||
offset = 0
|
||||
}: TProjectSnapshotListDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||
|
||||
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
|
||||
@ -91,10 +105,16 @@ export const secretSnapshotServiceFactory = ({
|
||||
return snapshots;
|
||||
};
|
||||
|
||||
const getSnapshotData = async ({ actorId, actor, actorOrgId, id }: TGetSnapshotDataDTO) => {
|
||||
const getSnapshotData = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TGetSnapshotDataDTO) => {
|
||||
const snapshot = await snapshotDAL.findSecretSnapshotDataById(id);
|
||||
if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" });
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
snapshot.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||
return snapshot;
|
||||
};
|
||||
@ -145,11 +165,23 @@ export const secretSnapshotServiceFactory = ({
|
||||
}
|
||||
};
|
||||
|
||||
const rollbackSnapshot = async ({ id: snapshotId, actor, actorId, actorOrgId }: TRollbackSnapshotDTO) => {
|
||||
const rollbackSnapshot = async ({
|
||||
id: snapshotId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TRollbackSnapshotDTO) => {
|
||||
const snapshot = await snapshotDAL.findById(snapshotId);
|
||||
if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
snapshot.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.SecretRollback
|
||||
|
@ -26,8 +26,14 @@ export const trustedIpServiceFactory = ({
|
||||
licenseService,
|
||||
projectDAL
|
||||
}: TTrustedIpServiceFactoryDep) => {
|
||||
const listIpsByProjectId = async ({ projectId, actor, actorId, actorOrgId }: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const listIpsByProjectId = async ({ projectId, actor, actorId, actorAuthMethod, actorOrgId }: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||
const trustedIps = await trustedIpDAL.find({
|
||||
projectId
|
||||
@ -38,13 +44,20 @@ export const trustedIpServiceFactory = ({
|
||||
const addProjectIp = async ({
|
||||
projectId,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
ipAddress: ip,
|
||||
comment,
|
||||
isActive
|
||||
}: TCreateIpDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
||||
|
||||
const project = await projectDAL.findById(projectId);
|
||||
@ -78,11 +91,18 @@ export const trustedIpServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
ipAddress: ip,
|
||||
comment,
|
||||
trustedIpId
|
||||
}: TUpdateIpDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
||||
|
||||
const project = await projectDAL.findById(projectId);
|
||||
@ -113,8 +133,21 @@ export const trustedIpServiceFactory = ({
|
||||
return { trustedIp, project }; // for audit log
|
||||
};
|
||||
|
||||
const deleteProjectIp = async ({ projectId, actorId, actor, actorOrgId, trustedIpId }: TDeleteIpDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
|
||||
const deleteProjectIp = async ({
|
||||
projectId,
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
trustedIpId
|
||||
}: TDeleteIpDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
||||
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
287
backend/src/lib/api-docs/constants.ts
Normal file
287
backend/src/lib/api-docs/constants.ts
Normal file
@ -0,0 +1,287 @@
|
||||
export const IDENTITIES = {
|
||||
CREATE: {
|
||||
name: "The name of the identity to create.",
|
||||
organizationId: "The organization ID to which the identity belongs.",
|
||||
role: "The role of the identity. Possible values are 'no-access', 'member', and 'admin'."
|
||||
},
|
||||
UPDATE: {
|
||||
identityId: "The ID of the identity to update.",
|
||||
name: "The new name of the identity.",
|
||||
role: "The new role of the identity."
|
||||
},
|
||||
DELETE: {
|
||||
identityId: "The ID of the identity to delete."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const UNIVERSAL_AUTH = {
|
||||
LOGIN: {
|
||||
clientId: "Your Machine Identity Client ID.",
|
||||
clientSecret: "Your Machine Identity Client Secret."
|
||||
},
|
||||
ATTACH: {
|
||||
identityId: "The ID of the identity to attach the configuration onto.",
|
||||
clientSecretTrustedIps:
|
||||
"A list of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. You can use 0.0.0.0/0, to allow usage from any network address.",
|
||||
accessTokenTrustedIps:
|
||||
"A list of IPs or CIDR ranges that access tokens can be used from. You can use 0.0.0.0/0, to allow usage from any network address.",
|
||||
accessTokenTTL: "The lifetime for an access token in seconds. This value will be referenced at renewal time.",
|
||||
accessTokenMaxTTL:
|
||||
"The maximum lifetime for an access token in seconds. This value will be referenced at renewal time.",
|
||||
accessTokenNumUsesLimit:
|
||||
"The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses."
|
||||
},
|
||||
RETRIEVE: {
|
||||
identityId: "The ID of the identity to retrieve."
|
||||
},
|
||||
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.",
|
||||
accessTokenTrustedIps: "The new list of IPs or CIDR ranges that access tokens can be used from.",
|
||||
accessTokenTTL: "The new lifetime for an access token in seconds.",
|
||||
accessTokenMaxTTL: "The new maximum lifetime for an access token in seconds.",
|
||||
accessTokenNumUsesLimit: "The new maximum number of times that an access token can be used."
|
||||
},
|
||||
CREATE_CLIENT_SECRET: {
|
||||
identityId: "The ID of the identity to create a client secret for.",
|
||||
description: "The description of the client secret.",
|
||||
numUsesLimit:
|
||||
"The maximum number of times that the client secret can be used; a value of 0 implies infinite number of uses.",
|
||||
ttl: "The lifetime for the client secret in seconds."
|
||||
},
|
||||
LIST_CLIENT_SECRETS: {
|
||||
identityId: "The ID of the identity to list client secrets for."
|
||||
},
|
||||
REVOKE_CLIENT_SECRET: {
|
||||
identityId: "The ID of the identity to revoke the client secret from.",
|
||||
clientSecretId: "The ID of the client secret to revoke."
|
||||
},
|
||||
RENEW_ACCESS_TOKEN: {
|
||||
accessToken: "The access token to renew."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const ORGANIZATIONS = {
|
||||
LIST_USER_MEMBERSHIPS: {
|
||||
organizationId: "The ID of the organization to get memberships from."
|
||||
},
|
||||
UPDATE_USER_MEMBERSHIP: {
|
||||
organizationId: "The ID of the organization to update the membership for.",
|
||||
membershipId: "The ID of the membership to update.",
|
||||
role: "The new role of the membership."
|
||||
},
|
||||
DELETE_USER_MEMBERSHIP: {
|
||||
organizationId: "The ID of the organization to delete the membership from.",
|
||||
membershipId: "The ID of the membership to delete."
|
||||
},
|
||||
LIST_IDENTITY_MEMBERSHIPS: {
|
||||
orgId: "The ID of the organization to get identity memberships from."
|
||||
},
|
||||
GET_PROJECTS: {
|
||||
organizationId: "The ID of the organization to get projects from."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const PROJECTS = {
|
||||
CREATE: {
|
||||
organizationSlug: "The slug of the organization to create the project in.",
|
||||
projectName: "The name of the project to create.",
|
||||
slug: "An optional slug for the project."
|
||||
},
|
||||
DELETE: {
|
||||
workspaceId: "The ID of the project to delete."
|
||||
},
|
||||
GET: {
|
||||
workspaceId: "The ID of the project."
|
||||
},
|
||||
UPDATE: {
|
||||
workspaceId: "The ID of the project to update.",
|
||||
name: "The new name of the project.",
|
||||
autoCapitalization: "Disable or enable auto-capitalization for the project."
|
||||
},
|
||||
INVITE_MEMBER: {
|
||||
projectId: "The ID of the project to invite the member to.",
|
||||
emails: "A list of organization member emails to invite to the project.",
|
||||
usernames: "A list of usernames to invite to the project."
|
||||
},
|
||||
REMOVE_MEMBER: {
|
||||
projectId: "The ID of the project to remove the member from.",
|
||||
emails: "A list of organization member emails to remove from the project.",
|
||||
usernames: "A list of usernames to remove from the project."
|
||||
},
|
||||
GET_USER_MEMBERSHIPS: {
|
||||
workspaceId: "The ID of the project to get memberships from."
|
||||
},
|
||||
UPDATE_USER_MEMBERSHIP: {
|
||||
workspaceId: "The ID of the project to update the membership for.",
|
||||
membershipId: "The ID of the membership to update.",
|
||||
roles: "A list of roles to update the membership to."
|
||||
},
|
||||
LIST_IDENTITY_MEMBERSHIPS: {
|
||||
projectId: "The ID of the project to get identity memberships from."
|
||||
},
|
||||
UPDATE_IDENTITY_MEMBERSHIP: {
|
||||
projectId: "The ID of the project to update the identity membership for.",
|
||||
identityId: "The ID of the identity to update the membership for.",
|
||||
roles: "A list of roles to update the membership to."
|
||||
},
|
||||
DELETE_IDENTITY_MEMBERSHIP: {
|
||||
projectId: "The ID of the project to delete the identity membership from.",
|
||||
identityId: "The ID of the identity to delete the membership from."
|
||||
},
|
||||
GET_KEY: {
|
||||
workspaceId: "The ID of the project to get the key from."
|
||||
},
|
||||
GET_SNAPSHOTS: {
|
||||
workspaceId: "The ID of the project to get snapshots from.",
|
||||
environment: "The environment to get snapshots from.",
|
||||
path: "The secret path to get snapshots from.",
|
||||
offset: "The offset to start from. If you enter 10, it will start from the 10th snapshot.",
|
||||
limit: "The number of snapshots to return."
|
||||
},
|
||||
ROLLBACK_TO_SNAPSHOT: {
|
||||
secretSnapshotId: "The ID of the snapshot to rollback to."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const ENVIRONMENTS = {
|
||||
CREATE: {
|
||||
workspaceId: "The ID of the project to create the environment in.",
|
||||
name: "The name of the environment to create.",
|
||||
slug: "The slug of the environment to create."
|
||||
},
|
||||
UPDATE: {
|
||||
workspaceId: "The ID of the project to update the environment in.",
|
||||
id: "The ID of the environment to update.",
|
||||
name: "The new name of the environment.",
|
||||
slug: "The new slug of the environment.",
|
||||
position: "The new position of the environment. The lowest number will be displayed as the first environment."
|
||||
},
|
||||
DELETE: {
|
||||
workspaceId: "The ID of the project to delete the environment from.",
|
||||
id: "The ID of the environment to delete."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const FOLDERS = {
|
||||
LIST: {
|
||||
workspaceId: "The ID of the project to list folders from.",
|
||||
environment: "The slug of the environment to list folders from.",
|
||||
path: "The path to list folders from.",
|
||||
directory: "The directory to list folders from. (Deprecated in favor of path)"
|
||||
},
|
||||
CREATE: {
|
||||
workspaceId: "The ID of the project to create the folder in.",
|
||||
environment: "The slug of the environment to create the folder in.",
|
||||
name: "The name of the folder to create.",
|
||||
path: "The path of the folder to create.",
|
||||
directory: "The directory of the folder to create. (Deprecated in favor of path)"
|
||||
},
|
||||
UPDATE: {
|
||||
folderId: "The ID of the folder to update.",
|
||||
environment: "The slug of the environment where the folder is located.",
|
||||
name: "The new name of the folder.",
|
||||
path: "The path of the folder to update.",
|
||||
directory: "The new directory of the folder to update. (Deprecated in favor of path)",
|
||||
workspaceId: "The ID of the project where the folder is located."
|
||||
},
|
||||
DELETE: {
|
||||
folderIdOrName: "The ID or name of the folder to delete.",
|
||||
workspaceId: "The ID of the project to delete the folder from.",
|
||||
environment: "The slug of the environment where the folder is located.",
|
||||
directory: "The directory of the folder to delete. (Deprecated in favor of path)",
|
||||
path: "The path of the folder to delete."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const RAW_SECRETS = {
|
||||
LIST: {
|
||||
workspaceId: "The ID of the project to list secrets from.",
|
||||
workspaceSlug: "The slug of the project to list secrets from. This parameter is only usable by machine identities.",
|
||||
environment: "The slug of the environment to list secrets from.",
|
||||
secretPath: "The secret path to list secrets from.",
|
||||
includeImports: "Weather to include imported secrets or not."
|
||||
},
|
||||
CREATE: {
|
||||
secretName: "The name of the secret to create.",
|
||||
environment: "The slug of the environment to create the secret in.",
|
||||
secretComment: "Attach a comment to the secret.",
|
||||
secretPath: "The path to create the secret in.",
|
||||
secretValue: "The value of the secret to create.",
|
||||
skipMultilineEncoding: "Skip multiline encoding for the secret value.",
|
||||
type: "The type of the secret to create.",
|
||||
workspaceId: "The ID of the project to create the secret in."
|
||||
},
|
||||
GET: {
|
||||
secretName: "The name of the secret to get.",
|
||||
workspaceId: "The ID of the project to get the secret from.",
|
||||
environment: "The slug of the environment to get the secret from.",
|
||||
secretPath: "The path of the secret to get.",
|
||||
version: "The version of the secret to get.",
|
||||
type: "The type of the secret to get.",
|
||||
includeImports: "Weather to include imported secrets or not."
|
||||
},
|
||||
UPDATE: {
|
||||
secretName: "The name of the secret to update.",
|
||||
environment: "The slug of the environment where the secret is located.",
|
||||
secretPath: "The path of the secret to update",
|
||||
secretValue: "The new value of the secret.",
|
||||
skipMultilineEncoding: "Skip multiline encoding for the secret value.",
|
||||
type: "The type of the secret to update.",
|
||||
workspaceId: "The ID of the project to update the secret in."
|
||||
},
|
||||
DELETE: {
|
||||
secretName: "The name of the secret to delete.",
|
||||
environment: "The slug of the environment where the secret is located.",
|
||||
secretPath: "The path of the secret.",
|
||||
type: "The type of the secret to delete.",
|
||||
workspaceId: "The ID of the project where the secret is located."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const SECRET_IMPORTS = {
|
||||
LIST: {
|
||||
workspaceId: "The ID of the project to list secret imports from.",
|
||||
environment: "The slug of the environment to list secret imports from.",
|
||||
path: "The path to list secret imports from."
|
||||
},
|
||||
CREATE: {
|
||||
environment: "The slug of the environment to import into.",
|
||||
path: "The path to import into.",
|
||||
workspaceId: "The ID of the project you are working in.",
|
||||
import: {
|
||||
environment: "The slug of the environment to import from.",
|
||||
path: "The path to import from."
|
||||
}
|
||||
},
|
||||
UPDATE: {
|
||||
secretImportId: "The ID of the secret import to update.",
|
||||
environment: "The slug of the environment where the secret import is located.",
|
||||
import: {
|
||||
environment: "The new environment slug to import from.",
|
||||
path: "The new path to import from.",
|
||||
position: "The new position of the secret import. The lowest number will be displayed as the first import."
|
||||
},
|
||||
path: "The path of the secret import to update.",
|
||||
workspaceId: "The ID of the project where the secret import is located."
|
||||
},
|
||||
DELETE: {
|
||||
workspaceId: "The ID of the project to delete the secret import from.",
|
||||
secretImportId: "The ID of the secret import to delete.",
|
||||
environment: "The slug of the environment where the secret import is located.",
|
||||
path: "The path of the secret import to delete."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const AUDIT_LOGS = {
|
||||
EXPORT: {
|
||||
workspaceId: "The ID of the project to export audit logs from.",
|
||||
eventType: "The type of the event to export.",
|
||||
userAgentType: "Choose which consuming application to export audit logs for.",
|
||||
startDate: "The date to start the export from.",
|
||||
endDate: "The date to end the export at.",
|
||||
offset: "The offset to start from. If you enter 10, it will start from the 10th audit log.",
|
||||
limit: "The number of audit logs to return.",
|
||||
actor: "The actor to filter the audit logs by."
|
||||
}
|
||||
} as const;
|
1
backend/src/lib/api-docs/index.ts
Normal file
1
backend/src/lib/api-docs/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./constants";
|
@ -15,8 +15,16 @@ const envSchema = z
|
||||
PORT: z.coerce.number().default(4000),
|
||||
REDIS_URL: zpStr(z.string()),
|
||||
HOST: zpStr(z.string().default("localhost")),
|
||||
DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")),
|
||||
DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")).default(
|
||||
`postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`
|
||||
),
|
||||
DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()),
|
||||
DB_HOST: zpStr(z.string().describe("Postgres database host").optional()),
|
||||
DB_PORT: zpStr(z.string().describe("Postgres database port").optional()).default("5432"),
|
||||
DB_USER: zpStr(z.string().describe("Postgres database username").optional()),
|
||||
DB_PASSWORD: zpStr(z.string().describe("Postgres database password").optional()),
|
||||
DB_NAME: zpStr(z.string().describe("Postgres database name").optional()),
|
||||
|
||||
NODE_ENV: z.enum(["development", "test", "production"]).default("production"),
|
||||
SALT_ROUNDS: z.coerce.number().default(10),
|
||||
INITIAL_ORGANIZATION_NAME: zpStr(z.string().optional()),
|
||||
@ -98,6 +106,7 @@ const envSchema = z
|
||||
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
|
||||
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
|
||||
LICENSE_KEY: zpStr(z.string().optional()),
|
||||
LICENSE_KEY_OFFLINE: zpStr(z.string().optional()),
|
||||
|
||||
// GENERIC
|
||||
STANDALONE_MODE: z
|
||||
|
@ -17,4 +17,5 @@ export {
|
||||
decryptSecrets,
|
||||
decryptSecretVersions
|
||||
} from "./secret-encryption";
|
||||
export { verifyOfflineLicense } from "./signing";
|
||||
export { generateSrpServerKey, srpCheckClientProof } from "./srp";
|
||||
|
8
backend/src/lib/crypto/license_public_key.pem
Normal file
8
backend/src/lib/crypto/license_public_key.pem
Normal file
@ -0,0 +1,8 @@
|
||||
-----BEGIN RSA PUBLIC KEY-----
|
||||
MIIBCgKCAQEApchBY3BXTu4zWGBguB7nM/pjpVLY3V7VGZOAxmR5ueQTJOwiGM13
|
||||
5HN3EM9fDlQnZu9VSc0OFqRM/bUeUaI1oLPE6WzTHjdHyKjDI/S+TLx3VGEsvhM1
|
||||
uukZpYX+3KX2w4wzRHBaBWyglFy0CVNth9UJhhpD+KKfv7dzcRmsbyoUWi9wGfJu
|
||||
wLYCwaCwZRXIt1sLGmMncPz14vfwdnm2a5Tj1Jbt0GTyBl+1/ZqLbO6SsslLg2G+
|
||||
o7FfGS9z8OUTkvDdu16qxL+p2wCEFZMnOz5BB4oakuT2gS9iOO2l5AOPcT4WzPzy
|
||||
PYbX3d7cN9BkOY9I5z0cX4wzqHjQTvGNLQIDAQAB
|
||||
-----END RSA PUBLIC KEY-----
|
22
backend/src/lib/crypto/signing.ts
Normal file
22
backend/src/lib/crypto/signing.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import crypto, { KeyObject } from "crypto";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
|
||||
export const verifySignature = (data: string, signature: Buffer, publicKey: KeyObject) => {
|
||||
const verify = crypto.createVerify("SHA256");
|
||||
verify.update(data);
|
||||
verify.end();
|
||||
return verify.verify(publicKey, signature);
|
||||
};
|
||||
|
||||
export const verifyOfflineLicense = async (licenseContents: string, signature: string) => {
|
||||
const publicKeyPem = await fs.readFile(path.join(__dirname, "license_public_key.pem"), "utf8");
|
||||
|
||||
const publicKey = crypto.createPublicKey({
|
||||
key: publicKeyPem,
|
||||
format: "pem",
|
||||
type: "pkcs1"
|
||||
});
|
||||
|
||||
return verifySignature(licenseContents, Buffer.from(signature, "base64"), publicKey);
|
||||
};
|
@ -1,17 +1,19 @@
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
export type TOrgPermission = {
|
||||
actor: ActorType;
|
||||
actorId: string;
|
||||
orgId: string;
|
||||
actorOrgId?: string;
|
||||
actorAuthMethod: ActorAuthMethod;
|
||||
actorOrgId: string | undefined;
|
||||
};
|
||||
|
||||
export type TProjectPermission = {
|
||||
actor: ActorType;
|
||||
actorId: string;
|
||||
projectId: string;
|
||||
actorOrgId?: string;
|
||||
actorAuthMethod: ActorAuthMethod;
|
||||
actorOrgId: string | undefined;
|
||||
};
|
||||
|
||||
export type RequiredKeys<T> = {
|
||||
|
@ -6,42 +6,49 @@ import { TServiceTokens, TUsers } from "@app/db/schemas";
|
||||
import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { ActorType, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||
|
||||
export type TAuthMode =
|
||||
| {
|
||||
orgId?: string;
|
||||
authMode: AuthMode.JWT;
|
||||
actor: ActorType.USER;
|
||||
userId: string;
|
||||
tokenVersionId: string; // the session id of token used
|
||||
user: TUsers;
|
||||
orgId?: string;
|
||||
authMethod: AuthMethod;
|
||||
}
|
||||
| {
|
||||
authMode: AuthMode.API_KEY;
|
||||
authMethod: null;
|
||||
actor: ActorType.USER;
|
||||
userId: string;
|
||||
user: TUsers;
|
||||
orgId?: string;
|
||||
orgId: string;
|
||||
}
|
||||
| {
|
||||
authMode: AuthMode.SERVICE_TOKEN;
|
||||
serviceToken: TServiceTokens & { createdByEmail: string };
|
||||
actor: ActorType.SERVICE;
|
||||
serviceTokenId: string;
|
||||
orgId: string;
|
||||
authMethod: null;
|
||||
}
|
||||
| {
|
||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN;
|
||||
actor: ActorType.IDENTITY;
|
||||
identityId: string;
|
||||
identityName: string;
|
||||
orgId: string;
|
||||
authMethod: null;
|
||||
}
|
||||
| {
|
||||
authMode: AuthMode.SCIM_TOKEN;
|
||||
actor: ActorType.SCIM_CLIENT;
|
||||
scimTokenId: string;
|
||||
orgId: string;
|
||||
authMethod: null;
|
||||
};
|
||||
|
||||
const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
|
||||
@ -50,6 +57,7 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
|
||||
return { authMode: AuthMode.API_KEY, token: apiKey, actor: ActorType.USER } as const;
|
||||
}
|
||||
const authHeader = req.headers?.authorization;
|
||||
|
||||
if (!authHeader) return { authMode: null, token: null };
|
||||
|
||||
const authTokenValue = authHeader.slice(7); // slice of after Bearer
|
||||
@ -71,6 +79,7 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
|
||||
actor: ActorType.USER
|
||||
} as const;
|
||||
case AuthTokenType.API_KEY:
|
||||
// throw new Error("API Key auth is no longer supported.");
|
||||
return { authMode: AuthMode.API_KEY, token: decodedToken, actor: ActorType.USER } as const;
|
||||
case AuthTokenType.IDENTITY_ACCESS_TOKEN:
|
||||
return {
|
||||
@ -89,17 +98,30 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ! Important: You can only 100% count on the `req.permission.orgId` field being present when the auth method is Identity Access Token (Machine Identity).
|
||||
export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
||||
server.decorateRequest("auth", null);
|
||||
server.addHook("onRequest", async (req) => {
|
||||
const appCfg = getConfig();
|
||||
const { authMode, token, actor } = await extractAuth(req, appCfg.AUTH_SECRET);
|
||||
|
||||
if (req.url.includes("/api/v3/auth/")) {
|
||||
return;
|
||||
}
|
||||
if (!authMode) return;
|
||||
|
||||
switch (authMode) {
|
||||
case AuthMode.JWT: {
|
||||
const { user, tokenVersionId, orgId } = await server.services.authToken.fnValidateJwtIdentity(token);
|
||||
req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor, orgId };
|
||||
req.auth = {
|
||||
authMode: AuthMode.JWT,
|
||||
user,
|
||||
userId: user.id,
|
||||
tokenVersionId,
|
||||
actor,
|
||||
orgId,
|
||||
authMethod: token.authMethod
|
||||
};
|
||||
break;
|
||||
}
|
||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||
@ -107,29 +129,40 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
||||
req.auth = {
|
||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
||||
actor,
|
||||
orgId: identity.orgId,
|
||||
identityId: identity.identityId,
|
||||
identityName: identity.name
|
||||
identityName: identity.name,
|
||||
authMethod: null
|
||||
};
|
||||
break;
|
||||
}
|
||||
case AuthMode.SERVICE_TOKEN: {
|
||||
const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token);
|
||||
req.auth = {
|
||||
orgId: serviceToken.orgId,
|
||||
authMode: AuthMode.SERVICE_TOKEN as const,
|
||||
serviceToken,
|
||||
serviceTokenId: serviceToken.id,
|
||||
actor
|
||||
actor,
|
||||
authMethod: null
|
||||
};
|
||||
break;
|
||||
}
|
||||
case AuthMode.API_KEY: {
|
||||
const user = await server.services.apiKey.fnValidateApiKey(token as string);
|
||||
req.auth = { authMode: AuthMode.API_KEY as const, userId: user.id, actor, user };
|
||||
req.auth = {
|
||||
authMode: AuthMode.API_KEY as const,
|
||||
userId: user.id,
|
||||
actor,
|
||||
user,
|
||||
orgId: "API_KEY", // We set the orgId to an arbitrary value, since we can't link an API key to a specific org. We have to deprecate API keys soon!
|
||||
authMethod: null
|
||||
};
|
||||
break;
|
||||
}
|
||||
case AuthMode.SCIM_TOKEN: {
|
||||
const { orgId, scimTokenId } = await server.services.scim.fnValidateScimToken(token);
|
||||
req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId };
|
||||
req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId, authMethod: null };
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -9,13 +9,33 @@ export const injectPermission = fp(async (server) => {
|
||||
if (!req.auth) return;
|
||||
|
||||
if (req.auth.actor === ActorType.USER) {
|
||||
req.permission = { type: ActorType.USER, id: req.auth.userId, orgId: req.auth?.orgId };
|
||||
req.permission = {
|
||||
type: ActorType.USER,
|
||||
id: req.auth.userId,
|
||||
orgId: req.auth.orgId, // if the req.auth.authMode is AuthMode.API_KEY, the orgId will be "API_KEY"
|
||||
authMethod: req.auth.authMethod // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null
|
||||
};
|
||||
} else if (req.auth.actor === ActorType.IDENTITY) {
|
||||
req.permission = { type: ActorType.IDENTITY, id: req.auth.identityId };
|
||||
req.permission = {
|
||||
type: ActorType.IDENTITY,
|
||||
id: req.auth.identityId,
|
||||
orgId: req.auth.orgId,
|
||||
authMethod: null
|
||||
};
|
||||
} else if (req.auth.actor === ActorType.SERVICE) {
|
||||
req.permission = { type: ActorType.SERVICE, id: req.auth.serviceTokenId };
|
||||
req.permission = {
|
||||
type: ActorType.SERVICE,
|
||||
id: req.auth.serviceTokenId,
|
||||
orgId: req.auth.orgId,
|
||||
authMethod: null
|
||||
};
|
||||
} else if (req.auth.actor === ActorType.SCIM_CLIENT) {
|
||||
req.permission = { type: ActorType.SCIM_CLIENT, id: req.auth.scimTokenId, orgId: req.auth.orgId };
|
||||
req.permission = {
|
||||
type: ActorType.SCIM_CLIENT,
|
||||
id: req.auth.scimTokenId,
|
||||
orgId: req.auth.orgId,
|
||||
authMethod: null
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -3,15 +3,26 @@ import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
interface TAuthOptions {
|
||||
requireOrg: boolean;
|
||||
}
|
||||
|
||||
export const verifyAuth =
|
||||
<T extends FastifyRequest>(authStrats: AuthMode[]) =>
|
||||
<T extends FastifyRequest>(authStrategies: AuthMode[], options: TAuthOptions = { requireOrg: true }) =>
|
||||
(req: T, _res: FastifyReply, done: HookHandlerDoneFunction) => {
|
||||
if (!Array.isArray(authStrats)) throw new Error("Auth strategy must be array");
|
||||
if (!Array.isArray(authStrategies)) throw new Error("Auth strategy must be array");
|
||||
if (!req.auth) throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" });
|
||||
|
||||
const isAccessAllowed = authStrats.some((strat) => strat === req.auth.authMode);
|
||||
const isAccessAllowed = authStrategies.some((strategy) => strategy === req.auth.authMode);
|
||||
if (!isAccessAllowed) {
|
||||
throw new UnauthorizedError({ name: `${req.url} Unauthorized Access` });
|
||||
}
|
||||
|
||||
// New optional option. There are some routes which do not require an organization ID to be present on the request.
|
||||
// An example of this is the /v1 auth routes.
|
||||
if (req.auth.authMode === AuthMode.JWT && options.requireOrg === true && !req.permission.orgId) {
|
||||
throw new UnauthorizedError({ name: `${req.url} Unauthorized Access, no organization found in request` });
|
||||
}
|
||||
|
||||
done();
|
||||
};
|
||||
|
@ -53,6 +53,7 @@ import { identityServiceFactory } from "@app/services/identity/identity-service"
|
||||
import { identityAccessTokenDALFactory } from "@app/services/identity-access-token/identity-access-token-dal";
|
||||
import { identityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
||||
import { identityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
import { identityProjectMembershipRoleDALFactory } from "@app/services/identity-project/identity-project-membership-role-dal";
|
||||
import { identityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||
import { identityUaClientSecretDALFactory } from "@app/services/identity-ua/identity-ua-client-secret-dal";
|
||||
import { identityUaDALFactory } from "@app/services/identity-ua/identity-ua-dal";
|
||||
@ -78,6 +79,7 @@ import { projectKeyDALFactory } from "@app/services/project-key/project-key-dal"
|
||||
import { projectKeyServiceFactory } from "@app/services/project-key/project-key-service";
|
||||
import { projectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
import { projectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
|
||||
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
||||
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
||||
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
||||
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
||||
@ -141,6 +143,7 @@ export const registerRoutes = async (
|
||||
|
||||
const projectDAL = projectDALFactory(db);
|
||||
const projectMembershipDAL = projectMembershipDALFactory(db);
|
||||
const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(db);
|
||||
const projectRoleDAL = projectRoleDALFactory(db);
|
||||
const projectEnvDAL = projectEnvDALFactory(db);
|
||||
const projectKeyDAL = projectKeyDALFactory(db);
|
||||
@ -164,6 +167,7 @@ export const registerRoutes = async (
|
||||
const identityAccessTokenDAL = identityAccessTokenDALFactory(db);
|
||||
const identityOrgMembershipDAL = identityOrgDALFactory(db);
|
||||
const identityProjectDAL = identityProjectDALFactory(db);
|
||||
const identityProjectMembershipRoleDAL = identityProjectMembershipRoleDALFactory(db);
|
||||
|
||||
const identityUaDAL = identityUaDALFactory(db);
|
||||
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
||||
@ -197,7 +201,8 @@ export const registerRoutes = async (
|
||||
permissionDAL,
|
||||
orgRoleDAL,
|
||||
projectRoleDAL,
|
||||
serviceTokenDAL
|
||||
serviceTokenDAL,
|
||||
projectDAL
|
||||
});
|
||||
const licenseService = licenseServiceFactory({ permissionService, orgDAL, licenseDAL, keyStore });
|
||||
const trustedIpService = trustedIpServiceFactory({
|
||||
@ -262,7 +267,7 @@ export const registerRoutes = async (
|
||||
|
||||
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL });
|
||||
const userService = userServiceFactory({ userDAL });
|
||||
const loginService = authLoginServiceFactory({ userDAL, smtpService, tokenService });
|
||||
const loginService = authLoginServiceFactory({ userDAL, smtpService, tokenService, orgDAL, tokenDAL: authTokenDAL });
|
||||
const passwordService = authPaswordServiceFactory({
|
||||
tokenService,
|
||||
smtpService,
|
||||
@ -321,6 +326,7 @@ export const registerRoutes = async (
|
||||
|
||||
const projectMembershipService = projectMembershipServiceFactory({
|
||||
projectMembershipDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
projectDAL,
|
||||
permissionService,
|
||||
projectBotDAL,
|
||||
@ -352,7 +358,8 @@ export const registerRoutes = async (
|
||||
projectBotDAL,
|
||||
projectMembershipDAL,
|
||||
secretApprovalRequestDAL,
|
||||
secretApprovalSecretDAL: sarSecretDAL
|
||||
secretApprovalSecretDAL: sarSecretDAL,
|
||||
projectUserMembershipRoleDAL
|
||||
});
|
||||
|
||||
const projectService = projectServiceFactory({
|
||||
@ -366,11 +373,15 @@ export const registerRoutes = async (
|
||||
projectKeyDAL,
|
||||
userDAL,
|
||||
projectEnvDAL,
|
||||
orgDAL,
|
||||
orgService,
|
||||
projectMembershipDAL,
|
||||
folderDAL,
|
||||
licenseService
|
||||
licenseService,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityProjectMembershipRoleDAL
|
||||
});
|
||||
|
||||
const projectEnvService = projectEnvServiceFactory({
|
||||
permissionService,
|
||||
projectEnvDAL,
|
||||
@ -508,7 +519,8 @@ export const registerRoutes = async (
|
||||
projectEnvDAL,
|
||||
serviceTokenDAL,
|
||||
userDAL,
|
||||
permissionService
|
||||
permissionService,
|
||||
projectDAL
|
||||
});
|
||||
|
||||
const identityService = identityServiceFactory({
|
||||
@ -516,12 +528,17 @@ export const registerRoutes = async (
|
||||
identityDAL,
|
||||
identityOrgMembershipDAL
|
||||
});
|
||||
const identityAccessTokenService = identityAccessTokenServiceFactory({ identityAccessTokenDAL });
|
||||
const identityAccessTokenService = identityAccessTokenServiceFactory({
|
||||
identityAccessTokenDAL,
|
||||
identityOrgMembershipDAL
|
||||
});
|
||||
const identityProjectService = identityProjectServiceFactory({
|
||||
permissionService,
|
||||
projectDAL,
|
||||
identityProjectDAL,
|
||||
identityOrgMembershipDAL
|
||||
identityOrgMembershipDAL,
|
||||
identityProjectMembershipRoleDAL,
|
||||
projectRoleDAL
|
||||
});
|
||||
const identityUaService = identityUaServiceFactory({
|
||||
identityOrgMembershipDAL,
|
||||
|
@ -21,7 +21,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT], { requireOrg: false }),
|
||||
handler: async (req, res) => {
|
||||
const appCfg = getConfig();
|
||||
if (req.auth.authMode === AuthMode.JWT) {
|
||||
@ -85,6 +85,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
||||
|
||||
const token = jwt.sign(
|
||||
{
|
||||
authMethod: decodedToken.authMethod,
|
||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||
userId: decodedToken.userId,
|
||||
tokenVersionId: tokenVersion.id,
|
||||
|
@ -30,6 +30,7 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.params.projectId
|
||||
});
|
||||
return { bot };
|
||||
@ -70,6 +71,7 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
botId: req.params.botId,
|
||||
botKey: req.body.botKey,
|
||||
isActive: req.body.isActive
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
|
||||
|
||||
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/token/renew",
|
||||
@ -7,7 +9,7 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid
|
||||
schema: {
|
||||
description: "Renew access token",
|
||||
body: z.object({
|
||||
accessToken: z.string().trim()
|
||||
accessToken: z.string().trim().describe(UNIVERSAL_AUTH.RENEW_ACCESS_TOKEN.accessToken)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { IDENTITIES } from "@app/lib/api-docs";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -20,9 +21,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
name: z.string().trim(),
|
||||
organizationId: z.string().trim(),
|
||||
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess)
|
||||
name: z.string().trim().describe(IDENTITIES.CREATE.name),
|
||||
organizationId: z.string().trim().describe(IDENTITIES.CREATE.organizationId),
|
||||
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess).describe(IDENTITIES.CREATE.role)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -34,6 +35,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
const identity = await server.services.identity.createIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
orgId: req.body.organizationId
|
||||
@ -78,11 +80,11 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
identityId: z.string().describe(IDENTITIES.UPDATE.identityId)
|
||||
}),
|
||||
body: z.object({
|
||||
name: z.string().trim().optional(),
|
||||
role: z.string().trim().min(1).optional()
|
||||
name: z.string().trim().optional().describe(IDENTITIES.UPDATE.name),
|
||||
role: z.string().trim().min(1).optional().describe(IDENTITIES.UPDATE.role)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -94,6 +96,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
const identity = await server.services.identity.updateIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.identityId,
|
||||
...req.body
|
||||
@ -127,7 +130,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
identityId: z.string().describe(IDENTITIES.DELETE.identityId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -139,6 +142,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
const identity = await server.services.identity.deleteIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.identityId
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
@ -26,8 +27,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
schema: {
|
||||
description: "Login with Universal Auth",
|
||||
body: z.object({
|
||||
clientId: z.string().trim(),
|
||||
clientSecret: z.string().trim()
|
||||
clientId: z.string().trim().describe(UNIVERSAL_AUTH.LOGIN.clientId),
|
||||
clientSecret: z.string().trim().describe(UNIVERSAL_AUTH.LOGIN.clientSecret)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -76,7 +77,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string().trim()
|
||||
identityId: z.string().trim().describe(UNIVERSAL_AUTH.ATTACH.identityId)
|
||||
}),
|
||||
body: z.object({
|
||||
clientSecretTrustedIps: z
|
||||
@ -85,14 +86,16 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
|
||||
.describe(UNIVERSAL_AUTH.ATTACH.clientSecretTrustedIps),
|
||||
accessTokenTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim()
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
|
||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenTrustedIps),
|
||||
accessTokenTTL: z
|
||||
.number()
|
||||
.int()
|
||||
@ -100,15 +103,22 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
.refine((value) => value !== 0, {
|
||||
message: "accessTokenTTL must have a non zero number"
|
||||
})
|
||||
.default(2592000),
|
||||
.default(2592000)
|
||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenTTL), // 30 days
|
||||
accessTokenMaxTTL: z
|
||||
.number()
|
||||
.int()
|
||||
.refine((value) => value !== 0, {
|
||||
message: "accessTokenMaxTTL must have a non zero number"
|
||||
})
|
||||
.default(2592000), // 30 days
|
||||
accessTokenNumUsesLimit: z.number().int().min(0).default(0)
|
||||
.default(2592000)
|
||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenMaxTTL), // 30 days
|
||||
accessTokenNumUsesLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenNumUsesLimit)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -121,6 +131,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
@ -156,7 +167,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
identityId: z.string().describe(UNIVERSAL_AUTH.UPDATE.identityId)
|
||||
}),
|
||||
body: z.object({
|
||||
clientSecretTrustedIps: z
|
||||
@ -165,16 +176,23 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.optional(),
|
||||
.optional()
|
||||
.describe(UNIVERSAL_AUTH.UPDATE.clientSecretTrustedIps),
|
||||
accessTokenTrustedIps: z
|
||||
.object({
|
||||
ipAddress: z.string().trim()
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
.optional(),
|
||||
accessTokenTTL: z.number().int().min(0).optional(),
|
||||
accessTokenNumUsesLimit: z.number().int().min(0).optional(),
|
||||
.optional()
|
||||
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenTrustedIps),
|
||||
accessTokenTTL: z.number().int().min(0).optional().describe(UNIVERSAL_AUTH.UPDATE.accessTokenTTL),
|
||||
accessTokenNumUsesLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.optional()
|
||||
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenNumUsesLimit),
|
||||
accessTokenMaxTTL: z
|
||||
.number()
|
||||
.int()
|
||||
@ -182,6 +200,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
message: "accessTokenMaxTTL must have a non zero number"
|
||||
})
|
||||
.optional()
|
||||
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenMaxTTL)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -194,6 +213,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
@ -230,7 +250,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
identityId: z.string().describe(UNIVERSAL_AUTH.RETRIEVE.identityId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -242,6 +262,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
const identityUniversalAuth = await server.services.identityUa.getIdentityUa({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
@ -273,12 +294,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
identityId: z.string().describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.identityId)
|
||||
}),
|
||||
body: z.object({
|
||||
description: z.string().trim().default(""),
|
||||
numUsesLimit: z.number().min(0).default(0),
|
||||
ttl: z.number().min(0).default(0)
|
||||
description: z.string().trim().default("").describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.description),
|
||||
numUsesLimit: z.number().min(0).default(0).describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.numUsesLimit),
|
||||
ttl: z.number().min(0).default(0).describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.ttl)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -291,6 +312,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
const { clientSecret, clientSecretData, orgId } = await server.services.identityUa.createUaClientSecret({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
...req.body
|
||||
@ -324,7 +346,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
identityId: z.string().describe(UNIVERSAL_AUTH.LIST_CLIENT_SECRETS.identityId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -336,6 +358,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUaClientSecrets({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
@ -366,8 +389,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
identityId: z.string(),
|
||||
clientSecretId: z.string()
|
||||
identityId: z.string().describe(UNIVERSAL_AUTH.REVOKE_CLIENT_SECRET.identityId),
|
||||
clientSecretId: z.string().describe(UNIVERSAL_AUTH.REVOKE_CLIENT_SECRET.clientSecretId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -379,6 +402,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
const clientSecretData = await server.services.identityUa.revokeUaClientSecret({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
clientSecretId: req.params.clientSecretId
|
||||
|
@ -53,6 +53,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const integrationAuth = await server.services.integrationAuth.getIntegrationAuth({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
@ -80,6 +81,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
integration: req.query.integration,
|
||||
projectId: req.query.projectId
|
||||
});
|
||||
@ -117,6 +119,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuthById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
@ -157,6 +160,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const integrationAuth = await server.services.integrationAuth.oauthExchange({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.body.workspaceId,
|
||||
...req.body
|
||||
@ -200,6 +204,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.body.workspaceId,
|
||||
...req.body
|
||||
@ -247,6 +252,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const apps = await server.services.integrationAuth.getIntegrationApps({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
...req.query
|
||||
@ -278,6 +284,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const teams = await server.services.integrationAuth.getIntegrationAuthTeams({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
@ -306,6 +313,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const branches = await server.services.integrationAuth.getVercelBranches({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
appId: req.query.appId
|
||||
@ -335,6 +343,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const groups = await server.services.integrationAuth.getChecklyGroups({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
accountId: req.query.accountId
|
||||
@ -343,6 +352,68 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/github/orgs",
|
||||
method: "GET",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
integrationAuthId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
orgs: z.object({ name: z.string(), orgId: z.string() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const orgs = await server.services.integrationAuth.getGithubOrgs({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
if (!orgs) throw new Error("No organization found.");
|
||||
|
||||
return { orgs };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/github/envs",
|
||||
method: "GET",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
integrationAuthId: z.string().trim()
|
||||
}),
|
||||
querystring: z.object({
|
||||
repoOwner: z.string().trim(),
|
||||
repoName: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
envs: z.object({ name: z.string(), envId: z.string() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const envs = await server.services.integrationAuth.getGithubEnvs({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
repoName: req.query.repoName,
|
||||
repoOwner: req.query.repoOwner
|
||||
});
|
||||
if (!envs) throw new Error("No organization found.");
|
||||
|
||||
return { envs };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/qovery/orgs",
|
||||
method: "GET",
|
||||
@ -361,6 +432,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const orgs = await server.services.integrationAuth.getQoveryOrgs({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
@ -389,6 +461,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const projects = await server.services.integrationAuth.getQoveryProjects({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
orgId: req.query.orgId
|
||||
@ -418,6 +491,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const environments = await server.services.integrationAuth.getQoveryEnvs({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
projectId: req.query.projectId
|
||||
@ -447,6 +521,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const apps = await server.services.integrationAuth.getQoveryApps({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
environmentId: req.query.environmentId
|
||||
@ -476,6 +551,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const containers = await server.services.integrationAuth.getQoveryContainers({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
environmentId: req.query.environmentId
|
||||
@ -505,6 +581,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const jobs = await server.services.integrationAuth.getQoveryJobs({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
environmentId: req.query.environmentId
|
||||
@ -537,6 +614,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const pipelines = await server.services.integrationAuth.getHerokuPipelines({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
@ -565,6 +643,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const environments = await server.services.integrationAuth.getRailwayEnvironments({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
appId: req.query.appId
|
||||
@ -594,6 +673,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const services = await server.services.integrationAuth.getRailwayServices({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
appId: req.query.appId
|
||||
@ -630,6 +710,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const workspaces = await server.services.integrationAuth.getBitbucketWorkspaces({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
@ -663,6 +744,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const secretGroups = await server.services.integrationAuth.getNorthFlankSecretGroups({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
appId: req.query.appId
|
||||
@ -697,6 +779,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
const buildConfigs = await server.services.integrationAuth.getTeamcityBuildConfigs({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationAuthId,
|
||||
appId: req.query.appId
|
||||
|
@ -33,6 +33,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
secretPrefix: z.string().optional(),
|
||||
secretSuffix: z.string().optional(),
|
||||
initialSyncBehavior: z.string().optional(),
|
||||
shouldAutoRedeploy: z.boolean().optional(),
|
||||
secretGCPLabel: z
|
||||
.object({
|
||||
labelName: z.string(),
|
||||
@ -53,6 +54,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
const { integration, integrationAuth } = await server.services.integration.createIntegration({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body
|
||||
});
|
||||
@ -123,6 +125,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
const integration = await server.services.integration.updateIntegration({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationId,
|
||||
...req.body
|
||||
@ -148,6 +151,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
handler: async (req) => {
|
||||
const integration = await server.services.integration.deleteIntegration({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.integrationId
|
||||
|
@ -29,6 +29,7 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
||||
orgId: req.body.organizationId,
|
||||
userId: req.permission.id,
|
||||
inviteeEmail: req.body.inviteeEmail,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
|
@ -15,7 +15,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT], { requireOrg: false }),
|
||||
handler: async (req) => {
|
||||
const organizations = await server.services.org.findAllOrganizationOfUser(req.permission.id);
|
||||
return { organizations };
|
||||
@ -40,6 +40,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
const organization = await server.services.org.findOrganizationById(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { organization };
|
||||
@ -76,6 +77,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
const users = await server.services.org.findAllOrgMembers(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { users };
|
||||
@ -111,6 +113,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId,
|
||||
data: req.body
|
||||
});
|
||||
@ -138,6 +141,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
const incidentContactsOrg = await req.server.services.org.findIncidentContacts(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { incidentContactsOrg };
|
||||
@ -162,6 +166,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.body.email,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { incidentContactsOrg };
|
||||
@ -185,6 +190,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.params.incidentContactId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { incidentContactsOrg };
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { ProjectEnvironmentsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ENVIRONMENTS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -18,11 +19,11 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(ENVIRONMENTS.CREATE.workspaceId)
|
||||
}),
|
||||
body: z.object({
|
||||
name: z.string().trim(),
|
||||
slug: z.string().trim()
|
||||
name: z.string().trim().describe(ENVIRONMENTS.CREATE.name),
|
||||
slug: z.string().trim().describe(ENVIRONMENTS.CREATE.slug)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -38,6 +39,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.params.workspaceId,
|
||||
...req.body
|
||||
});
|
||||
@ -73,13 +75,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
id: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(ENVIRONMENTS.UPDATE.workspaceId),
|
||||
id: z.string().trim().describe(ENVIRONMENTS.UPDATE.id)
|
||||
}),
|
||||
body: z.object({
|
||||
slug: z.string().trim().optional(),
|
||||
name: z.string().trim().optional(),
|
||||
position: z.number().optional()
|
||||
slug: z.string().trim().optional().describe(ENVIRONMENTS.UPDATE.slug),
|
||||
name: z.string().trim().optional().describe(ENVIRONMENTS.UPDATE.name),
|
||||
position: z.number().optional().describe(ENVIRONMENTS.UPDATE.position)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -94,6 +96,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
const { environment, old } = await server.services.projectEnv.updateEnvironment({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
id: req.params.id,
|
||||
@ -136,8 +139,8 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
id: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(ENVIRONMENTS.DELETE.workspaceId),
|
||||
id: z.string().trim().describe(ENVIRONMENTS.DELETE.id)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -152,6 +155,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
const environment = await server.services.projectEnv.deleteEnvironment({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
id: req.params.id
|
||||
|
@ -30,6 +30,7 @@ export const registerProjectKeyRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.params.workspaceId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
nonce: req.body.key.nonce,
|
||||
receiverId: req.body.key.userId,
|
||||
|
@ -1,15 +1,18 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
OrgMembershipsSchema,
|
||||
ProjectMembershipRole,
|
||||
ProjectMembershipsSchema,
|
||||
ProjectUserMembershipRolesSchema,
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||
|
||||
export const registerProjectMembershipRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@ -24,20 +27,35 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(PROJECTS.GET_USER_MEMBERSHIPS.workspaceId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
memberships: ProjectMembershipsSchema.merge(
|
||||
z.object({
|
||||
user: UsersSchema.pick({
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
id: true
|
||||
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true }))
|
||||
})
|
||||
)
|
||||
memberships: ProjectMembershipsSchema.omit({ role: true })
|
||||
.merge(
|
||||
z.object({
|
||||
user: UsersSchema.pick({
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
id: true
|
||||
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
|
||||
roles: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
role: z.string(),
|
||||
customRoleId: z.string().optional().nullable(),
|
||||
customRoleName: z.string().optional().nullable(),
|
||||
customRoleSlug: z.string().optional().nullable(),
|
||||
isTemporary: z.boolean(),
|
||||
temporaryMode: z.string().optional().nullable(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional()
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
.omit({ createdAt: true, updatedAt: true })
|
||||
.array()
|
||||
})
|
||||
@ -48,6 +66,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
const memberships = await server.services.projectMembership.getProjectMemberships({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
@ -84,12 +103,10 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
const data = await server.services.projectMembership.addUsersToProject({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
members: req.body.members.map((member) => ({
|
||||
...member,
|
||||
projectRole: ProjectMembershipRole.Member
|
||||
}))
|
||||
members: req.body.members
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@ -120,43 +137,62 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
membershipId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(PROJECTS.UPDATE_USER_MEMBERSHIP.workspaceId),
|
||||
membershipId: z.string().trim().describe(PROJECTS.UPDATE_USER_MEMBERSHIP.membershipId)
|
||||
}),
|
||||
body: z.object({
|
||||
role: z.string().trim()
|
||||
roles: z
|
||||
.array(
|
||||
z.union([
|
||||
z.object({
|
||||
role: z.string(),
|
||||
isTemporary: z.literal(false).default(false)
|
||||
}),
|
||||
z.object({
|
||||
role: z.string(),
|
||||
isTemporary: z.literal(true),
|
||||
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
|
||||
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
||||
temporaryAccessStartTime: z.string().datetime()
|
||||
})
|
||||
])
|
||||
)
|
||||
.min(1)
|
||||
.refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least long lived role is required")
|
||||
.describe(PROJECTS.UPDATE_USER_MEMBERSHIP.roles)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
membership: ProjectMembershipsSchema
|
||||
roles: ProjectUserMembershipRolesSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const membership = await server.services.projectMembership.updateProjectMembership({
|
||||
const roles = await server.services.projectMembership.updateProjectMembership({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
membershipId: req.params.membershipId,
|
||||
role: req.body.role
|
||||
roles: req.body.roles
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.params.workspaceId,
|
||||
event: {
|
||||
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
|
||||
metadata: {
|
||||
userId: membership.userId,
|
||||
newRole: req.body.role,
|
||||
oldRole: membership.role,
|
||||
email: ""
|
||||
}
|
||||
}
|
||||
});
|
||||
return { membership };
|
||||
// await server.services.auditLog.createAuditLog({
|
||||
// ...req.auditLogInfo,
|
||||
// projectId: req.params.workspaceId,
|
||||
// event: {
|
||||
// type: EventType.UPDATE_USER_WORKSPACE_ROLE,
|
||||
// metadata: {
|
||||
// userId: membership.userId,
|
||||
// newRole: req.body.role,
|
||||
// oldRole: membership.role,
|
||||
// email: ""
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
return { roles };
|
||||
}
|
||||
});
|
||||
|
||||
@ -186,6 +222,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
const membership = await server.services.projectMembership.deleteProjectMembership({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
membershipId: req.params.membershipId
|
||||
|
@ -7,8 +7,10 @@ import {
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||
|
||||
import { integrationAuthPubSchema } from "../sanitizedSchemas";
|
||||
import { sanitizedServiceTokenSchema } from "../v2/service-token-router";
|
||||
@ -44,6 +46,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
const publicKeys = await server.services.projectKey.getProjectPublicKeys({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
@ -60,17 +63,32 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
users: ProjectMembershipsSchema.merge(
|
||||
z.object({
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
id: true
|
||||
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true }))
|
||||
})
|
||||
)
|
||||
users: ProjectMembershipsSchema.omit({ role: true })
|
||||
.merge(
|
||||
z.object({
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
id: true
|
||||
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
|
||||
roles: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
role: z.string(),
|
||||
customRoleId: z.string().optional().nullable(),
|
||||
customRoleName: z.string().optional().nullable(),
|
||||
customRoleSlug: z.string().optional().nullable(),
|
||||
isTemporary: z.boolean(),
|
||||
temporaryMode: z.string().optional().nullable(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional()
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
.omit({ createdAt: true, updatedAt: true })
|
||||
.array()
|
||||
})
|
||||
@ -81,6 +99,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
const users = await server.services.projectMembership.getProjectMemberships({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.params.workspaceId,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
@ -110,7 +129,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
method: "GET",
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(PROJECTS.GET.workspaceId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -121,37 +140,14 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const workspace = await server.services.project.getAProject({
|
||||
filter: {
|
||||
type: ProjectFilterType.ID,
|
||||
projectId: req.params.workspaceId
|
||||
},
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
return { workspace };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceName: z.string().trim(),
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
workspace: projectWithEnv
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const workspace = await server.services.project.createProject({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
orgId: req.body.organizationId,
|
||||
actorOrgId: req.permission.orgId,
|
||||
workspaceName: req.body.workspaceName
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
return { workspace };
|
||||
}
|
||||
@ -162,7 +158,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
method: "DELETE",
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(PROJECTS.DELETE.workspaceId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -173,10 +169,14 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const workspace = await server.services.project.deleteProject({
|
||||
filter: {
|
||||
type: ProjectFilterType.ID,
|
||||
projectId: req.params.workspaceId
|
||||
},
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
return { workspace };
|
||||
}
|
||||
@ -204,6 +204,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
const workspace = await server.services.project.updateName({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
name: req.body.name
|
||||
@ -220,11 +221,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
method: "PATCH",
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(PROJECTS.UPDATE.workspaceId)
|
||||
}),
|
||||
body: z.object({
|
||||
name: z.string().trim().max(64, { message: "Name must be 64 or fewer characters" }).optional(),
|
||||
autoCapitalization: z.boolean().optional()
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(64, { message: "Name must be 64 or fewer characters" })
|
||||
.optional()
|
||||
.describe(PROJECTS.UPDATE.name),
|
||||
autoCapitalization: z.boolean().optional().describe(PROJECTS.UPDATE.autoCapitalization)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -232,17 +238,21 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const workspace = await server.services.project.updateProject({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
filter: {
|
||||
type: ProjectFilterType.ID,
|
||||
projectId: req.params.workspaceId
|
||||
},
|
||||
update: {
|
||||
name: req.body.name,
|
||||
autoCapitalization: req.body.autoCapitalization
|
||||
}
|
||||
},
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
return {
|
||||
workspace
|
||||
@ -272,6 +282,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
const workspace = await server.services.project.toggleAutoCapitalization({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
autoCapitalization: req.body.autoCapitalization
|
||||
@ -308,6 +319,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
handler: async (req) => {
|
||||
const integrations = await server.services.integration.listIntegrationByProject({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
@ -333,6 +345,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
handler: async (req) => {
|
||||
const authorizations = await server.services.integrationAuth.listIntegrationAuthByProjectId({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
@ -358,6 +371,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
handler: async (req) => {
|
||||
const serviceTokenData = await server.services.serviceToken.getProjectServiceTokens({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { SecretFoldersSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { FOLDERS } from "@app/lib/api-docs";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -19,12 +20,12 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
name: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
workspaceId: z.string().trim().describe(FOLDERS.CREATE.workspaceId),
|
||||
environment: z.string().trim().describe(FOLDERS.CREATE.environment),
|
||||
name: z.string().trim().describe(FOLDERS.CREATE.name),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.CREATE.path),
|
||||
// backward compatiability with cli
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.CREATE.directory)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -38,6 +39,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
const folder = await server.services.folder.createFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
projectId: req.body.workspaceId,
|
||||
@ -73,15 +75,15 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
],
|
||||
params: z.object({
|
||||
// old way this was name
|
||||
folderId: z.string()
|
||||
folderId: z.string().describe(FOLDERS.UPDATE.folderId)
|
||||
}),
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
name: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
workspaceId: z.string().trim().describe(FOLDERS.UPDATE.workspaceId),
|
||||
environment: z.string().trim().describe(FOLDERS.UPDATE.environment),
|
||||
name: z.string().trim().describe(FOLDERS.UPDATE.name),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.UPDATE.path),
|
||||
// backward compatiability with cli
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.UPDATE.directory)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -95,6 +97,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
const { folder, old } = await server.services.folder.updateFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
projectId: req.body.workspaceId,
|
||||
@ -119,6 +122,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(daniel): Expose this route in api reference and write docs for it.
|
||||
server.route({
|
||||
url: "/:folderIdOrName",
|
||||
method: "DELETE",
|
||||
@ -131,14 +135,14 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
folderIdOrName: z.string()
|
||||
folderIdOrName: z.string().describe(FOLDERS.DELETE.folderIdOrName)
|
||||
}),
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
workspaceId: z.string().trim().describe(FOLDERS.DELETE.workspaceId),
|
||||
environment: z.string().trim().describe(FOLDERS.DELETE.environment),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.DELETE.path),
|
||||
// keep this here as cli need directory
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.DELETE.directory)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -152,6 +156,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
const folder = await server.services.folder.deleteFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
projectId: req.body.workspaceId,
|
||||
@ -187,11 +192,11 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
],
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
workspaceId: z.string().trim().describe(FOLDERS.LIST.workspaceId),
|
||||
environment: z.string().trim().describe(FOLDERS.LIST.environment),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.LIST.path),
|
||||
// backward compatiability with cli
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.LIST.directory)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -205,6 +210,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
const folders = await server.services.folder.getFolders({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query,
|
||||
projectId: req.query.workspaceId,
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { SECRET_IMPORTS } from "@app/lib/api-docs";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -19,12 +20,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.CREATE.workspaceId),
|
||||
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.environment),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.path),
|
||||
import: z.object({
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().transform(removeTrailingSlash)
|
||||
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.import.environment),
|
||||
path: z.string().trim().transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.import.path)
|
||||
})
|
||||
}),
|
||||
response: {
|
||||
@ -43,6 +44,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
const secretImport = await server.services.secretImport.createImport({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
projectId: req.body.workspaceId,
|
||||
@ -80,20 +82,21 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
secretImportId: z.string().trim()
|
||||
secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId)
|
||||
}),
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.workspaceId),
|
||||
environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path),
|
||||
import: z.object({
|
||||
environment: z.string().trim().optional(),
|
||||
environment: z.string().trim().optional().describe(SECRET_IMPORTS.UPDATE.import.environment),
|
||||
path: z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.transform((val) => (val ? removeTrailingSlash(val) : val)),
|
||||
position: z.number().optional()
|
||||
.transform((val) => (val ? removeTrailingSlash(val) : val))
|
||||
.describe(SECRET_IMPORTS.UPDATE.import.path),
|
||||
position: z.number().optional().describe(SECRET_IMPORTS.UPDATE.import.position)
|
||||
})
|
||||
}),
|
||||
response: {
|
||||
@ -112,6 +115,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
const secretImport = await server.services.secretImport.updateImport({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.secretImportId,
|
||||
...req.body,
|
||||
@ -150,12 +154,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
secretImportId: z.string().trim()
|
||||
secretImportId: z.string().trim().describe(SECRET_IMPORTS.DELETE.secretImportId)
|
||||
}),
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.DELETE.workspaceId),
|
||||
environment: z.string().trim().describe(SECRET_IMPORTS.DELETE.environment),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.DELETE.path)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -173,6 +177,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
const secretImport = await server.services.secretImport.deleteImport({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.secretImportId,
|
||||
...req.body,
|
||||
@ -210,9 +215,9 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
],
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.LIST.workspaceId),
|
||||
environment: z.string().trim().describe(SECRET_IMPORTS.LIST.environment),
|
||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.LIST.path)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -232,6 +237,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
const secretImports = await server.services.secretImport.getImports({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query,
|
||||
projectId: req.query.workspaceId
|
||||
@ -285,6 +291,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
const importedSecrets = await server.services.secretImport.getSecretsFromImports({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query,
|
||||
projectId: req.query.workspaceId
|
||||
|
@ -23,6 +23,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
|
||||
const workspaceTags = await server.services.secretTag.getProjectTags({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId
|
||||
});
|
||||
@ -53,6 +54,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
|
||||
const workspaceTag = await server.services.secretTag.createTag({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId,
|
||||
...req.body
|
||||
@ -80,6 +82,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
|
||||
const workspaceTag = await server.services.secretTag.deleteTag({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.tagId
|
||||
});
|
||||
|
@ -15,7 +15,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT], { requireOrg: false }),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.getMe(req.permission.id);
|
||||
return { user };
|
||||
|
@ -47,6 +47,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
const webhook = await server.services.webhook.createWebhook({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.body.workspaceId,
|
||||
...req.body
|
||||
@ -93,6 +94,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
const webhook = await server.services.webhook.updateWebhook({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.webhookId,
|
||||
isDisabled: req.body.isDisabled
|
||||
@ -130,6 +132,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
const webhook = await server.services.webhook.deleteWebhook({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.webhookId
|
||||
});
|
||||
@ -172,6 +175,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
const webhook = await server.services.webhook.testWebhook({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.webhookId
|
||||
});
|
||||
@ -204,6 +208,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
const webhooks = await server.services.webhook.listWebhooks({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query,
|
||||
projectId: req.query.workspaceId
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -18,7 +19,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
orgId: z.string().trim()
|
||||
orgId: z.string().trim().describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orgId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -41,6 +42,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
||||
const identityMemberships = await server.services.identity.listOrgIdentities({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.orgId
|
||||
});
|
||||
|
@ -1,13 +1,16 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
IdentitiesSchema,
|
||||
IdentityProjectMembershipsSchema,
|
||||
ProjectMembershipRole,
|
||||
ProjectRolesSchema
|
||||
ProjectUserMembershipRolesSchema
|
||||
} from "@app/db/schemas";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||
|
||||
export const registerIdentityProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@ -32,6 +35,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
const identityMembership = await server.services.identityProject.createProjectIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
projectId: req.params.projectId,
|
||||
@ -53,28 +57,46 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
projectId: z.string().trim(),
|
||||
identityId: z.string().trim()
|
||||
projectId: z.string().trim().describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.projectId),
|
||||
identityId: z.string().trim().describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.identityId)
|
||||
}),
|
||||
body: z.object({
|
||||
role: z.string().trim().min(1).default(ProjectMembershipRole.NoAccess)
|
||||
roles: z
|
||||
.array(
|
||||
z.union([
|
||||
z.object({
|
||||
role: z.string(),
|
||||
isTemporary: z.literal(false).default(false)
|
||||
}),
|
||||
z.object({
|
||||
role: z.string(),
|
||||
isTemporary: z.literal(true),
|
||||
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
|
||||
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
||||
temporaryAccessStartTime: z.string().datetime()
|
||||
})
|
||||
])
|
||||
)
|
||||
.min(1)
|
||||
.describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.roles)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identityMembership: IdentityProjectMembershipsSchema
|
||||
roles: ProjectUserMembershipRolesSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const identityMembership = await server.services.identityProject.updateProjectIdentity({
|
||||
const roles = await server.services.identityProject.updateProjectIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
projectId: req.params.projectId,
|
||||
role: req.body.role
|
||||
roles: req.body.roles
|
||||
});
|
||||
return { identityMembership };
|
||||
return { roles };
|
||||
}
|
||||
});
|
||||
|
||||
@ -90,8 +112,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
projectId: z.string().trim(),
|
||||
identityId: z.string().trim()
|
||||
projectId: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.projectId),
|
||||
identityId: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.identityId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -103,6 +125,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
const identityMembership = await server.services.identityProject.deleteProjectIdentity({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
projectId: req.params.projectId
|
||||
@ -123,22 +146,33 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
projectId: z.string().trim().describe(PROJECTS.LIST_IDENTITY_MEMBERSHIPS.projectId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identityMemberships: IdentityProjectMembershipsSchema.merge(
|
||||
z.object({
|
||||
customRole: ProjectRolesSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
permissions: true,
|
||||
description: true
|
||||
}).optional(),
|
||||
identityMemberships: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
identityId: z.string(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
roles: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
role: z.string(),
|
||||
customRoleId: z.string().optional().nullable(),
|
||||
customRoleName: z.string().optional().nullable(),
|
||||
customRoleSlug: z.string().optional().nullable(),
|
||||
isTemporary: z.boolean(),
|
||||
temporaryMode: z.string().optional().nullable(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional()
|
||||
})
|
||||
),
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
||||
})
|
||||
).array()
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -146,6 +180,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
const identityMemberships = await server.services.identityProject.listProjectIdentities({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId
|
||||
});
|
||||
|
@ -68,11 +68,14 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
handler: async (req, res) => {
|
||||
const userAgent = req.headers["user-agent"];
|
||||
const mfaJwtToken = req.headers.authorization?.replace("Bearer ", "");
|
||||
if (!userAgent) throw new Error("user agent header is required");
|
||||
if (!mfaJwtToken) throw new Error("authorization header is required");
|
||||
const appCfg = getConfig();
|
||||
|
||||
const { user, token } = await server.services.login.verifyMfaToken({
|
||||
userAgent,
|
||||
mfaJwtToken,
|
||||
ip: req.realIp,
|
||||
userId: req.mfa.userId,
|
||||
orgId: req.mfa.orgId,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { OrganizationsSchema, OrgMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -17,7 +18,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
organizationId: z.string().trim().describe(ORGANIZATIONS.LIST_USER_MEMBERSHIPS.organizationId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -44,6 +45,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
const users = await server.services.org.findAllOrgMembers(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { users };
|
||||
@ -62,7 +64,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
organizationId: z.string().trim().describe(ORGANIZATIONS.GET_PROJECTS.organizationId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -88,6 +90,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId
|
||||
});
|
||||
|
||||
@ -106,9 +109,12 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
apiKeyAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
|
||||
params: z.object({
|
||||
organizationId: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.organizationId),
|
||||
membershipId: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.membershipId)
|
||||
}),
|
||||
body: z.object({
|
||||
role: z.string().trim()
|
||||
role: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.role)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -123,6 +129,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
const membership = await server.services.org.updateOrgMembership({
|
||||
userId: req.permission.id,
|
||||
role: req.body.role,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId,
|
||||
membershipId: req.params.membershipId,
|
||||
actorOrgId: req.permission.orgId
|
||||
@ -142,7 +149,10 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
apiKeyAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
|
||||
params: z.object({
|
||||
organizationId: z.string().trim().describe(ORGANIZATIONS.DELETE_USER_MEMBERSHIP.organizationId),
|
||||
membershipId: z.string().trim().describe(ORGANIZATIONS.DELETE_USER_MEMBERSHIP.membershipId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
membership: OrgMembershipsSchema
|
||||
@ -155,6 +165,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
|
||||
const membership = await server.services.org.deleteOrgMembership({
|
||||
userId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgId: req.params.organizationId,
|
||||
membershipId: req.params.membershipId,
|
||||
actorOrgId: req.permission.orgId
|
||||
@ -176,7 +187,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY], { requireOrg: false }),
|
||||
handler: async (req) => {
|
||||
if (req.auth.actor !== ActorType.USER) return;
|
||||
|
||||
@ -210,6 +221,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
const organization = await server.services.org.deleteOrganizationById(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { organization };
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { ProjectMembershipsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -11,11 +12,11 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
url: "/:projectId/memberships",
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().describe("The ID of the project.")
|
||||
projectId: z.string().describe(PROJECTS.INVITE_MEMBER.projectId)
|
||||
}),
|
||||
body: z.object({
|
||||
emails: z.string().email().array().default([]).describe("Emails of the users to add to the project."),
|
||||
usernames: z.string().array().default([]).describe("Usernames of the users to add to the project.")
|
||||
emails: z.string().email().array().default([]).describe(PROJECTS.INVITE_MEMBER.emails),
|
||||
usernames: z.string().array().default([]).describe(PROJECTS.INVITE_MEMBER.usernames)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -27,7 +28,9 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
handler: async (req) => {
|
||||
const memberships = await server.services.projectMembership.addUsersToProjectNonE2EE({
|
||||
projectId: req.params.projectId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
emails: req.body.emails,
|
||||
usernames: req.body.usernames
|
||||
@ -55,12 +58,12 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
url: "/:projectId/memberships",
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().describe("The ID of the project.")
|
||||
projectId: z.string().describe(PROJECTS.REMOVE_MEMBER.projectId)
|
||||
}),
|
||||
|
||||
body: z.object({
|
||||
emails: z.string().email().array().default([]).describe("Emails of the users to remove from the project."),
|
||||
usernames: z.string().array().default([]).describe("Usernames of the users to remove from the project.")
|
||||
emails: z.string().email().array().default([]).describe(PROJECTS.REMOVE_MEMBER.emails),
|
||||
usernames: z.string().array().default([]).describe(PROJECTS.REMOVE_MEMBER.usernames)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -73,6 +76,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
const memberships = await server.services.projectMembership.deleteProjectMemberships({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId,
|
||||
emails: req.body.emails,
|
||||
|
@ -3,10 +3,12 @@ import { z } from "zod";
|
||||
|
||||
import { ProjectKeysSchema, ProjectsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||
|
||||
const projectWithEnv = ProjectsSchema.merge(
|
||||
@ -16,6 +18,14 @@ const projectWithEnv = ProjectsSchema.merge(
|
||||
})
|
||||
);
|
||||
|
||||
const slugSchema = z
|
||||
.string()
|
||||
.min(5)
|
||||
.max(36)
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be at least 5 character but no more than 36"
|
||||
});
|
||||
|
||||
export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
/* Get project key */
|
||||
server.route({
|
||||
@ -29,7 +39,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(PROJECTS.GET_KEY.workspaceId)
|
||||
}),
|
||||
response: {
|
||||
200: ProjectKeysSchema.merge(
|
||||
@ -46,6 +56,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
const key = await server.services.projectKey.getLatestProjectKey({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
@ -73,7 +84,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
}),
|
||||
|
||||
body: z.object({
|
||||
userPrivateKey: z.string().trim()
|
||||
}),
|
||||
@ -81,11 +91,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
200: z.void()
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
await server.services.project.upgradeProject({
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.params.projectId,
|
||||
userPrivateKey: req.body.userPrivateKey
|
||||
});
|
||||
@ -106,9 +118,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const status = await server.services.project.getProjectUpgradeStatus({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id
|
||||
@ -127,7 +141,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
projectName: z.string().trim(),
|
||||
projectName: z.string().trim().describe(PROJECTS.CREATE.projectName),
|
||||
slug: z
|
||||
.string()
|
||||
.min(5)
|
||||
@ -135,8 +149,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.optional(),
|
||||
organizationId: z.string().trim()
|
||||
.optional()
|
||||
.describe(PROJECTS.CREATE.slug),
|
||||
organizationSlug: z.string().trim().describe(PROJECTS.CREATE.organizationSlug)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -144,12 +159,14 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const project = await server.services.project.createProject({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
orgId: req.body.organizationId,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
orgSlug: req.body.organizationSlug,
|
||||
workspaceName: req.body.projectName,
|
||||
slug: req.body.slug
|
||||
});
|
||||
@ -158,7 +175,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
event: PostHogEventTypes.ProjectCreated,
|
||||
distinctId: getTelemetryDistinctId(req),
|
||||
properties: {
|
||||
orgId: req.body.organizationId,
|
||||
orgId: project.orgId,
|
||||
name: project.name,
|
||||
...req.auditLogInfo
|
||||
}
|
||||
@ -167,4 +184,104 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
return { project };
|
||||
}
|
||||
});
|
||||
|
||||
/* Delete a project by slug */
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:slug",
|
||||
schema: {
|
||||
params: z.object({
|
||||
slug: slugSchema.describe("The slug of the project to delete.")
|
||||
}),
|
||||
response: {
|
||||
200: ProjectsSchema
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
|
||||
handler: async (req) => {
|
||||
const project = await server.services.project.deleteProject({
|
||||
filter: {
|
||||
type: ProjectFilterType.SLUG,
|
||||
slug: req.params.slug,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type
|
||||
});
|
||||
|
||||
return project;
|
||||
}
|
||||
});
|
||||
|
||||
/* Get a project by slug */
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:slug",
|
||||
schema: {
|
||||
params: z.object({
|
||||
slug: slugSchema.describe("The slug of the project to get.")
|
||||
}),
|
||||
response: {
|
||||
200: projectWithEnv
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const project = await server.services.project.getAProject({
|
||||
filter: {
|
||||
slug: req.params.slug,
|
||||
orgId: req.permission.orgId,
|
||||
type: ProjectFilterType.SLUG
|
||||
},
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type
|
||||
});
|
||||
|
||||
return project;
|
||||
}
|
||||
});
|
||||
|
||||
/* Update a project by slug */
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:slug",
|
||||
schema: {
|
||||
params: z.object({
|
||||
slug: slugSchema.describe("The slug of the project to update.")
|
||||
}),
|
||||
body: z.object({
|
||||
name: z.string().trim().optional().describe("The new name of the project."),
|
||||
autoCapitalization: z.boolean().optional().describe("The new auto-capitalization setting.")
|
||||
}),
|
||||
response: {
|
||||
200: ProjectsSchema
|
||||
}
|
||||
},
|
||||
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const project = await server.services.project.updateProject({
|
||||
filter: {
|
||||
type: ProjectFilterType.SLUG,
|
||||
slug: req.params.slug,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
update: {
|
||||
name: req.body.name,
|
||||
autoCapitalization: req.body.autoCapitalization
|
||||
},
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
return project;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -46,6 +46,8 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
|
||||
handler: async (req) => {
|
||||
const { serviceToken, user } = await server.services.serviceToken.getServiceToken({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type
|
||||
});
|
||||
|
||||
@ -98,6 +100,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
|
||||
const { serviceToken, token } = await server.services.serviceToken.createServiceToken({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
projectId: req.body.workspaceId
|
||||
@ -136,6 +139,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
|
||||
const serviceTokenData = await server.services.serviceToken.deleteServiceToken({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.serviceTokenId
|
||||
});
|
||||
|
@ -60,7 +60,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY], { requireOrg: false }),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.updateAuthMethods(req.permission.id, req.body.authMethods);
|
||||
return { user };
|
||||
|
@ -34,6 +34,42 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/select-organization",
|
||||
config: {
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
token: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req, res) => {
|
||||
const cfg = getConfig();
|
||||
const tokens = await server.services.login.selectOrganization({
|
||||
userAgent: req.headers["user-agent"],
|
||||
authJwtToken: req.headers.authorization,
|
||||
organizationId: req.body.organizationId,
|
||||
ipAddress: req.realIp
|
||||
});
|
||||
|
||||
void res.setCookie("jid", tokens.refresh, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: cfg.HTTPS_ENABLED
|
||||
});
|
||||
|
||||
return { token: tokens.access };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/login2",
|
||||
|
@ -20,6 +20,7 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider)
|
||||
handler: async (req) => {
|
||||
const count = await server.services.secretBlindIndex.getSecretBlindIndexStatus({
|
||||
projectId: req.params.projectId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId
|
||||
@ -52,6 +53,7 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider)
|
||||
handler: async (req) => {
|
||||
const secrets = await server.services.secretBlindIndex.getProjectSecrets({
|
||||
projectId: req.params.projectId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId
|
||||
@ -86,6 +88,7 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider)
|
||||
await server.services.secretBlindIndex.updateProjectSecretName({
|
||||
projectId: req.params.projectId,
|
||||
secretsToUpdate: req.body.secretsToUpdate,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId
|
||||
|
@ -10,12 +10,14 @@ import {
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||
import { RAW_SECRETS } from "@app/lib/api-docs";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { getUserAgentType } from "@app/server/plugins/audit-log";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||
|
||||
import { secretRawSchema } from "../sanitizedSchemas";
|
||||
@ -33,13 +35,15 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim().optional(),
|
||||
environment: z.string().trim().optional(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceId),
|
||||
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug),
|
||||
environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.LIST.secretPath),
|
||||
include_imports: z
|
||||
.enum(["true", "false"])
|
||||
.default("false")
|
||||
.transform((value) => value === "true")
|
||||
.describe(RAW_SECRETS.LIST.includeImports)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -68,6 +72,22 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
environment = scope[0].environment;
|
||||
workspaceId = req.auth.serviceToken.projectId;
|
||||
}
|
||||
} else if (req.permission.type === ActorType.IDENTITY && req.query.workspaceSlug && !workspaceId) {
|
||||
const workspace = await server.services.project.getAProject({
|
||||
filter: {
|
||||
type: ProjectFilterType.SLUG,
|
||||
orgId: req.permission.orgId,
|
||||
slug: req.query.workspaceSlug
|
||||
},
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
if (!workspace) throw new BadRequestError({ message: `No project found with slug ${req.query.workspaceSlug}` });
|
||||
|
||||
workspaceId = workspace.id;
|
||||
}
|
||||
|
||||
if (!workspaceId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" });
|
||||
@ -77,13 +97,14 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
environment,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: workspaceId,
|
||||
path: secretPath,
|
||||
includeImports: req.query.include_imports
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.query.workspaceId,
|
||||
projectId: workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRETS,
|
||||
@ -123,18 +144,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
secretName: z.string().trim()
|
||||
secretName: z.string().trim().describe(RAW_SECRETS.GET.secretName)
|
||||
}),
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim().optional(),
|
||||
environment: z.string().trim().optional(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
version: z.coerce.number().optional(),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared),
|
||||
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.GET.workspaceId),
|
||||
environment: z.string().trim().optional().describe(RAW_SECRETS.GET.environment),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.GET.secretPath),
|
||||
version: z.coerce.number().optional().describe(RAW_SECRETS.GET.version),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.GET.type),
|
||||
include_imports: z
|
||||
.enum(["true", "false"])
|
||||
.default("false")
|
||||
.transform((value) => value === "true")
|
||||
.describe(RAW_SECRETS.GET.includeImports)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -160,6 +182,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secret = await server.services.secret.getSecretByNameRaw({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
environment,
|
||||
projectId: workspaceId,
|
||||
@ -213,16 +236,24 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
secretName: z.string().trim()
|
||||
secretName: z.string().trim().describe(RAW_SECRETS.CREATE.secretName)
|
||||
}),
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
|
||||
secretComment: z.string().trim().optional().default(""),
|
||||
skipMultilineEncoding: z.boolean().optional(),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared)
|
||||
workspaceId: z.string().trim().describe(RAW_SECRETS.CREATE.workspaceId),
|
||||
environment: z.string().trim().describe(RAW_SECRETS.CREATE.environment),
|
||||
secretPath: z
|
||||
.string()
|
||||
.trim()
|
||||
.default("/")
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(RAW_SECRETS.CREATE.secretPath),
|
||||
secretValue: z
|
||||
.string()
|
||||
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
||||
.describe(RAW_SECRETS.CREATE.secretValue),
|
||||
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -237,6 +268,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
environment: req.body.environment,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: req.body.workspaceId,
|
||||
secretPath: req.body.secretPath,
|
||||
secretName: req.params.secretName,
|
||||
@ -290,15 +322,23 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
secretName: z.string().trim()
|
||||
secretName: z.string().trim().describe(RAW_SECRETS.UPDATE.secretName)
|
||||
}),
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
skipMultilineEncoding: z.boolean().optional(),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared)
|
||||
workspaceId: z.string().trim().describe(RAW_SECRETS.UPDATE.workspaceId),
|
||||
environment: z.string().trim().describe(RAW_SECRETS.UPDATE.environment),
|
||||
secretValue: z
|
||||
.string()
|
||||
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
||||
.describe(RAW_SECRETS.UPDATE.secretValue),
|
||||
secretPath: z
|
||||
.string()
|
||||
.trim()
|
||||
.default("/")
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(RAW_SECRETS.UPDATE.secretPath),
|
||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.UPDATE.skipMultilineEncoding),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -312,6 +352,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
environment: req.body.environment,
|
||||
projectId: req.body.workspaceId,
|
||||
secretPath: req.body.secretPath,
|
||||
@ -364,13 +405,18 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
secretName: z.string().trim()
|
||||
secretName: z.string().trim().describe(RAW_SECRETS.DELETE.secretName)
|
||||
}),
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared)
|
||||
workspaceId: z.string().trim().describe(RAW_SECRETS.DELETE.workspaceId),
|
||||
environment: z.string().trim().describe(RAW_SECRETS.DELETE.environment),
|
||||
secretPath: z
|
||||
.string()
|
||||
.trim()
|
||||
.default("/")
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(RAW_SECRETS.DELETE.secretPath),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.DELETE.type)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -383,6 +429,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secret = await server.services.secret.deleteSecretRaw({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
environment: req.body.environment,
|
||||
projectId: req.body.workspaceId,
|
||||
@ -478,6 +525,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const { secrets, imports } = await server.services.secret.getSecrets({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
environment: req.query.environment,
|
||||
projectId: req.query.workspaceId,
|
||||
@ -564,6 +612,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secret = await server.services.secret.getSecretByName({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
environment: req.query.environment,
|
||||
projectId: req.query.workspaceId,
|
||||
@ -666,6 +715,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
if (req.body.type !== SecretType.Personal && req.permission.type === ActorType.USER) {
|
||||
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -675,6 +726,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -718,6 +770,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secret = await server.services.secret.createSecret({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
path: secretPath,
|
||||
type,
|
||||
@ -842,6 +895,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -851,6 +905,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -896,6 +951,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secret = await server.services.secret.updateSecret({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
path: secretPath,
|
||||
type,
|
||||
@ -986,6 +1042,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -995,6 +1052,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -1028,6 +1086,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secret = await server.services.secret.deleteSecret({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
path: secretPath,
|
||||
type,
|
||||
@ -1110,6 +1169,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -1119,6 +1179,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -1148,6 +1209,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secrets = await server.services.secret.createManySecret({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
path: secretPath,
|
||||
environment,
|
||||
@ -1231,6 +1293,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -1240,6 +1303,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -1268,6 +1332,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secrets = await server.services.secret.updateManySecret({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
path: secretPath,
|
||||
environment,
|
||||
@ -1340,6 +1405,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -1349,6 +1415,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
secretPath,
|
||||
environment,
|
||||
@ -1376,6 +1443,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
const secrets = await server.services.secret.deleteManySecret({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
path: req.body.secretPath,
|
||||
environment,
|
||||
|
@ -108,7 +108,8 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
user: UsersSchema,
|
||||
token: z.string()
|
||||
token: z.string(),
|
||||
organizationId: z.string().nullish()
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -124,12 +125,13 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
}
|
||||
|
||||
const { user, accessToken, refreshToken } = await server.services.signup.completeEmailAccountSignup({
|
||||
...req.body,
|
||||
ip: req.realIp,
|
||||
userAgent,
|
||||
authorization: req.headers.authorization as string
|
||||
});
|
||||
const { user, accessToken, refreshToken, organizationId } =
|
||||
await server.services.signup.completeEmailAccountSignup({
|
||||
...req.body,
|
||||
ip: req.realIp,
|
||||
userAgent,
|
||||
authorization: req.headers.authorization as string
|
||||
});
|
||||
|
||||
if (user.email) {
|
||||
void server.services.telemetry.sendLoopsEvent(user.email, user.firstName || "", user.lastName || "");
|
||||
@ -152,7 +154,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
|
||||
secure: appCfg.HTTPS_ENABLED
|
||||
});
|
||||
|
||||
return { message: "Successfully set up account", user, token: accessToken };
|
||||
return { message: "Successfully set up account", user, token: accessToken, organizationId };
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -15,10 +15,10 @@ export const validateProviderAuthToken = (providerToken: string, username?: stri
|
||||
if (decodedToken.username !== username) throw new Error("Invalid auth credentials");
|
||||
|
||||
if (decodedToken.organizationId) {
|
||||
return { orgId: decodedToken.organizationId };
|
||||
return { orgId: decodedToken.organizationId, authMethod: decodedToken.authMethod };
|
||||
}
|
||||
|
||||
return {};
|
||||
return { authMethod: decodedToken.authMethod, orgId: null };
|
||||
};
|
||||
|
||||
export const validateSignUpAuthorization = (token: string, userId: string, validate = true) => {
|
||||
|
@ -1,13 +1,16 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import { TUsers, UserDeviceSchema } from "@app/db/schemas";
|
||||
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
import { TTokenDALFactory } from "../auth-token/auth-token-dal";
|
||||
import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { validateProviderAuthToken } from "./auth-fns";
|
||||
@ -17,16 +20,24 @@ import {
|
||||
TOauthLoginDTO,
|
||||
TVerifyMfaTokenDTO
|
||||
} from "./auth-login-type";
|
||||
import { AuthMethod, AuthTokenType } from "./auth-type";
|
||||
import { AuthMethod, AuthModeJwtTokenPayload, AuthModeMfaJwtTokenPayload, AuthTokenType } from "./auth-type";
|
||||
|
||||
type TAuthLoginServiceFactoryDep = {
|
||||
userDAL: TUserDALFactory;
|
||||
orgDAL: TOrgDALFactory;
|
||||
tokenService: TAuthTokenServiceFactory;
|
||||
smtpService: TSmtpService;
|
||||
tokenDAL: TTokenDALFactory;
|
||||
};
|
||||
|
||||
export type TAuthLoginFactory = ReturnType<typeof authLoginServiceFactory>;
|
||||
export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: TAuthLoginServiceFactoryDep) => {
|
||||
export const authLoginServiceFactory = ({
|
||||
userDAL,
|
||||
tokenService,
|
||||
smtpService,
|
||||
orgDAL,
|
||||
tokenDAL
|
||||
}: TAuthLoginServiceFactoryDep) => {
|
||||
/*
|
||||
* Private
|
||||
* Not exported. This is to update user device list
|
||||
@ -83,12 +94,14 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
user,
|
||||
ip,
|
||||
userAgent,
|
||||
organizationId
|
||||
organizationId,
|
||||
authMethod
|
||||
}: {
|
||||
user: TUsers;
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
organizationId?: string;
|
||||
organizationId: string | undefined;
|
||||
authMethod: AuthMethod;
|
||||
}) => {
|
||||
const cfg = getConfig();
|
||||
await updateUserDeviceSession(user, ip, userAgent);
|
||||
@ -98,8 +111,10 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
userId: user.id
|
||||
});
|
||||
if (!tokenSession) throw new Error("Failed to create token");
|
||||
|
||||
const accessToken = jwt.sign(
|
||||
{
|
||||
authMethod,
|
||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||
userId: user.id,
|
||||
tokenVersionId: tokenSession.id,
|
||||
@ -112,6 +127,7 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{
|
||||
authMethod,
|
||||
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
||||
userId: user.id,
|
||||
tokenVersionId: tokenSession.id,
|
||||
@ -158,9 +174,9 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
const loginExchangeClientProof = async ({
|
||||
email,
|
||||
clientProof,
|
||||
providerAuthToken,
|
||||
ip,
|
||||
userAgent
|
||||
userAgent,
|
||||
providerAuthToken
|
||||
}: TLoginClientProofDTO) => {
|
||||
const userEnc = await userDAL.findUserEncKeyByUsername({
|
||||
username: email
|
||||
@ -168,14 +184,16 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
if (!userEnc) throw new Error("Failed to find user");
|
||||
const cfg = getConfig();
|
||||
|
||||
let organizationId;
|
||||
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
|
||||
const { orgId } = validateProviderAuthToken(providerAuthToken as string, email);
|
||||
organizationId = orgId;
|
||||
} else if (providerAuthToken) {
|
||||
// SAML SSO
|
||||
const { orgId } = validateProviderAuthToken(providerAuthToken, email);
|
||||
organizationId = orgId;
|
||||
let authMethod = AuthMethod.EMAIL;
|
||||
let organizationId: string | undefined;
|
||||
|
||||
if (providerAuthToken) {
|
||||
const decodedProviderToken = validateProviderAuthToken(providerAuthToken, email);
|
||||
|
||||
authMethod = decodedProviderToken.authMethod;
|
||||
if (isAuthMethodSaml(authMethod) && decodedProviderToken.orgId) {
|
||||
organizationId = decodedProviderToken.orgId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?");
|
||||
@ -196,9 +214,9 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
if (userEnc.isMfaEnabled && userEnc.email) {
|
||||
const mfaToken = jwt.sign(
|
||||
{
|
||||
authMethod,
|
||||
authTokenType: AuthTokenType.MFA_TOKEN,
|
||||
userId: userEnc.userId,
|
||||
organizationId
|
||||
userId: userEnc.userId
|
||||
},
|
||||
cfg.AUTH_SECRET,
|
||||
{
|
||||
@ -221,12 +239,60 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
},
|
||||
ip,
|
||||
userAgent,
|
||||
authMethod,
|
||||
organizationId
|
||||
});
|
||||
|
||||
return { token, isMfaEnabled: false, user: userEnc } as const;
|
||||
};
|
||||
|
||||
const selectOrganization = async ({
|
||||
userAgent,
|
||||
authJwtToken,
|
||||
ipAddress,
|
||||
organizationId
|
||||
}: {
|
||||
userAgent: string | undefined;
|
||||
authJwtToken: string | undefined;
|
||||
ipAddress: string;
|
||||
organizationId: string;
|
||||
}) => {
|
||||
const cfg = getConfig();
|
||||
|
||||
if (!authJwtToken) throw new UnauthorizedError({ name: "Authorization header is required" });
|
||||
if (!userAgent) throw new UnauthorizedError({ name: "user agent header is required" });
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
authJwtToken = authJwtToken.replace("Bearer ", ""); // remove bearer from token
|
||||
|
||||
// The decoded JWT token, which contains the auth method.
|
||||
const decodedToken = jwt.verify(authJwtToken, cfg.AUTH_SECRET) as AuthModeJwtTokenPayload;
|
||||
if (!decodedToken.authMethod) throw new UnauthorizedError({ name: "Auth method not found on existing token" });
|
||||
|
||||
const user = await userDAL.findUserEncKeyByUserId(decodedToken.userId);
|
||||
if (!user) throw new BadRequestError({ message: "User not found", name: "Find user from token" });
|
||||
|
||||
// Check if the user actually has access to the specified organization.
|
||||
const userOrgs = await orgDAL.findAllOrgsByUserId(user.id);
|
||||
const hasOrganizationMembership = userOrgs.some((org) => org.id === organizationId);
|
||||
|
||||
if (!hasOrganizationMembership) {
|
||||
throw new UnauthorizedError({ message: "User does not have access to the organization" });
|
||||
}
|
||||
|
||||
await tokenDAL.incrementTokenSessionVersion(user.id, decodedToken.tokenVersionId);
|
||||
|
||||
const tokens = await generateUserTokens({
|
||||
authMethod: decodedToken.authMethod,
|
||||
user,
|
||||
userAgent,
|
||||
ip: ipAddress,
|
||||
organizationId
|
||||
});
|
||||
|
||||
return tokens;
|
||||
};
|
||||
|
||||
/*
|
||||
* Multi factor authentication re-send code, Get user id from token
|
||||
* saved in frontend
|
||||
@ -244,12 +310,15 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
* Multi factor authentication verification of code
|
||||
* Third step of login in which user completes with mfa
|
||||
* */
|
||||
const verifyMfaToken = async ({ userId, mfaToken, ip, userAgent, orgId }: TVerifyMfaTokenDTO) => {
|
||||
const verifyMfaToken = async ({ userId, mfaToken, mfaJwtToken, ip, userAgent, orgId }: TVerifyMfaTokenDTO) => {
|
||||
await tokenService.validateTokenForUser({
|
||||
type: TokenType.TOKEN_EMAIL_MFA,
|
||||
userId,
|
||||
code: mfaToken
|
||||
});
|
||||
|
||||
const decodedToken = jwt.verify(mfaJwtToken, getConfig().AUTH_SECRET) as AuthModeMfaJwtTokenPayload;
|
||||
|
||||
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
|
||||
if (!userEnc) throw new Error("Failed to authenticate user");
|
||||
|
||||
@ -260,7 +329,8 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
},
|
||||
ip,
|
||||
userAgent,
|
||||
organizationId: orgId
|
||||
organizationId: orgId,
|
||||
authMethod: decodedToken.authMethod
|
||||
});
|
||||
|
||||
return { token, user: userEnc };
|
||||
@ -339,6 +409,7 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }:
|
||||
oauth2Login,
|
||||
resendMfaToken,
|
||||
verifyMfaToken,
|
||||
selectOrganization,
|
||||
generateUserTokens
|
||||
};
|
||||
};
|
||||
|
@ -17,6 +17,7 @@ export type TLoginClientProofDTO = {
|
||||
export type TVerifyMfaTokenDTO = {
|
||||
userId: string;
|
||||
mfaToken: string;
|
||||
mfaJwtToken: string;
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
orgId?: string;
|
||||
|
@ -150,11 +150,15 @@ export const authSignupServiceFactory = ({
|
||||
});
|
||||
|
||||
if (!organizationId) {
|
||||
await orgService.createOrganization({
|
||||
const newOrganization = await orgService.createOrganization({
|
||||
userId: user.id,
|
||||
userEmail: user.email ?? user.username,
|
||||
orgName: organizationName
|
||||
});
|
||||
|
||||
if (!newOrganization) throw new Error("Failed to create organization");
|
||||
|
||||
organizationId = newOrganization.id;
|
||||
}
|
||||
|
||||
const updatedMembersips = await orgDAL.updateMembership(
|
||||
@ -174,6 +178,7 @@ export const authSignupServiceFactory = ({
|
||||
|
||||
const accessToken = jwt.sign(
|
||||
{
|
||||
authMethod: AuthMethod.EMAIL,
|
||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||
userId: updateduser.info.id,
|
||||
tokenVersionId: tokenSession.id,
|
||||
@ -186,6 +191,7 @@ export const authSignupServiceFactory = ({
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{
|
||||
authMethod: AuthMethod.EMAIL,
|
||||
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
||||
userId: updateduser.info.id,
|
||||
tokenVersionId: tokenSession.id,
|
||||
@ -196,7 +202,7 @@ export const authSignupServiceFactory = ({
|
||||
{ expiresIn: appCfg.JWT_REFRESH_LIFETIME }
|
||||
);
|
||||
|
||||
return { user: updateduser.info, accessToken, refreshToken };
|
||||
return { user: updateduser.info, accessToken, refreshToken, organizationId };
|
||||
};
|
||||
|
||||
/*
|
||||
@ -277,6 +283,7 @@ export const authSignupServiceFactory = ({
|
||||
|
||||
const accessToken = jwt.sign(
|
||||
{
|
||||
authMethod: AuthMethod.EMAIL,
|
||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||
userId: updateduser.info.id,
|
||||
tokenVersionId: tokenSession.id,
|
||||
@ -288,6 +295,7 @@ export const authSignupServiceFactory = ({
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{
|
||||
authMethod: AuthMethod.EMAIL,
|
||||
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
||||
userId: updateduser.info.id,
|
||||
tokenVersionId: tokenSession.id,
|
||||
|
@ -6,6 +6,7 @@ export enum AuthMethod {
|
||||
OKTA_SAML = "okta-saml",
|
||||
AZURE_SAML = "azure-saml",
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||
GOOGLE_SAML = "google-saml",
|
||||
LDAP = "ldap"
|
||||
}
|
||||
|
||||
@ -38,8 +39,12 @@ export enum ActorType { // would extend to AWS, Azure, ...
|
||||
SCIM_CLIENT = "scimClient"
|
||||
}
|
||||
|
||||
// This will be null unless the token-type is JWT
|
||||
export type ActorAuthMethod = AuthMethod | null;
|
||||
|
||||
export type AuthModeJwtTokenPayload = {
|
||||
authTokenType: AuthTokenType.ACCESS_TOKEN;
|
||||
authMethod: AuthMethod;
|
||||
userId: string;
|
||||
tokenVersionId: string;
|
||||
accessVersion: number;
|
||||
@ -48,12 +53,15 @@ export type AuthModeJwtTokenPayload = {
|
||||
|
||||
export type AuthModeMfaJwtTokenPayload = {
|
||||
authTokenType: AuthTokenType.MFA_TOKEN;
|
||||
authMethod: AuthMethod;
|
||||
userId: string;
|
||||
organizationId?: string;
|
||||
};
|
||||
|
||||
export type AuthModeRefreshJwtTokenPayload = {
|
||||
// authMode
|
||||
authTokenType: AuthTokenType.REFRESH_TOKEN;
|
||||
authMethod: AuthMethod;
|
||||
userId: string;
|
||||
tokenVersionId: string;
|
||||
refreshVersion: number;
|
||||
@ -63,6 +71,8 @@ export type AuthModeRefreshJwtTokenPayload = {
|
||||
export type AuthModeProviderJwtTokenPayload = {
|
||||
authTokenType: AuthTokenType.PROVIDER_TOKEN;
|
||||
username: string;
|
||||
authMethod: AuthMethod;
|
||||
email: string;
|
||||
organizationId?: string;
|
||||
};
|
||||
|
||||
|
@ -6,17 +6,20 @@ import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
|
||||
|
||||
import { AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload, TRenewAccessTokenDTO } from "./identity-access-token-types";
|
||||
|
||||
type TIdentityAccessTokenServiceFactoryDep = {
|
||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||
};
|
||||
|
||||
export type TIdentityAccessTokenServiceFactory = ReturnType<typeof identityAccessTokenServiceFactory>;
|
||||
|
||||
export const identityAccessTokenServiceFactory = ({
|
||||
identityAccessTokenDAL
|
||||
identityAccessTokenDAL,
|
||||
identityOrgMembershipDAL
|
||||
}: TIdentityAccessTokenServiceFactoryDep) => {
|
||||
const validateAccessTokenExp = (identityAccessToken: TIdentityAccessTokens) => {
|
||||
const {
|
||||
@ -117,8 +120,16 @@ export const identityAccessTokenServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({
|
||||
identityId: identityAccessToken.identityId
|
||||
});
|
||||
|
||||
if (!identityOrgMembership) {
|
||||
throw new UnauthorizedError({ message: "Identity does not belong to any organization" });
|
||||
}
|
||||
|
||||
validateAccessTokenExp(identityAccessToken);
|
||||
return identityAccessToken;
|
||||
return { ...identityAccessToken, orgId: identityOrgMembership.orgId };
|
||||
};
|
||||
|
||||
return { renewAccessToken, fnValidateIdentityAccessToken };
|
||||
|
@ -3,7 +3,7 @@ import { Knex } from "knex";
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
import { ormify, sqlNestRelationships } from "@app/lib/knex";
|
||||
|
||||
export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFactory>;
|
||||
|
||||
@ -15,52 +15,81 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
const docs = await (tx || db)(TableName.IdentityProjectMembership)
|
||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.join(
|
||||
TableName.IdentityProjectMembershipRole,
|
||||
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
||||
`${TableName.IdentityProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.IdentityProjectMembership}.roleId`,
|
||||
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.select(selectAllTableCols(TableName.IdentityProjectMembership))
|
||||
// cr stands for custom role
|
||||
.select(db.ref("id").as("crId").withSchema(TableName.ProjectRoles))
|
||||
.select(db.ref("name").as("crName").withSchema(TableName.ProjectRoles))
|
||||
.select(db.ref("slug").as("crSlug").withSchema(TableName.ProjectRoles))
|
||||
.select(db.ref("description").as("crDescription").withSchema(TableName.ProjectRoles))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.ProjectRoles))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.ProjectRoles))
|
||||
.select(db.ref("id").as("identityId").withSchema(TableName.Identity))
|
||||
.select(db.ref("name").as("identityName").withSchema(TableName.Identity))
|
||||
.select(db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity));
|
||||
return docs.map(
|
||||
({
|
||||
crId,
|
||||
crDescription,
|
||||
crSlug,
|
||||
crPermission,
|
||||
crName,
|
||||
identityId,
|
||||
identityName,
|
||||
identityAuthMethod,
|
||||
...el
|
||||
}) => ({
|
||||
...el,
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity),
|
||||
db.ref("id").as("identityId").withSchema(TableName.Identity),
|
||||
db.ref("name").as("identityName").withSchema(TableName.Identity),
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("role").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("temporaryMode").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole)
|
||||
);
|
||||
|
||||
const members = sqlNestRelationships({
|
||||
data: docs,
|
||||
parentMapper: ({ identityId, identityName, identityAuthMethod, id, createdAt, updatedAt }) => ({
|
||||
id,
|
||||
identityId,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
identity: {
|
||||
id: identityId,
|
||||
name: identityName,
|
||||
authMethod: identityAuthMethod
|
||||
},
|
||||
customRole: el.roleId
|
||||
? {
|
||||
id: crId,
|
||||
name: crName,
|
||||
slug: crSlug,
|
||||
permissions: crPermission,
|
||||
description: crDescription
|
||||
}
|
||||
: undefined
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
key: "id",
|
||||
childrenMapper: [
|
||||
{
|
||||
label: "roles" as const,
|
||||
key: "membershipRoleId",
|
||||
mapper: ({
|
||||
role,
|
||||
customRoleId,
|
||||
customRoleName,
|
||||
customRoleSlug,
|
||||
membershipRoleId,
|
||||
temporaryRange,
|
||||
temporaryMode,
|
||||
temporaryAccessEndTime,
|
||||
temporaryAccessStartTime,
|
||||
isTemporary
|
||||
}) => ({
|
||||
id: membershipRoleId,
|
||||
role,
|
||||
customRoleId,
|
||||
customRoleName,
|
||||
customRoleSlug,
|
||||
temporaryRange,
|
||||
temporaryMode,
|
||||
temporaryAccessEndTime,
|
||||
temporaryAccessStartTime,
|
||||
isTemporary
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
return members;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindByProjectId" });
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user