mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 21:51:56 +00:00
Merge branch 'main' into feat/addSecretImportsToOverview
This commit is contained in:
Vendored
+1
@@ -106,6 +106,7 @@ declare module "@fastify/request-context" {
|
||||
claims: Record<string, string>;
|
||||
};
|
||||
};
|
||||
identityPermissionMetadata?: Record<string, unknown>; // filled by permission service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas/models";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds"))) {
|
||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||
t.specificType("adminIdentityIds", "text[]");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds")) {
|
||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||
t.dropColumn("adminIdentityIds");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
|
||||
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
|
||||
if (doesParentColumExist && doesNameColumnExist) {
|
||||
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||
t.index(["parentId", "name"]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
|
||||
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
|
||||
if (doesParentColumExist && doesNameColumnExist) {
|
||||
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||
t.dropIndex(["parentId", "name"]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,8 @@ export const SuperAdminSchema = z.object({
|
||||
encryptedSlackClientId: zodBuffer.nullable().optional(),
|
||||
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
|
||||
authConsentContent: z.string().nullable().optional(),
|
||||
pageFrameContent: z.string().nullable().optional()
|
||||
pageFrameContent: z.string().nullable().optional(),
|
||||
adminIdentityIds: z.string().array().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
@@ -81,8 +83,12 @@ export const auditLogServiceFactory = ({
|
||||
if (!data.projectId && !data.orgId)
|
||||
throw new BadRequestError({ message: "Must specify either project id or org id" });
|
||||
}
|
||||
|
||||
return auditLogQueue.pushToLog(data);
|
||||
const el = { ...data };
|
||||
if (el.actor.type === ActorType.USER || el.actor.type === ActorType.IDENTITY) {
|
||||
const permissionMetadata = requestContext.get("identityPermissionMetadata");
|
||||
el.actor.metadata.permission = permissionMetadata;
|
||||
}
|
||||
return auditLogQueue.pushToLog(el);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -290,6 +290,7 @@ interface UserActorMetadata {
|
||||
userId: string;
|
||||
email?: string | null;
|
||||
username: string;
|
||||
permission?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ServiceActorMetadata {
|
||||
@@ -300,6 +301,7 @@ interface ServiceActorMetadata {
|
||||
interface IdentityActorMetadata {
|
||||
identityId: string;
|
||||
name: string;
|
||||
permission?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ScimClientActorMetadata {}
|
||||
|
||||
@@ -50,7 +50,7 @@ export type TLicenseServiceFactory = ReturnType<typeof licenseServiceFactory>;
|
||||
const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login";
|
||||
const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/license-login";
|
||||
|
||||
const LICENSE_SERVER_CLOUD_PLAN_TTL = 30; // 30 second
|
||||
const LICENSE_SERVER_CLOUD_PLAN_TTL = 5 * 60; // 5 mins
|
||||
const FEATURE_CACHE_KEY = (orgId: string) => `infisical-cloud-plan-${orgId}`;
|
||||
|
||||
export const licenseServiceFactory = ({
|
||||
@@ -142,7 +142,10 @@ export const licenseServiceFactory = ({
|
||||
try {
|
||||
if (instanceType === InstanceType.Cloud) {
|
||||
const cachedPlan = await keyStore.getItem(FEATURE_CACHE_KEY(orgId));
|
||||
if (cachedPlan) return JSON.parse(cachedPlan) as TFeatureSet;
|
||||
if (cachedPlan) {
|
||||
logger.info(`getPlan: plan fetched from cache [orgId=${orgId}] [projectId=${projectId}]`);
|
||||
return JSON.parse(cachedPlan) as TFeatureSet;
|
||||
}
|
||||
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||
@@ -170,6 +173,8 @@ export const licenseServiceFactory = ({
|
||||
JSON.stringify(onPremFeatures)
|
||||
);
|
||||
return onPremFeatures;
|
||||
} finally {
|
||||
logger.info(`getPlan: Process done for [orgId=${orgId}] [projectId=${projectId}]`);
|
||||
}
|
||||
return onPremFeatures;
|
||||
};
|
||||
|
||||
@@ -131,12 +131,12 @@ function validateOrgSSO(actorAuthMethod: ActorAuthMethod, isOrgSsoEnforced: TOrg
|
||||
}
|
||||
}
|
||||
|
||||
const escapeHandlebarsMissingMetadata = (obj: Record<string, string>) => {
|
||||
const escapeHandlebarsMissingDict = (obj: Record<string, string>, key: string) => {
|
||||
const handler = {
|
||||
get(target: Record<string, string>, prop: string) {
|
||||
if (!(prop in target)) {
|
||||
if (!Object.hasOwn(target, prop)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
target[prop] = `{{identity.metadata.${prop}}}`; // Add missing key as an "own" property
|
||||
target[prop] = `{{${key}.${prop}}}`; // Add missing key as an "own" property
|
||||
}
|
||||
return target[prop];
|
||||
}
|
||||
@@ -145,4 +145,4 @@ const escapeHandlebarsMissingMetadata = (obj: Record<string, string>) => {
|
||||
return new Proxy(obj, handler);
|
||||
};
|
||||
|
||||
export { escapeHandlebarsMissingMetadata, isAuthMethodSaml, validateOrgSSO };
|
||||
export { escapeHandlebarsMissingDict, isAuthMethodSaml, validateOrgSSO };
|
||||
|
||||
@@ -23,7 +23,7 @@ import { TServiceTokenDALFactory } from "@app/services/service-token/service-tok
|
||||
|
||||
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
||||
import { TPermissionDALFactory } from "./permission-dal";
|
||||
import { escapeHandlebarsMissingMetadata, validateOrgSSO } from "./permission-fns";
|
||||
import { escapeHandlebarsMissingDict, validateOrgSSO } from "./permission-fns";
|
||||
import {
|
||||
TBuildOrgPermissionDTO,
|
||||
TBuildProjectPermissionDTO,
|
||||
@@ -244,13 +244,13 @@ export const permissionServiceFactory = ({
|
||||
|
||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(
|
||||
objectify(
|
||||
userProjectPermission.metadata,
|
||||
(i) => i.key,
|
||||
(i) => i.value
|
||||
)
|
||||
const unescapedMetadata = objectify(
|
||||
userProjectPermission.metadata,
|
||||
(i) => i.key,
|
||||
(i) => i.value
|
||||
);
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingDict(unescapedMetadata, "identity.metadata");
|
||||
requestContext.set("identityPermissionMetadata", { metadata: unescapedMetadata });
|
||||
const interpolateRules = templatedRules(
|
||||
{
|
||||
identity: {
|
||||
@@ -318,23 +318,26 @@ export const permissionServiceFactory = ({
|
||||
|
||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||
const identityAuthInfo = requestContext.get("identityAuthInfo");
|
||||
const unescapedIdentityAuthInfo = requestContext.get("identityAuthInfo");
|
||||
const unescapedMetadata = objectify(
|
||||
identityProjectPermission.metadata,
|
||||
(i) => i.key,
|
||||
(i) => i.value
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
) as Record<string, any>;
|
||||
if (identityAuthInfo?.identityId === identityId && identityAuthInfo) {
|
||||
unescapedMetadata.auth = identityAuthInfo;
|
||||
}
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(unescapedMetadata);
|
||||
);
|
||||
const identityAuthInfo =
|
||||
unescapedIdentityAuthInfo?.identityId === identityId && unescapedIdentityAuthInfo
|
||||
? escapeHandlebarsMissingDict(unescapedIdentityAuthInfo as never, "identity.auth")
|
||||
: {};
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingDict(unescapedMetadata, "identity.metadata");
|
||||
|
||||
requestContext.set("identityPermissionMetadata", { metadata: unescapedMetadata, auth: unescapedIdentityAuthInfo });
|
||||
const interpolateRules = templatedRules(
|
||||
{
|
||||
identity: {
|
||||
id: identityProjectPermission.identityId,
|
||||
username: identityProjectPermission.username,
|
||||
metadata: metadataKeyValuePair
|
||||
metadata: metadataKeyValuePair,
|
||||
auth: identityAuthInfo
|
||||
}
|
||||
},
|
||||
{ data: false }
|
||||
@@ -428,12 +431,13 @@ export const permissionServiceFactory = ({
|
||||
|
||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
||||
objectify(
|
||||
userProjectPermission.metadata,
|
||||
(i) => i.key,
|
||||
(i) => i.value
|
||||
)
|
||||
),
|
||||
"identity.metadata"
|
||||
);
|
||||
const interpolateRules = templatedRules(
|
||||
{
|
||||
@@ -473,14 +477,14 @@ export const permissionServiceFactory = ({
|
||||
|
||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(
|
||||
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
||||
objectify(
|
||||
identityProjectPermission.metadata,
|
||||
(i) => i.key,
|
||||
(i) => i.value
|
||||
)
|
||||
),
|
||||
"identity.metadata"
|
||||
);
|
||||
|
||||
const interpolateRules = templatedRules(
|
||||
{
|
||||
identity: {
|
||||
|
||||
@@ -56,6 +56,7 @@ const envSchema = z
|
||||
// TODO(akhilmhdh): will be changed to one
|
||||
ENCRYPTION_KEY: zpStr(z.string().optional()),
|
||||
ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()),
|
||||
QUEUE_WORKERS_ENABLED: zodStrBool.default("true"),
|
||||
HTTPS_ENABLED: zodStrBool,
|
||||
// smtp options
|
||||
SMTP_HOST: zpStr(z.string().optional()),
|
||||
|
||||
@@ -272,10 +272,13 @@ export const queueServiceFactory = (
|
||||
connection
|
||||
});
|
||||
|
||||
workerContainer[name] = new Worker<TQueueJobTypes[T]["payload"], void, TQueueJobTypes[T]["name"]>(name, jobFn, {
|
||||
...queueSettings,
|
||||
connection
|
||||
});
|
||||
const appCfg = getConfig();
|
||||
if (appCfg.QUEUE_WORKERS_ENABLED) {
|
||||
workerContainer[name] = new Worker<TQueueJobTypes[T]["payload"], void, TQueueJobTypes[T]["name"]>(name, jobFn, {
|
||||
...queueSettings,
|
||||
connection
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const startPg = async <T extends QueueName>(
|
||||
@@ -307,6 +310,11 @@ export const queueServiceFactory = (
|
||||
event: U,
|
||||
listener: WorkerListener<TQueueJobTypes[T]["payload"], void, TQueueJobTypes[T]["name"]>[U]
|
||||
) => {
|
||||
const appCfg = getConfig();
|
||||
if (!appCfg.QUEUE_WORKERS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const worker = workerContainer[name];
|
||||
worker.on(event, listener);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
export type TAuthMode =
|
||||
| {
|
||||
@@ -44,6 +45,7 @@ export type TAuthMode =
|
||||
identityName: string;
|
||||
orgId: string;
|
||||
authMethod: null;
|
||||
isInstanceAdmin?: boolean;
|
||||
}
|
||||
| {
|
||||
authMode: AuthMode.SCIM_TOKEN;
|
||||
@@ -130,13 +132,15 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
||||
}
|
||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
|
||||
const serverCfg = await getServerCfg();
|
||||
req.auth = {
|
||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
||||
actor,
|
||||
orgId: identity.orgId,
|
||||
identityId: identity.identityId,
|
||||
identityName: identity.name,
|
||||
authMethod: null
|
||||
authMethod: null,
|
||||
isInstanceAdmin: serverCfg?.adminIdentityIds?.includes(identity.identityId)
|
||||
};
|
||||
if (token?.identityAuth?.oidc) {
|
||||
requestContext.set("identityAuthInfo", {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
|
||||
|
||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const verifySuperAdmin = <T extends FastifyRequest>(
|
||||
req: T,
|
||||
_res: FastifyReply,
|
||||
done: HookHandlerDoneFunction
|
||||
) => {
|
||||
if (req.auth.actor !== ActorType.USER || !req.auth.user.superAdmin)
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Requires elevated super admin privileges"
|
||||
});
|
||||
done();
|
||||
if (isSuperAdmin(req.auth)) {
|
||||
return done();
|
||||
}
|
||||
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Requires elevated super admin privileges"
|
||||
});
|
||||
};
|
||||
|
||||
@@ -65,7 +65,7 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) =>
|
||||
payload: JSON.stringify(req.body),
|
||||
signature: signatureSHA256
|
||||
});
|
||||
void res.send("ok");
|
||||
return res.send("ok");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export const registerServeUI = async (
|
||||
TELEMETRY_CAPTURING_ENABLED: appCfg.TELEMETRY_ENABLED
|
||||
};
|
||||
const js = `window.__INFISICAL_RUNTIME_ENV__ = Object.freeze(${JSON.stringify(config)});`;
|
||||
void res.send(js);
|
||||
return res.send(js);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -57,7 +57,7 @@ export const registerServeUI = async (
|
||||
reply.callNotFound();
|
||||
return;
|
||||
}
|
||||
void reply.sendFile("index.html");
|
||||
return reply.sendFile("index.html");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CronJob } from "cron";
|
||||
import { Knex } from "knex";
|
||||
import { monitorEventLoopDelay } from "perf_hooks";
|
||||
import { z } from "zod";
|
||||
|
||||
import { registerCertificateEstRouter } from "@app/ee/routes/est/certificate-est-router";
|
||||
@@ -96,6 +97,7 @@ import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal"
|
||||
import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig, TEnvConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TQueueServiceFactory } from "@app/queue";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { accessTokenQueueServiceFactory } from "@app/services/access-token-queue/access-token-queue";
|
||||
@@ -246,6 +248,9 @@ import { registerV1Routes } from "./v1";
|
||||
import { registerV2Routes } from "./v2";
|
||||
import { registerV3Routes } from "./v3";
|
||||
|
||||
const histogram = monitorEventLoopDelay({ resolution: 20 });
|
||||
histogram.enable();
|
||||
|
||||
export const registerRoutes = async (
|
||||
server: FastifyZodProvider,
|
||||
{
|
||||
@@ -637,6 +642,9 @@ export const registerRoutes = async (
|
||||
userDAL,
|
||||
identityDAL,
|
||||
userAliasDAL,
|
||||
identityTokenAuthDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityOrgMembershipDAL,
|
||||
authService: loginService,
|
||||
serverCfgDAL: superAdminDAL,
|
||||
kmsRootConfigDAL,
|
||||
@@ -1627,6 +1635,18 @@ export const registerRoutes = async (
|
||||
const cfg = getConfig();
|
||||
const serverCfg = await getServerCfg();
|
||||
|
||||
const meanLagMs = histogram.mean / 1e6;
|
||||
const maxLagMs = histogram.max / 1e6;
|
||||
const p99LagMs = histogram.percentile(99) / 1e6;
|
||||
|
||||
logger.info(
|
||||
`Event loop stats - Mean: ${meanLagMs.toFixed(2)}ms, Max: ${maxLagMs.toFixed(2)}ms, p99: ${p99LagMs.toFixed(
|
||||
2
|
||||
)}ms`
|
||||
);
|
||||
|
||||
logger.info(`Raw event loop stats: ${JSON.stringify(histogram, null, 2)}`);
|
||||
|
||||
// try {
|
||||
// await db.raw("SELECT NOW()");
|
||||
// } catch (err) {
|
||||
|
||||
@@ -98,7 +98,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -139,7 +139,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -171,12 +171,16 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
identities: IdentitiesSchema.pick({
|
||||
name: true,
|
||||
id: true
|
||||
}).array()
|
||||
})
|
||||
.extend({
|
||||
isInstanceAdmin: z.boolean()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -206,7 +210,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -240,7 +244,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -265,7 +269,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -293,7 +297,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -316,7 +320,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -394,4 +398,141 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/identity-management/identities/:identityId/super-admin-access",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identity: IdentitiesSchema.pick({
|
||||
name: true,
|
||||
id: true
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
handler: async (req) => {
|
||||
const identity = await server.services.superAdmin.deleteIdentitySuperAdminAccess(
|
||||
req.params.identityId,
|
||||
req.permission.id
|
||||
);
|
||||
|
||||
return {
|
||||
identity
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/user-management/users/:userId/admin-access",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
userId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
id: true
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
handler: async (req) => {
|
||||
const user = await server.services.superAdmin.deleteUserSuperAdminAccess(req.params.userId);
|
||||
|
||||
return {
|
||||
user
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/bootstrap",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().email().trim().min(1),
|
||||
password: z.string().trim().min(1),
|
||||
organization: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
id: true,
|
||||
superAdmin: true
|
||||
}),
|
||||
organization: OrganizationsSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true
|
||||
}),
|
||||
identity: IdentitiesSchema.pick({
|
||||
id: true,
|
||||
name: true
|
||||
}).extend({
|
||||
credentials: z.object({
|
||||
token: z.string()
|
||||
}) // would just be Token AUTH for now
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { user, organization, machineIdentity } = await server.services.superAdmin.bootstrapInstance({
|
||||
...req.body,
|
||||
organizationName: req.body.organization
|
||||
});
|
||||
|
||||
await server.services.telemetry.sendPostHogEvents({
|
||||
event: PostHogEventTypes.AdminInit,
|
||||
distinctId: user.user.username ?? "",
|
||||
properties: {
|
||||
username: user.user.username,
|
||||
email: user.user.email ?? "",
|
||||
lastName: user.user.lastName || "",
|
||||
firstName: user.user.firstName || ""
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Successfully bootstrapped instance",
|
||||
user: user.user,
|
||||
organization,
|
||||
identity: machineIdentity
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
validateAccountIds,
|
||||
validatePrincipalArns
|
||||
} from "@app/services/identity-aws-auth/identity-aws-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -130,7 +131,8 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -8,8 +8,7 @@ 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";
|
||||
import { validateAzureAuthField } from "@app/services/identity-azure-auth/identity-azure-auth-validators";
|
||||
|
||||
import {} from "../sanitizedSchemas";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -127,7 +126,8 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -8,6 +8,7 @@ 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";
|
||||
import { validateGcpAuthField } from "@app/services/identity-gcp-auth/identity-gcp-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -121,7 +122,8 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
validateJwtAuthAudiencesField,
|
||||
validateJwtBoundClaimsField
|
||||
} from "@app/services/identity-jwt-auth/identity-jwt-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
const IdentityJwtAuthResponseSchema = IdentityJwtAuthsSchema.omit({
|
||||
encryptedJwksCaCert: true,
|
||||
@@ -169,7 +170,8 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -7,6 +7,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
const IdentityKubernetesAuthResponseSchema = IdentityKubernetesAuthsSchema.pick({
|
||||
id: true,
|
||||
@@ -147,7 +148,8 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
validateOidcAuthAudiencesField,
|
||||
validateOidcBoundClaimsField
|
||||
} from "@app/services/identity-oidc-auth/identity-oidc-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
const IdentityOidcAuthResponseSchema = IdentityOidcAuthsSchema.pick({
|
||||
id: true,
|
||||
@@ -148,7 +149,8 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -7,6 +7,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||
|
||||
import { SanitizedProjectSchema } from "../sanitizedSchemas";
|
||||
@@ -118,6 +119,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.body
|
||||
});
|
||||
|
||||
@@ -166,7 +168,8 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.identityId
|
||||
id: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -7,6 +7,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -74,7 +75,8 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@@ -157,7 +159,8 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@@ -257,7 +260,8 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@@ -312,6 +316,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.body
|
||||
});
|
||||
|
||||
@@ -370,6 +375,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.query
|
||||
});
|
||||
|
||||
@@ -421,6 +427,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
tokenId: req.params.tokenId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.body
|
||||
});
|
||||
|
||||
@@ -470,7 +477,8 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
tokenId: req.params.tokenId
|
||||
tokenId: req.params.tokenId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({
|
||||
id: true,
|
||||
@@ -142,8 +143,10 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityUniversalAuth.orgId,
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityAwsAuthDALFactory } from "./identity-aws-auth-dal";
|
||||
import { extractPrincipalArn } from "./identity-aws-auth-fns";
|
||||
import {
|
||||
@@ -149,8 +150,11 @@ export const identityAwsAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachAwsAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export type TAttachAwsAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateAwsAuthDTO = {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityAzureAuthDALFactory } from "./identity-azure-auth-dal";
|
||||
import { validateAzureIdentity } from "./identity-azure-auth-fns";
|
||||
import {
|
||||
@@ -122,8 +123,11 @@ export const identityAzureAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachAzureAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export type TAttachAzureAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateAzureAuthDTO = {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityGcpAuthDALFactory } from "./identity-gcp-auth-dal";
|
||||
import { validateIamIdentity, validateIdTokenIdentity } from "./identity-gcp-auth-fns";
|
||||
import {
|
||||
@@ -162,8 +163,11 @@ export const identityGcpAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachGcpAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ export type TAttachGcpAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateGcpAuthDTO = {
|
||||
|
||||
@@ -19,6 +19,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityJwtAuthDALFactory } from "./identity-jwt-auth-dal";
|
||||
import { doesFieldValueMatchJwtPolicy } from "./identity-jwt-auth-fns";
|
||||
import {
|
||||
@@ -250,8 +251,11 @@ export const identityJwtAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachJwtAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) {
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -19,6 +19,7 @@ export type TAttachJwtAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateJwtAuthDTO = {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityKubernetesAuthDALFactory } from "./identity-kubernetes-auth-dal";
|
||||
import { extractK8sUsername } from "./identity-kubernetes-auth-fns";
|
||||
import {
|
||||
@@ -101,7 +102,8 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${tokenReviewerJwt}`
|
||||
},
|
||||
|
||||
signal: AbortSignal.timeout(10000),
|
||||
timeout: 10000,
|
||||
// if ca cert, rejectUnauthorized: true
|
||||
httpsAgent: new https.Agent({
|
||||
ca: caCert,
|
||||
@@ -227,8 +229,11 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachKubernetesAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export type TAttachKubernetesAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateKubernetesAuthDTO = {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
|
||||
import { doesAudValueMatchOidcPolicy, doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
|
||||
import {
|
||||
@@ -222,8 +223,10 @@ export const identityOidcAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachOidcAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) {
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -13,6 +13,7 @@ export type TAttachOidcAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateOidcAuthDTO = {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityTokenAuthDALFactory } from "./identity-token-auth-dal";
|
||||
import {
|
||||
TAttachTokenAuthDTO,
|
||||
@@ -59,8 +60,11 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachTokenAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -126,8 +130,11 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TUpdateTokenAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -218,8 +225,11 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TRevokeTokenAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -271,8 +281,11 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
name
|
||||
name,
|
||||
isActorSuperAdmin
|
||||
}: TCreateTokenAuthTokenDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -350,8 +363,11 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TGetTokenAuthTokensDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -386,7 +402,8 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TUpdateTokenAuthTokenDTO) => {
|
||||
const foundToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
||||
@@ -398,6 +415,8 @@ export const identityTokenAuthServiceFactory = ({
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({ message: `Failed to find identity with ID ${foundToken.identityId}` });
|
||||
}
|
||||
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(foundToken.identityId, isActorSuperAdmin);
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have Token Auth"
|
||||
@@ -446,18 +465,22 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TRevokeTokenAuthTokenDTO) => {
|
||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
||||
[`${TableName.IdentityAccessToken}.isAccessTokenRevoked` as "isAccessTokenRevoked"]: false,
|
||||
[`${TableName.IdentityAccessToken}.authMethod` as "authMethod"]: IdentityAuthMethod.TOKEN_AUTH
|
||||
});
|
||||
|
||||
if (!identityAccessToken)
|
||||
throw new NotFoundError({
|
||||
message: `Token with ID ${tokenId} not found or already revoked`
|
||||
});
|
||||
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityAccessToken.identityId, isActorSuperAdmin);
|
||||
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({
|
||||
identityId: identityAccessToken.identityId
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ export type TAttachTokenAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateTokenAuthDTO = {
|
||||
@@ -14,6 +15,7 @@ export type TUpdateTokenAuthDTO = {
|
||||
accessTokenMaxTTL?: number;
|
||||
accessTokenNumUsesLimit?: number;
|
||||
accessTokenTrustedIps?: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetTokenAuthDTO = {
|
||||
@@ -22,24 +24,29 @@ export type TGetTokenAuthDTO = {
|
||||
|
||||
export type TRevokeTokenAuthDTO = {
|
||||
identityId: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TCreateTokenAuthTokenDTO = {
|
||||
identityId: string;
|
||||
name?: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetTokenAuthTokensDTO = {
|
||||
identityId: string;
|
||||
offset: number;
|
||||
limit: number;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateTokenAuthTokenDTO = {
|
||||
tokenId: string;
|
||||
name?: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TRevokeTokenAuthTokenDTO = {
|
||||
tokenId: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityUaClientSecretDALFactory } from "./identity-ua-client-secret-dal";
|
||||
import { TIdentityUaDALFactory } from "./identity-ua-dal";
|
||||
import {
|
||||
@@ -68,9 +69,15 @@ export const identityUaServiceFactory = ({
|
||||
isClientSecretRevoked: false
|
||||
});
|
||||
|
||||
const validClientSecretInfo = clientSecrtInfo.find(({ clientSecretHash }) =>
|
||||
bcrypt.compareSync(clientSecret, clientSecretHash)
|
||||
);
|
||||
let validClientSecretInfo: (typeof clientSecrtInfo)[0] | null = null;
|
||||
for await (const info of clientSecrtInfo) {
|
||||
const isMatch = await bcrypt.compare(clientSecret, info.clientSecretHash);
|
||||
if (isMatch) {
|
||||
validClientSecretInfo = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validClientSecretInfo) throw new UnauthorizedError({ message: "Invalid credentials" });
|
||||
|
||||
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
|
||||
@@ -103,7 +110,7 @@ export const identityUaServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
|
||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo.id, tx);
|
||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
{
|
||||
identityId: identityUa.identityId,
|
||||
@@ -150,8 +157,11 @@ export const identityUaServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
}: TAttachUaDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ export type TAttachUaDTO = {
|
||||
accessTokenNumUsesLimit: number;
|
||||
clientSecretTrustedIps: { ipAddress: string }[];
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateUaDTO = {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
|
||||
import { ActorType } from "../auth/auth-type";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityDALFactory } from "./identity-dal";
|
||||
import { TIdentityMetadataDALFactory } from "./identity-metadata-dal";
|
||||
import { TIdentityOrgDALFactory } from "./identity-org-dal";
|
||||
@@ -112,8 +113,11 @@ export const identityServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
metadata
|
||||
metadata,
|
||||
isActorSuperAdmin
|
||||
}: TUpdateIdentityDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(id, isActorSuperAdmin);
|
||||
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id });
|
||||
if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
@@ -209,7 +213,16 @@ export const identityServiceFactory = ({
|
||||
return identity;
|
||||
};
|
||||
|
||||
const deleteIdentity = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TDeleteIdentityDTO) => {
|
||||
const deleteIdentity = async ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
id,
|
||||
isActorSuperAdmin
|
||||
}: TDeleteIdentityDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(id, isActorSuperAdmin);
|
||||
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id });
|
||||
if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
|
||||
@@ -12,10 +12,12 @@ export type TUpdateIdentityDTO = {
|
||||
role?: string;
|
||||
name?: string;
|
||||
metadata?: { key: string; value: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TOrgPermission, "orgId">;
|
||||
|
||||
export type TDeleteIdentityDTO = {
|
||||
id: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TOrgPermission, "orgId">;
|
||||
|
||||
export type TGetIdentityByIdDTO = {
|
||||
|
||||
@@ -923,16 +923,14 @@ const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => {
|
||||
/**
|
||||
* Return list of projects for Windmill integration
|
||||
*/
|
||||
const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
const { data } = await request.get<{ id: string; name: string }[]>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
const getAppsWindmill = async ({ accessToken, url }: { accessToken: string; url?: string | null }) => {
|
||||
const apiUrl = url ? `${url}/api` : IntegrationUrls.WINDMILL_API_URL;
|
||||
const { data } = await request.get<{ id: string; name: string }[]>(`${apiUrl}/workspaces/list`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// check for write access of secrets in windmill workspaces
|
||||
const writeAccessCheck = data.map(async (app) => {
|
||||
@@ -941,7 +939,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
const folderPath = "f/folder/variable";
|
||||
|
||||
const { data: writeUser } = await request.post<object>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`,
|
||||
`${apiUrl}/w/${app.id}/variables/create`,
|
||||
{
|
||||
path: userPath,
|
||||
value: "variable",
|
||||
@@ -957,7 +955,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
);
|
||||
|
||||
const { data: writeFolder } = await request.post<object>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`,
|
||||
`${apiUrl}/w/${app.id}/variables/create`,
|
||||
{
|
||||
path: folderPath,
|
||||
value: "variable",
|
||||
@@ -974,14 +972,14 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
|
||||
// is write access is allowed then delete the created secrets from workspace
|
||||
if (writeUser && writeFolder) {
|
||||
await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, {
|
||||
await request.delete(`${apiUrl}/w/${app.id}/variables/delete/${userPath}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, {
|
||||
await request.delete(`${apiUrl}/w/${app.id}/variables/delete/${folderPath}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -1316,7 +1314,8 @@ export const getApps = async ({
|
||||
|
||||
case Integrations.WINDMILL:
|
||||
return getAppsWindmill({
|
||||
accessToken
|
||||
accessToken,
|
||||
url
|
||||
});
|
||||
|
||||
case Integrations.DIGITAL_OCEAN_APP_PLATFORM:
|
||||
|
||||
@@ -4127,10 +4127,10 @@ const syncSecretsWindmill = async ({
|
||||
is_secret: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const apiUrl = integration.url ? `${integration.url}/api` : IntegrationUrls.WINDMILL_API_URL;
|
||||
// get secrets stored in windmill workspace
|
||||
const res = (
|
||||
await request.get<WindmillSecret[]>(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, {
|
||||
await request.get<WindmillSecret[]>(`${apiUrl}/w/${integration.appId}/variables/list`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
@@ -4146,7 +4146,6 @@ const syncSecretsWindmill = async ({
|
||||
|
||||
// eslint-disable-next-line
|
||||
const pattern = new RegExp("^(u/|f/)[a-zA-Z0-9_-]+/([a-zA-Z0-9_-]+/)*[a-zA-Z0-9_-]*[^/]$");
|
||||
|
||||
for await (const key of Object.keys(secrets)) {
|
||||
if ((key.startsWith("u/") || key.startsWith("f/")) && pattern.test(key)) {
|
||||
if (!(key in res)) {
|
||||
@@ -4154,7 +4153,7 @@ const syncSecretsWindmill = async ({
|
||||
// -> create secret
|
||||
|
||||
await request.post(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/create`,
|
||||
`${apiUrl}/w/${integration.appId}/variables/create`,
|
||||
{
|
||||
path: key,
|
||||
value: secrets[key].value,
|
||||
@@ -4171,7 +4170,7 @@ const syncSecretsWindmill = async ({
|
||||
} else {
|
||||
// -> update secret
|
||||
await request.post(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/update/${res[key].path}`,
|
||||
`${apiUrl}/w/${integration.appId}/variables/update/${res[key].path}`,
|
||||
{
|
||||
path: key,
|
||||
value: secrets[key].value,
|
||||
@@ -4192,16 +4191,13 @@ const syncSecretsWindmill = async ({
|
||||
for await (const key of Object.keys(res)) {
|
||||
if (!(key in secrets)) {
|
||||
// -> delete secret
|
||||
await request.delete(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/delete/${res[key].path}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.delete(`${apiUrl}/w/${integration.appId}/variables/delete/${res[key].path}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName, TProjectEnvironments, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas";
|
||||
import { TableName, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas";
|
||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||
import { groupBy, removeTrailingSlash } from "@app/lib/fn";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
@@ -41,12 +41,12 @@ const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: str
|
||||
void baseQb
|
||||
.select({
|
||||
depth: 1,
|
||||
// latestFolderVerId: db.raw("NULL::uuid"),
|
||||
path: db.raw("'/'")
|
||||
})
|
||||
.from(TableName.SecretFolder)
|
||||
.where({
|
||||
parentId: null
|
||||
parentId: null,
|
||||
name: "root"
|
||||
})
|
||||
.whereIn(
|
||||
"envId",
|
||||
@@ -69,9 +69,7 @@ const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: str
|
||||
.where((wb) =>
|
||||
formatedQuery.map(({ secretPath }) =>
|
||||
wb.orWhereRaw(
|
||||
`depth = array_position(ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[], ${
|
||||
TableName.SecretFolder
|
||||
}.name,depth)`,
|
||||
`secret_folders.name = (ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[])[depth]`,
|
||||
[...secretPath]
|
||||
)
|
||||
)
|
||||
@@ -107,7 +105,6 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
||||
void baseQb
|
||||
.select({
|
||||
depth: 1,
|
||||
// latestFolderVerId: db.raw("NULL::uuid"),
|
||||
path: db.raw("'/'")
|
||||
})
|
||||
.from(TableName.SecretFolder)
|
||||
@@ -117,6 +114,11 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
||||
parentId: null
|
||||
})
|
||||
.whereIn(`${TableName.Environment}.slug`, environments)
|
||||
.select(
|
||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||
db.ref("name").withSchema(TableName.Environment).as("envName"),
|
||||
db.ref("projectId").withSchema(TableName.Environment)
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.union(
|
||||
(qb) =>
|
||||
@@ -128,21 +130,20 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
||||
depth: db.raw("parent.depth + 1"),
|
||||
path: db.raw(
|
||||
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||
)
|
||||
),
|
||||
envSlug: db.ref("envSlug").withSchema("parent"),
|
||||
envName: db.ref("envName").withSchema("parent"),
|
||||
projectId: db.ref("projectId").withSchema("parent")
|
||||
})
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.whereRaw(
|
||||
`depth = array_position(ARRAY[${pathSegments
|
||||
.map(() => "?")
|
||||
.join(",")}]::varchar[], secret_folders.name,depth)`,
|
||||
[...pathSegments]
|
||||
)
|
||||
.whereRaw(`secret_folders.name = (ARRAY[${pathSegments.map(() => "?").join(",")}]::varchar[])[depth]`, [
|
||||
...pathSegments
|
||||
])
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
|
||||
);
|
||||
})
|
||||
.from<TSecretFolders & { depth: number; path: string }>("parent")
|
||||
.leftJoin<TProjectEnvironments>(TableName.Environment, `${TableName.Environment}.id`, "parent.envId")
|
||||
.select<
|
||||
(TSecretFolders & {
|
||||
depth: number;
|
||||
@@ -152,13 +153,7 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
||||
envName: string;
|
||||
projectId: string;
|
||||
})[]
|
||||
>(
|
||||
selectAllTableCols("parent" as TableName.SecretFolder),
|
||||
db.ref("id").withSchema(TableName.Environment).as("envId"),
|
||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||
db.ref("name").withSchema(TableName.Environment).as("envName"),
|
||||
db.ref("projectId").withSchema(TableName.Environment)
|
||||
);
|
||||
>(selectAllTableCols("parent" as TableName.SecretFolder));
|
||||
};
|
||||
|
||||
const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: string[]) =>
|
||||
@@ -220,19 +215,12 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
throw new BadRequestError({
|
||||
message: "Invalid secret path. Only alphanumeric characters, dashes, and underscores are allowed."
|
||||
});
|
||||
|
||||
const formatedPath = removeTrailingSlash(path);
|
||||
try {
|
||||
const folder = await sqlFindFolderByPathQuery(
|
||||
tx || db.replicaNode(),
|
||||
projectId,
|
||||
[environment],
|
||||
removeTrailingSlash(path)
|
||||
)
|
||||
.orderBy("depth", "desc")
|
||||
const query = sqlFindFolderByPathQuery(tx || db.replicaNode(), projectId, [environment], formatedPath)
|
||||
.where("path", formatedPath)
|
||||
.first();
|
||||
if (folder && folder.path !== removeTrailingSlash(path)) {
|
||||
return;
|
||||
}
|
||||
const folder = await query;
|
||||
if (!folder) return;
|
||||
const { envId: id, envName: name, envSlug: slug, ...el } = folder;
|
||||
return { ...el, envId: id, environment: { id, name, slug } };
|
||||
@@ -250,22 +238,13 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
});
|
||||
|
||||
try {
|
||||
const pathDepth = removeTrailingSlash(path).split("/").filter(Boolean).length + 1;
|
||||
|
||||
const formatedPath = removeTrailingSlash(path);
|
||||
const folders = await sqlFindFolderByPathQuery(
|
||||
tx || db.replicaNode(),
|
||||
projectId,
|
||||
environments,
|
||||
removeTrailingSlash(path)
|
||||
)
|
||||
.orderBy("depth", "desc")
|
||||
.where("depth", pathDepth);
|
||||
|
||||
const firstFolder = folders[0];
|
||||
|
||||
if (firstFolder && firstFolder.path !== removeTrailingSlash(path)) {
|
||||
return [];
|
||||
}
|
||||
formatedPath
|
||||
).where("path", removeTrailingSlash(path));
|
||||
|
||||
return folders.map((folder) => {
|
||||
const { envId: id, envName: name, envSlug: slug, ...el } = folder;
|
||||
@@ -323,7 +302,6 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
const findSecretPathByFolderIds = async (projectId: string, folderIds: string[], tx?: Knex) => {
|
||||
try {
|
||||
const folders = await sqlFindSecretPathByFolderId(tx || db.replicaNode(), projectId, folderIds);
|
||||
|
||||
// travelling all the way from leaf node to root contains real path
|
||||
const rootFolders = groupBy(
|
||||
folders.filter(({ parentId }) => parentId === null),
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||
|
||||
import { ActorType } from "../auth/auth-type";
|
||||
import { getServerCfg } from "./super-admin-service";
|
||||
|
||||
export const isSuperAdmin = (auth: TAuthMode) => {
|
||||
if (auth.actor === ActorType.USER && auth.user.superAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auth.actor === ActorType.IDENTITY && auth.isInstanceAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const validateIdentityUpdateForSuperAdminPrivileges = async (
|
||||
identityId: string,
|
||||
isActorSuperAdmin?: boolean
|
||||
) => {
|
||||
const serverCfg = await getServerCfg();
|
||||
if (serverCfg.adminIdentityIds?.includes(identityId) && !isActorSuperAdmin) {
|
||||
throw new ForbiddenRequestError({
|
||||
message:
|
||||
"You are attempting to modify an instance admin identity. This requires elevated instance admin privileges"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,16 +1,21 @@
|
||||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
||||
import { IdentityAuthMethod, OrgMembershipRole, TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||
import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||
|
||||
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||
import { AuthMethod } from "../auth/auth-type";
|
||||
import { AuthMethod, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TIdentityTokenAuthDALFactory } from "../identity-token-auth/identity-token-auth-dal";
|
||||
import { KMS_ROOT_CONFIG_UUID } from "../kms/kms-fns";
|
||||
import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
@@ -20,10 +25,19 @@ import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
||||
import { UserAliasType } from "../user-alias/user-alias-types";
|
||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||
import { LoginMethod, TAdminGetIdentitiesDTO, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types";
|
||||
import {
|
||||
LoginMethod,
|
||||
TAdminBootstrapInstanceDTO,
|
||||
TAdminGetIdentitiesDTO,
|
||||
TAdminGetUsersDTO,
|
||||
TAdminSignUpDTO
|
||||
} from "./super-admin-types";
|
||||
|
||||
type TSuperAdminServiceFactoryDep = {
|
||||
identityDAL: Pick<TIdentityDALFactory, "getIdentitiesByFilter">;
|
||||
identityDAL: TIdentityDALFactory;
|
||||
identityTokenAuthDAL: TIdentityTokenAuthDALFactory;
|
||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||
serverCfgDAL: TSuperAdminDALFactory;
|
||||
userDAL: TUserDALFactory;
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
||||
@@ -60,7 +74,10 @@ export const superAdminServiceFactory = ({
|
||||
keyStore,
|
||||
kmsRootConfigDAL,
|
||||
kmsService,
|
||||
licenseService
|
||||
licenseService,
|
||||
identityAccessTokenDAL,
|
||||
identityTokenAuthDAL,
|
||||
identityOrgMembershipDAL
|
||||
}: TSuperAdminServiceFactoryDep) => {
|
||||
const initServerCfg = async () => {
|
||||
// TODO(akhilmhdh): bad pattern time less change this later to me itself
|
||||
@@ -274,6 +291,137 @@ export const superAdminServiceFactory = ({
|
||||
return { token, user: userInfo, organization };
|
||||
};
|
||||
|
||||
const bootstrapInstance = async ({ email, password, organizationName }: TAdminBootstrapInstanceDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID);
|
||||
if (serverCfg?.initialized) {
|
||||
throw new BadRequestError({ message: "Instance has already been set up" });
|
||||
}
|
||||
|
||||
const existingUser = await userDAL.findOne({ email });
|
||||
if (existingUser) throw new BadRequestError({ name: "Instance initialization", message: "User already exists" });
|
||||
|
||||
const userInfo = await userDAL.transaction(async (tx) => {
|
||||
const newUser = await userDAL.create(
|
||||
{
|
||||
firstName: "Admin",
|
||||
lastName: "User",
|
||||
username: email,
|
||||
email,
|
||||
superAdmin: true,
|
||||
isGhost: false,
|
||||
isAccepted: true,
|
||||
authMethods: [AuthMethod.EMAIL],
|
||||
isEmailVerified: true
|
||||
},
|
||||
tx
|
||||
);
|
||||
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(password);
|
||||
const encKeys = await generateUserSrpKeys(email, password);
|
||||
|
||||
const userEnc = await userDAL.createUserEncryption(
|
||||
{
|
||||
userId: newUser.id,
|
||||
encryptionVersion: 2,
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
publicKey: encKeys.publicKey,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier,
|
||||
serverEncryptedPrivateKeyEncoding: encoding,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKey: ciphertext
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
return { user: newUser, enc: userEnc };
|
||||
});
|
||||
|
||||
const initialOrganizationName = organizationName ?? "Admin Org";
|
||||
|
||||
const organization = await orgService.createOrganization({
|
||||
userId: userInfo.user.id,
|
||||
userEmail: userInfo.user.email,
|
||||
orgName: initialOrganizationName
|
||||
});
|
||||
|
||||
const { identity, credentials } = await identityDAL.transaction(async (tx) => {
|
||||
const newIdentity = await identityDAL.create({ name: "Instance Admin Identity" }, tx);
|
||||
await identityOrgMembershipDAL.create(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
orgId: organization.id,
|
||||
role: OrgMembershipRole.Admin
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const tokenAuth = await identityTokenAuthDAL.create(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
accessTokenMaxTTL: 0,
|
||||
accessTokenTTL: 0,
|
||||
accessTokenNumUsesLimit: 0,
|
||||
accessTokenTrustedIps: JSON.stringify([
|
||||
{
|
||||
type: "ipv4",
|
||||
prefix: 0,
|
||||
ipAddress: "0.0.0.0"
|
||||
},
|
||||
{
|
||||
type: "ipv6",
|
||||
prefix: 0,
|
||||
ipAddress: "::"
|
||||
}
|
||||
])
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
isAccessTokenRevoked: false,
|
||||
accessTokenTTL: tokenAuth.accessTokenTTL,
|
||||
accessTokenMaxTTL: tokenAuth.accessTokenMaxTTL,
|
||||
accessTokenNumUses: 0,
|
||||
accessTokenNumUsesLimit: tokenAuth.accessTokenNumUsesLimit,
|
||||
name: "Instance Admin Token",
|
||||
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const generatedAccessToken = jwt.sign(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
identityAccessTokenId: newToken.id,
|
||||
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
|
||||
} as TIdentityAccessTokenJwtPayload,
|
||||
appCfg.AUTH_SECRET
|
||||
);
|
||||
|
||||
return { identity: newIdentity, auth: tokenAuth, credentials: { token: generatedAccessToken } };
|
||||
});
|
||||
|
||||
await updateServerCfg({ initialized: true, adminIdentityIds: [identity.id] }, userInfo.user.id);
|
||||
|
||||
return {
|
||||
user: userInfo,
|
||||
organization,
|
||||
machineIdentity: {
|
||||
...identity,
|
||||
credentials
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const getUsers = ({ offset, limit, searchTerm, adminsOnly }: TAdminGetUsersDTO) => {
|
||||
return userDAL.getUsersByFilter({
|
||||
limit,
|
||||
@@ -289,13 +437,46 @@ export const superAdminServiceFactory = ({
|
||||
return user;
|
||||
};
|
||||
|
||||
const getIdentities = ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||
return identityDAL.getIdentitiesByFilter({
|
||||
const deleteIdentitySuperAdminAccess = async (identityId: string, actorId: string) => {
|
||||
const identity = await identityDAL.findById(identityId);
|
||||
if (!identity) {
|
||||
throw new NotFoundError({ name: "Identity", message: "Identity not found" });
|
||||
}
|
||||
|
||||
const currentAdminIdentityIds = (await getServerCfg()).adminIdentityIds ?? [];
|
||||
if (!currentAdminIdentityIds?.includes(identityId)) {
|
||||
throw new BadRequestError({ name: "Identity", message: "Identity does not have super admin access" });
|
||||
}
|
||||
|
||||
await updateServerCfg({ adminIdentityIds: currentAdminIdentityIds.filter((id) => id !== identityId) }, actorId);
|
||||
|
||||
return identity;
|
||||
};
|
||||
|
||||
const deleteUserSuperAdminAccess = async (userId: string) => {
|
||||
const user = await userDAL.findById(userId);
|
||||
if (!user) {
|
||||
throw new NotFoundError({ name: "User", message: "User not found" });
|
||||
}
|
||||
|
||||
const updatedUser = userDAL.updateById(userId, { superAdmin: false });
|
||||
|
||||
return updatedUser;
|
||||
};
|
||||
|
||||
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||
const identities = await identityDAL.getIdentitiesByFilter({
|
||||
limit,
|
||||
offset,
|
||||
searchTerm,
|
||||
sortBy: "name"
|
||||
});
|
||||
const serverCfg = await getServerCfg();
|
||||
|
||||
return identities.map((identity) => ({
|
||||
...identity,
|
||||
isInstanceAdmin: Boolean(serverCfg?.adminIdentityIds?.includes(identity.id))
|
||||
}));
|
||||
};
|
||||
|
||||
const grantServerAdminAccessToUser = async (userId: string) => {
|
||||
@@ -393,12 +574,15 @@ export const superAdminServiceFactory = ({
|
||||
initServerCfg,
|
||||
updateServerCfg,
|
||||
adminSignUp,
|
||||
bootstrapInstance,
|
||||
getUsers,
|
||||
deleteUser,
|
||||
getIdentities,
|
||||
getAdminSlackConfig,
|
||||
updateRootEncryptionStrategy,
|
||||
getConfiguredEncryptionStrategies,
|
||||
grantServerAdminAccessToUser
|
||||
grantServerAdminAccessToUser,
|
||||
deleteIdentitySuperAdminAccess,
|
||||
deleteUserSuperAdminAccess
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,6 +16,12 @@ export type TAdminSignUpDTO = {
|
||||
userAgent: string;
|
||||
};
|
||||
|
||||
export type TAdminBootstrapInstanceDTO = {
|
||||
email: string;
|
||||
password: string;
|
||||
organizationName: string;
|
||||
};
|
||||
|
||||
export type TAdminGetUsersDTO = {
|
||||
offset: number;
|
||||
limit: number;
|
||||
|
||||
Reference in New Issue
Block a user