mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 12:26:13 +00:00
feat(infisical-pg): fixing minor compatiability issues with frontend and backend on identity
This commit is contained in:
@@ -12,6 +12,7 @@ Component List
|
|||||||
--------------
|
--------------
|
||||||
1. Service component
|
1. Service component
|
||||||
2. DAL component
|
2. DAL component
|
||||||
|
3. Router component
|
||||||
`);
|
`);
|
||||||
const componentType = parseInt(prompt("Select a component: "), 10);
|
const componentType = parseInt(prompt("Select a component: "), 10);
|
||||||
|
|
||||||
@@ -88,6 +89,35 @@ export const ${dalName} = (db: TDbClient) => {
|
|||||||
|
|
||||||
return { };
|
return { };
|
||||||
};
|
};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
} else if (componentType === 3) {
|
||||||
|
const name = prompt("Enter router name: ");
|
||||||
|
const version = prompt("Version number: ");
|
||||||
|
const pascalCase = name
|
||||||
|
.split("-")
|
||||||
|
.map((el) => `${el[0].toUpperCase()}${el.slice(1)}`)
|
||||||
|
.join("");
|
||||||
|
writeFileSync(
|
||||||
|
path.join(__dirname, `../src/server/routes/v${Number(version)}/${name}-router.ts`),
|
||||||
|
`import { z } from "zod";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const register${pascalCase}Router = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
params: z.object({}),
|
||||||
|
response: {
|
||||||
|
200: z.object({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {}
|
||||||
|
});
|
||||||
|
};
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+14
@@ -8,6 +8,10 @@ import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
|
|||||||
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
|
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
|
||||||
import { AuthMode } from "@app/services/auth/auth-signup-type";
|
import { AuthMode } from "@app/services/auth/auth-signup-type";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
||||||
|
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
||||||
|
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
|
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
||||||
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
||||||
import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
||||||
import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service";
|
import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service";
|
||||||
@@ -21,10 +25,13 @@ import { TProjectRoleServiceFactory } from "@app/services/project-role/project-r
|
|||||||
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
||||||
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
||||||
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||||
|
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||||
|
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
||||||
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/token/token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/token/token-service";
|
||||||
import { TUserDalFactory } from "@app/services/user/user-dal";
|
import { TUserDalFactory } from "@app/services/user/user-dal";
|
||||||
import { TUserServiceFactory } from "@app/services/user/user-service";
|
import { TUserServiceFactory } from "@app/services/user/user-service";
|
||||||
|
import { TWebhookServiceFactory } from "@app/services/webhook/webhook-service";
|
||||||
|
|
||||||
declare module "fastify" {
|
declare module "fastify" {
|
||||||
interface FastifyRequest {
|
interface FastifyRequest {
|
||||||
@@ -71,11 +78,18 @@ declare module "fastify" {
|
|||||||
projectKey: TProjectKeyServiceFactory;
|
projectKey: TProjectKeyServiceFactory;
|
||||||
projectRole: TProjectRoleServiceFactory;
|
projectRole: TProjectRoleServiceFactory;
|
||||||
secret: TSecretServiceFactory;
|
secret: TSecretServiceFactory;
|
||||||
|
secretTag: TSecretTagServiceFactory;
|
||||||
secretImport: TSecretImportServiceFactory;
|
secretImport: TSecretImportServiceFactory;
|
||||||
projectBot: TProjectBotServiceFactory;
|
projectBot: TProjectBotServiceFactory;
|
||||||
folder: TSecretFolderServiceFactory;
|
folder: TSecretFolderServiceFactory;
|
||||||
integration: TIntegrationServiceFactory;
|
integration: TIntegrationServiceFactory;
|
||||||
integrationAuth: TIntegrationAuthServiceFactory;
|
integrationAuth: TIntegrationAuthServiceFactory;
|
||||||
|
webhook: TWebhookServiceFactory;
|
||||||
|
serviceToken: TServiceTokenServiceFactory;
|
||||||
|
identity: TIdentityServiceFactory;
|
||||||
|
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
||||||
|
identityProject: TIdentityProjectServiceFactory;
|
||||||
|
identityUa: TIdentityUaServiceFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
|
|||||||
@@ -1,43 +1,94 @@
|
|||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TOrgMemberships, TProjectMemberships } from "@app/db/schemas";
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
export type TPermissionDalFactory = ReturnType<typeof permissionDalFactory>;
|
export type TPermissionDalFactory = ReturnType<typeof permissionDalFactory>;
|
||||||
|
|
||||||
export const permissionDalFactory = (db: TDbClient) => {
|
export const permissionDalFactory = (db: TDbClient) => {
|
||||||
const getOrgPermission = async (
|
const getOrgPermission = async (userId: string, orgId: string) => {
|
||||||
userId: string,
|
try {
|
||||||
orgId: string
|
const membership = await db(TableName.OrgMembership)
|
||||||
): Promise<(TOrgMemberships & { permissions: string }) | undefined> => {
|
.leftJoin(
|
||||||
const membership = await db(TableName.OrgMembership)
|
TableName.OrgRoles,
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
`${TableName.OrgMembership}.roleId`,
|
||||||
.where("userId", userId)
|
`${TableName.OrgRoles}.id`
|
||||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
)
|
||||||
.select(`${TableName.OrgMembership}.*`, "permissions")
|
.where("userId", userId)
|
||||||
.first();
|
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
||||||
|
.select("permissions")
|
||||||
|
.select(selectAllTableCols(TableName.OrgMembership))
|
||||||
|
.first();
|
||||||
|
|
||||||
return membership;
|
return membership;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "GetOrgPermission" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectPermission = async (
|
const getOrgIdentityPermission = async (identityId: string, orgId: string) => {
|
||||||
userId: string,
|
try {
|
||||||
projectId: string
|
const membership = await db(TableName.IdentityOrgMembership)
|
||||||
): Promise<(TProjectMemberships & { permissions: string }) | undefined> => {
|
.leftJoin(
|
||||||
const membership = await db(TableName.ProjectMembership)
|
TableName.OrgRoles,
|
||||||
.leftJoin(
|
`${TableName.IdentityOrgMembership}.roleId`,
|
||||||
TableName.ProjectRoles,
|
`${TableName.OrgRoles}.id`
|
||||||
`${TableName.ProjectMembership}.roleId`,
|
)
|
||||||
`${TableName.ProjectRoles}.id`
|
.where("identityId", identityId)
|
||||||
)
|
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId)
|
||||||
.where("userId", userId)
|
.select(selectAllTableCols(TableName.IdentityOrgMembership))
|
||||||
.where(`${TableName.ProjectMembership}.projectId`, projectId)
|
.select("permissions")
|
||||||
.select(`${TableName.ProjectMembership}.*`, "permissions")
|
.first();
|
||||||
.first();
|
return membership;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "GetOrgIdentityPermission" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return membership;
|
const getProjectPermission = async (userId: string, projectId: string) => {
|
||||||
|
try {
|
||||||
|
const membership = await db(TableName.ProjectMembership)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.ProjectRoles,
|
||||||
|
`${TableName.ProjectMembership}.roleId`,
|
||||||
|
`${TableName.ProjectRoles}.id`
|
||||||
|
)
|
||||||
|
.where("userId", userId)
|
||||||
|
.where(`${TableName.ProjectMembership}.projectId`, projectId)
|
||||||
|
.select(selectAllTableCols(TableName.ProjectMembership))
|
||||||
|
.select("permissions")
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return membership;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getProjectIdentityPermission = async (identityId: string, projectId: string) => {
|
||||||
|
try {
|
||||||
|
const membership = await db(TableName.IdentityProjectMembership)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.ProjectRoles,
|
||||||
|
`${TableName.IdentityProjectMembership}.roleId`,
|
||||||
|
`${TableName.ProjectRoles}.id`
|
||||||
|
)
|
||||||
|
.where("identityId", identityId)
|
||||||
|
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||||
|
.select(selectAllTableCols(TableName.IdentityProjectMembership))
|
||||||
|
.select("permissions")
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return membership;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "GetProjectIdentityPermission" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getOrgPermission,
|
getOrgPermission,
|
||||||
getProjectPermission
|
getOrgIdentityPermission,
|
||||||
|
getProjectPermission,
|
||||||
|
getProjectIdentityPermission
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ import {
|
|||||||
projectAdminPermissions,
|
projectAdminPermissions,
|
||||||
projectMemberPermissions,
|
projectMemberPermissions,
|
||||||
projectNoAccessPermissions,
|
projectNoAccessPermissions,
|
||||||
ProjectPermissionSet,
|
ProjectPermissionSet
|
||||||
projectViewerPermission
|
|
||||||
} from "./project-permission";
|
} from "./project-permission";
|
||||||
|
|
||||||
type TPermissionServiceFactoryDep = {
|
type TPermissionServiceFactoryDep = {
|
||||||
@@ -36,35 +35,101 @@ export const permissionServiceFactory = ({
|
|||||||
orgRoleDal,
|
orgRoleDal,
|
||||||
projectRoleDal
|
projectRoleDal
|
||||||
}: TPermissionServiceFactoryDep) => {
|
}: TPermissionServiceFactoryDep) => {
|
||||||
|
const buildOrgPermission = (role: string, permission?: unknown) => {
|
||||||
|
switch (role) {
|
||||||
|
case OrgMembershipRole.Admin:
|
||||||
|
return orgAdminPermissions;
|
||||||
|
case OrgMembershipRole.Member:
|
||||||
|
return orgMemberPermissions;
|
||||||
|
case OrgMembershipRole.NoAccess:
|
||||||
|
return orgNoAccessPermissions;
|
||||||
|
case OrgMembershipRole.Custom:
|
||||||
|
return createMongoAbility<OrgPermissionSet>(
|
||||||
|
unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(
|
||||||
|
permission as PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[]
|
||||||
|
),
|
||||||
|
{
|
||||||
|
conditionsMatcher
|
||||||
|
}
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new BadRequestError({ name: "OrgRoleInvalid", message: "Org role not found" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildProjectPermission = (role: string, permission?: unknown) => {
|
||||||
|
switch (role) {
|
||||||
|
case ProjectMembershipRole.Admin:
|
||||||
|
return projectAdminPermissions;
|
||||||
|
case ProjectMembershipRole.Member:
|
||||||
|
return projectMemberPermissions;
|
||||||
|
case ProjectMembershipRole.NoAccess:
|
||||||
|
return projectNoAccessPermissions;
|
||||||
|
case ProjectMembershipRole.Custom:
|
||||||
|
return createMongoAbility<ProjectPermissionSet>(
|
||||||
|
unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
|
||||||
|
permission as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
|
||||||
|
),
|
||||||
|
{
|
||||||
|
conditionsMatcher
|
||||||
|
}
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new BadRequestError({
|
||||||
|
name: "ProjectRoleInvalid",
|
||||||
|
message: "Project role not found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get user permission in an organization
|
* Get user permission in an organization
|
||||||
* */
|
* */
|
||||||
const getUserOrgPermission = async (userId: string, orgId: string) => {
|
const getUserOrgPermission = async (userId: string, orgId: string) => {
|
||||||
const membership = await permissionDal.getOrgPermission(userId, orgId);
|
const membership = await permissionDal.getOrgPermission(userId, orgId);
|
||||||
if (!membership) throw new UnauthorizedError({ name: "User not in org" });
|
if (!membership) throw new UnauthorizedError({ name: "User not in org" });
|
||||||
if (membership.role === "custom" && !membership.permissions) {
|
if (membership.role === OrgMembershipRole.Custom && !membership.permissions) {
|
||||||
throw new BadRequestError({ name: "Custom permission not found" });
|
throw new BadRequestError({ name: "Custom permission not found" });
|
||||||
}
|
}
|
||||||
|
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
|
||||||
|
};
|
||||||
|
|
||||||
if (membership.role === OrgMembershipRole.Admin)
|
const getIdentityOrgPermission = async (identityId: string, orgId: string) => {
|
||||||
return { permission: orgAdminPermissions, membership };
|
const membership = await permissionDal.getOrgIdentityPermission(identityId, orgId);
|
||||||
if (membership.role === OrgMembershipRole.Member)
|
if (!membership) throw new UnauthorizedError({ name: "Identity not in org" });
|
||||||
return { permission: orgMemberPermissions, membership };
|
if (membership.role === OrgMembershipRole.Custom && !membership.permissions) {
|
||||||
|
throw new BadRequestError({ name: "Custom permission not found" });
|
||||||
if (membership.role === OrgMembershipRole.NoAccess)
|
|
||||||
return { permission: orgNoAccessPermissions, membership };
|
|
||||||
if (membership.role === OrgMembershipRole.Custom) {
|
|
||||||
const permission = createMongoAbility<OrgPermissionSet>(
|
|
||||||
// akhilmhdh: putting any due to ts incompatiable matching with string and the other
|
|
||||||
unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(membership.permissions as any),
|
|
||||||
{
|
|
||||||
conditionsMatcher
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return { permission, membership };
|
|
||||||
}
|
}
|
||||||
|
return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
|
||||||
|
};
|
||||||
|
|
||||||
throw new BadRequestError({ name: "Role missing", message: "User role not found" });
|
const getOrgPermission = async (type: ActorType, id: string, orgId: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case ActorType.USER:
|
||||||
|
return getUserOrgPermission(id, orgId);
|
||||||
|
case ActorType.IDENTITY:
|
||||||
|
return getIdentityOrgPermission(id, orgId);
|
||||||
|
default:
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: "Permission not defined",
|
||||||
|
name: "Get org permission"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// instead of actor type this will fetch by role slug. meaning it can be the pre defined slugs like
|
||||||
|
// admin member or user defined ones like biller etc
|
||||||
|
const getOrgPermissionByRole = async (role: string, orgId: string) => {
|
||||||
|
const isCustomRole = !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole);
|
||||||
|
if (isCustomRole) {
|
||||||
|
const orgRole = await orgRoleDal.findOne({ slug: role, orgId });
|
||||||
|
if (!orgRole) throw new BadRequestError({ message: "Role not found" });
|
||||||
|
return {
|
||||||
|
permission: buildOrgPermission(OrgMembershipRole.Custom, orgRole.permissions),
|
||||||
|
role: orgRole
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { permission: buildOrgPermission(role, []) };
|
||||||
};
|
};
|
||||||
|
|
||||||
// user permission for a project in an organization
|
// user permission for a project in an organization
|
||||||
@@ -74,33 +139,30 @@ export const permissionServiceFactory = ({
|
|||||||
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
|
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
|
||||||
throw new BadRequestError({ name: "Custom permission not found" });
|
throw new BadRequestError({ name: "Custom permission not found" });
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
permission: buildProjectPermission(membership.role, membership.permissions),
|
||||||
|
membership
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
if (membership.role === ProjectMembershipRole.Admin)
|
const getIdentityProjectPermission = async (identityId: string, projectId: string) => {
|
||||||
return { permission: projectAdminPermissions, membership };
|
const membership = await permissionDal.getProjectIdentityPermission(identityId, projectId);
|
||||||
if (membership.role === ProjectMembershipRole.Member)
|
if (!membership) throw new UnauthorizedError({ name: "Identity not in org" });
|
||||||
return { permission: projectMemberPermissions, membership };
|
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
|
||||||
if (membership.role === ProjectMembershipRole.Viewer)
|
throw new BadRequestError({ name: "Custom permission not found" });
|
||||||
return { permission: projectViewerPermission, membership };
|
|
||||||
if (membership.role === ProjectMembershipRole.NoAccess)
|
|
||||||
return { permission: projectNoAccessPermissions, membership };
|
|
||||||
if (membership.role === ProjectMembershipRole.Custom) {
|
|
||||||
const permission = createMongoAbility<ProjectPermissionSet>(
|
|
||||||
// akhilmhdh: putting any due to ts incompatiable matching with string and the other
|
|
||||||
unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(membership.permissions as any),
|
|
||||||
{
|
|
||||||
conditionsMatcher
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return { permission, membership };
|
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
throw new BadRequestError({ name: "Role missing", message: "User role not found" });
|
permission: buildProjectPermission(membership.role, membership.permissions),
|
||||||
|
membership
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectPermission = async (type: ActorType, id: string, projectId: string) => {
|
const getProjectPermission = async (type: ActorType, id: string, projectId: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ActorType.USER:
|
case ActorType.USER:
|
||||||
return getUserProjectPermission(id, projectId);
|
return getUserProjectPermission(id, projectId);
|
||||||
|
case ActorType.IDENTITY:
|
||||||
|
return getIdentityProjectPermission(id, projectId);
|
||||||
default:
|
default:
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: "Permission not defined",
|
message: "Permission not defined",
|
||||||
@@ -109,76 +171,19 @@ export const permissionServiceFactory = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrgPermission = async (type: ActorType, id: string, orgId: string) => {
|
|
||||||
switch (type) {
|
|
||||||
case ActorType.USER:
|
|
||||||
return getUserOrgPermission(id, orgId);
|
|
||||||
default:
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "Permission not defined",
|
|
||||||
name: "Get org permission"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOrgPermissionByRole = async (role: string, orgId: string) => {
|
|
||||||
const isCustomRole = !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole);
|
|
||||||
if (isCustomRole) {
|
|
||||||
const orgRole = await orgRoleDal.findOne({ slug: role, orgId });
|
|
||||||
if (!orgRole) throw new BadRequestError({ message: "Role not found" });
|
|
||||||
return {
|
|
||||||
permission: createMongoAbility<OrgPermissionSet>(
|
|
||||||
unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(
|
|
||||||
(orgRole.permissions as PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[]) || []
|
|
||||||
),
|
|
||||||
{
|
|
||||||
conditionsMatcher
|
|
||||||
}
|
|
||||||
),
|
|
||||||
role: orgRole
|
|
||||||
};
|
|
||||||
}
|
|
||||||
switch (role) {
|
|
||||||
case OrgMembershipRole.Admin:
|
|
||||||
return { permission: orgAdminPermissions };
|
|
||||||
case OrgMembershipRole.Member:
|
|
||||||
return { permission: orgMemberPermissions };
|
|
||||||
case OrgMembershipRole.NoAccess:
|
|
||||||
return { permission: orgNoAccessPermissions };
|
|
||||||
default:
|
|
||||||
throw new BadRequestError({ message: "Org role not found" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getProjectPermissionByRole = async (role: string, projectId: string) => {
|
const getProjectPermissionByRole = async (role: string, projectId: string) => {
|
||||||
const isCustomRole = !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole);
|
const isCustomRole = !Object.values(ProjectMembershipRole).includes(
|
||||||
|
role as ProjectMembershipRole
|
||||||
|
);
|
||||||
if (isCustomRole) {
|
if (isCustomRole) {
|
||||||
const projectRole = await projectRoleDal.findOne({ slug: role, projectId });
|
const projectRole = await projectRoleDal.findOne({ slug: role, projectId });
|
||||||
if (!projectRole) throw new BadRequestError({ message: "Role not found" });
|
if (!projectRole) throw new BadRequestError({ message: "Role not found" });
|
||||||
return {
|
return {
|
||||||
permission: createMongoAbility<ProjectPermissionSet>(
|
permission: buildProjectPermission(ProjectMembershipRole.Custom, projectRole.permissions),
|
||||||
unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
|
|
||||||
(projectRole.permissions as PackRule<
|
|
||||||
RawRuleOf<MongoAbility<ProjectPermissionSet>>
|
|
||||||
>[]) || []
|
|
||||||
),
|
|
||||||
{
|
|
||||||
conditionsMatcher
|
|
||||||
}
|
|
||||||
),
|
|
||||||
role: projectRole
|
role: projectRole
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
switch (role) {
|
return { permission: buildProjectPermission(role, []) };
|
||||||
case ProjectMembershipRole.Admin:
|
|
||||||
return { permission: projectAdminPermissions };
|
|
||||||
case ProjectMembershipRole.Member:
|
|
||||||
return { permission: projectMemberPermissions };
|
|
||||||
case ProjectMembershipRole.NoAccess:
|
|
||||||
return { permission: projectNoAccessPermissions };
|
|
||||||
default:
|
|
||||||
throw new BadRequestError({ message: "Org role not found" });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -187,6 +192,8 @@ export const permissionServiceFactory = ({
|
|||||||
getUserProjectPermission,
|
getUserProjectPermission,
|
||||||
getProjectPermission,
|
getProjectPermission,
|
||||||
getOrgPermissionByRole,
|
getOrgPermissionByRole,
|
||||||
getProjectPermissionByRole
|
getProjectPermissionByRole,
|
||||||
|
buildOrgPermission,
|
||||||
|
buildProjectPermission
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ import { authPaswordServiceFactory } from "@app/services/auth/auth-password-serv
|
|||||||
import { authSignupServiceFactory } from "@app/services/auth/auth-signup-service";
|
import { authSignupServiceFactory } from "@app/services/auth/auth-signup-service";
|
||||||
import { tokenDalFactory } from "@app/services/auth-token/auth-token-dal";
|
import { tokenDalFactory } from "@app/services/auth-token/auth-token-dal";
|
||||||
import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
|
import { identityDalFactory } from "@app/services/identity/identity-dal";
|
||||||
|
import { identityOrgDalFactory } from "@app/services/identity/identity-org-dal";
|
||||||
|
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 { 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";
|
||||||
|
import { identityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
||||||
import { integrationDalFactory } from "@app/services/integration/integration-dal";
|
import { integrationDalFactory } from "@app/services/integration/integration-dal";
|
||||||
import { integrationServiceFactory } from "@app/services/integration/integration-service";
|
import { integrationServiceFactory } from "@app/services/integration/integration-service";
|
||||||
import { integrationAuthDalFactory } from "@app/services/integration-auth/integration-auth-dal";
|
import { integrationAuthDalFactory } from "@app/services/integration-auth/integration-auth-dal";
|
||||||
@@ -43,11 +53,16 @@ import { secretFolderServiceFactory } from "@app/services/secret-folder/secret-f
|
|||||||
import { secretImportDalFactory } from "@app/services/secret-import/secret-import-dal";
|
import { secretImportDalFactory } from "@app/services/secret-import/secret-import-dal";
|
||||||
import { secretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
import { secretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||||
import { secretTagDalFactory } from "@app/services/secret-tag/secret-tag-dal";
|
import { secretTagDalFactory } from "@app/services/secret-tag/secret-tag-dal";
|
||||||
|
import { secretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||||
|
import { serviceTokenDalFactory } from "@app/services/service-token/service-token-dal";
|
||||||
|
import { serviceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
||||||
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { superAdminDalFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDalFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { superAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
import { superAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
||||||
import { userDalFactory } from "@app/services/user/user-dal";
|
import { userDalFactory } from "@app/services/user/user-dal";
|
||||||
import { userServiceFactory } from "@app/services/user/user-service";
|
import { userServiceFactory } from "@app/services/user/user-service";
|
||||||
|
import { webhookDalFactory } from "@app/services/webhook/webhook-dal";
|
||||||
|
import { webhookServiceFactory } from "@app/services/webhook/webhook-service";
|
||||||
|
|
||||||
import { injectIdentity } from "../plugins/auth/inject-identity";
|
import { injectIdentity } from "../plugins/auth/inject-identity";
|
||||||
import { injectPermission } from "../plugins/auth/inject-permission";
|
import { injectPermission } from "../plugins/auth/inject-permission";
|
||||||
@@ -85,6 +100,16 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const integrationDal = integrationDalFactory(db);
|
const integrationDal = integrationDalFactory(db);
|
||||||
const integrationAuthDal = integrationAuthDalFactory(db);
|
const integrationAuthDal = integrationAuthDalFactory(db);
|
||||||
|
const webhookDal = webhookDalFactory(db);
|
||||||
|
const serviceTokenDal = serviceTokenDalFactory(db);
|
||||||
|
|
||||||
|
const identityDal = identityDalFactory(db);
|
||||||
|
const identityAccessTokenDal = identityAccessTokenDalFactory(db);
|
||||||
|
const identityOrgMembershipDal = identityOrgDalFactory(db);
|
||||||
|
const identityProjectDal = identityProjectDalFactory(db);
|
||||||
|
|
||||||
|
const identityUaDal = identityUaDalFactory(db);
|
||||||
|
const identityUaClientSecretDal = identityUaClientSecretDalFactory(db);
|
||||||
|
|
||||||
// ee db layer ops
|
// ee db layer ops
|
||||||
const permissionDal = permissionDalFactory(db);
|
const permissionDal = permissionDalFactory(db);
|
||||||
@@ -161,6 +186,7 @@ export const registerRoutes = async (
|
|||||||
secretDal,
|
secretDal,
|
||||||
secretTagDal
|
secretTagDal
|
||||||
});
|
});
|
||||||
|
const secretTagService = secretTagServiceFactory({ secretTagDal, permissionService });
|
||||||
const folderService = secretFolderServiceFactory({
|
const folderService = secretFolderServiceFactory({
|
||||||
permissionService,
|
permissionService,
|
||||||
folderDal,
|
folderDal,
|
||||||
@@ -186,6 +212,37 @@ export const registerRoutes = async (
|
|||||||
projectBotDal,
|
projectBotDal,
|
||||||
projectBotService
|
projectBotService
|
||||||
});
|
});
|
||||||
|
const webhookService = webhookServiceFactory({
|
||||||
|
permissionService,
|
||||||
|
webhookDal,
|
||||||
|
projectEnvDal
|
||||||
|
});
|
||||||
|
const serviceTokenService = serviceTokenServiceFactory({
|
||||||
|
projectEnvDal,
|
||||||
|
serviceTokenDal,
|
||||||
|
permissionService
|
||||||
|
});
|
||||||
|
|
||||||
|
const identityService = identityServiceFactory({
|
||||||
|
permissionService,
|
||||||
|
identityDal,
|
||||||
|
identityOrgMembershipDal
|
||||||
|
});
|
||||||
|
const identityAccessTokenService = identityAccessTokenServiceFactory({ identityAccessTokenDal });
|
||||||
|
const identityProjectService = identityProjectServiceFactory({
|
||||||
|
permissionService,
|
||||||
|
projectDal,
|
||||||
|
identityProjectDal,
|
||||||
|
identityOrgMembershipDal
|
||||||
|
});
|
||||||
|
const identityUaService = identityUaServiceFactory({
|
||||||
|
identityOrgMembershipDal,
|
||||||
|
permissionService,
|
||||||
|
identityDal,
|
||||||
|
identityAccessTokenDal,
|
||||||
|
identityUaClientSecretDal,
|
||||||
|
identityUaDal
|
||||||
|
});
|
||||||
|
|
||||||
await superAdminService.initServerCfg();
|
await superAdminService.initServerCfg();
|
||||||
// inject all services
|
// inject all services
|
||||||
@@ -206,11 +263,18 @@ export const registerRoutes = async (
|
|||||||
projectEnv: projectEnvService,
|
projectEnv: projectEnvService,
|
||||||
projectRole: projectRoleService,
|
projectRole: projectRoleService,
|
||||||
secret: secretService,
|
secret: secretService,
|
||||||
|
secretTag: secretTagService,
|
||||||
folder: folderService,
|
folder: folderService,
|
||||||
secretImport: secretImportService,
|
secretImport: secretImportService,
|
||||||
projectBot: projectBotService,
|
projectBot: projectBotService,
|
||||||
integration: integrationService,
|
integration: integrationService,
|
||||||
integrationAuth: integrationAuthService
|
integrationAuth: integrationAuthService,
|
||||||
|
webhook: webhookService,
|
||||||
|
serviceToken: serviceTokenService,
|
||||||
|
identity: identityService,
|
||||||
|
identityAccessToken: identityAccessTokenService,
|
||||||
|
identityProject: identityProjectService,
|
||||||
|
identityUa: identityUaService
|
||||||
});
|
});
|
||||||
|
|
||||||
server.decorate<FastifyZodProvider["store"]>("store", {
|
server.decorate<FastifyZodProvider["store"]>("store", {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { IntegrationAuthsSchema } from "@app/db/schemas";
|
||||||
|
|
||||||
|
// sometimes the return data must be santizied to avoid leaking important values
|
||||||
|
// always prefer pick over omit in zod
|
||||||
|
export const integrationAuthPubSchema = IntegrationAuthsSchema.pick({
|
||||||
|
projectId: true,
|
||||||
|
integration: true,
|
||||||
|
teamId: true,
|
||||||
|
url: true,
|
||||||
|
namespace: true,
|
||||||
|
accountId: true,
|
||||||
|
metadata: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true
|
||||||
|
});
|
||||||
@@ -10,17 +10,16 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim()
|
projectId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
bot: ProjectBotsSchema.pick({
|
bot: ProjectBotsSchema.omit({
|
||||||
name: true,
|
iv: true,
|
||||||
projectId: true,
|
encryptedPrivateKey: true,
|
||||||
isActive: true,
|
tag: true,
|
||||||
publicKey: true,
|
algorithm: true,
|
||||||
createdAt: true,
|
keyEncoding: true
|
||||||
updatedAt: true
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -30,7 +29,7 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
|
|||||||
const bot = await server.services.projectBot.findBotByProjectId({
|
const bot = await server.services.projectBot.findBotByProjectId({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
projectId: req.params.workspaceId
|
projectId: req.params.projectId
|
||||||
});
|
});
|
||||||
return { bot };
|
return { bot };
|
||||||
}
|
}
|
||||||
@@ -54,13 +53,12 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
bot: ProjectBotsSchema.pick({
|
bot: ProjectBotsSchema.omit({
|
||||||
name: true,
|
iv: true,
|
||||||
projectId: true,
|
encryptedPrivateKey: true,
|
||||||
isActive: true,
|
tag: true,
|
||||||
publicKey: true,
|
algorithm: true,
|
||||||
createdAt: true,
|
keyEncoding: true
|
||||||
updatedAt: true
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/token/renew",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
accessToken: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
accessToken: z.string(),
|
||||||
|
expiresIn: z.number(),
|
||||||
|
accessTokenMaxTTL: z.number(),
|
||||||
|
tokenType: z.literal("Bearer")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const { accessToken, identityAccessToken } =
|
||||||
|
await server.services.identityAccessToken.renewAccessToken({
|
||||||
|
accessToken: req.body.accessToken
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
accessToken,
|
||||||
|
tokenType: "Bearer" as const,
|
||||||
|
expiresIn: identityAccessToken.accessTokenTTL,
|
||||||
|
accessTokenMaxTTL: identityAccessToken.accessTokenMaxTTL
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim(),
|
||||||
|
organizationId: z.string().trim(),
|
||||||
|
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identity: IdentitiesSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identity = await server.services.identity.createIdentity({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
...req.body,
|
||||||
|
orgId: req.body.organizationId
|
||||||
|
});
|
||||||
|
return { identity };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:identityId",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim().optional(),
|
||||||
|
role: z.string().trim().min(1).optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identity: IdentitiesSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identity = await server.services.identity.updateIdentity({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
id: req.params.identityId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
return { identity };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/:identityId",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identity: IdentitiesSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identity = await server.services.identity.deleteIdentity({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
id: req.params.identityId
|
||||||
|
});
|
||||||
|
return { identity };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({
|
||||||
|
id: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
description: true,
|
||||||
|
clientSecretPrefix: true,
|
||||||
|
clientSecretNumUses: true,
|
||||||
|
clientSecretNumUsesLimit: true,
|
||||||
|
clientSecretTTL: true,
|
||||||
|
identityUAId: true,
|
||||||
|
isClientSecretRevoked: true
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/universal-auth/login",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
clientId: z.string().trim(),
|
||||||
|
clientSecret: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
accessToken: z.string(),
|
||||||
|
expiresIn: z.number(),
|
||||||
|
accessTokenMaxTTL: z.number(),
|
||||||
|
tokenType: z.literal("Bearer")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const { identityUa, accessToken } = await server.services.identityUa.login(
|
||||||
|
req.body.clientId,
|
||||||
|
req.body.clientSecret
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
accessToken,
|
||||||
|
tokenType: "Bearer" as const,
|
||||||
|
expiresIn: identityUa.accessTokenTTL,
|
||||||
|
accessTokenMaxTTL: identityUa.accessTokenMaxTTL
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/universal-auth/identities/:identityId",
|
||||||
|
method: "POST",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
clientSecretTrustedIps: z
|
||||||
|
.object({
|
||||||
|
ipAddress: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.min(1)
|
||||||
|
.default([{ ipAddress: "0.0.0.0/0" }]),
|
||||||
|
accessTokenTrustedIps: z
|
||||||
|
.object({
|
||||||
|
ipAddress: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.min(1)
|
||||||
|
.default([{ ipAddress: "0.0.0.0/0" }]),
|
||||||
|
accessTokenTTL: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.refine((value) => value !== 0, {
|
||||||
|
message: "accessTokenTTL must have a non zero number"
|
||||||
|
})
|
||||||
|
.default(2592000),
|
||||||
|
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)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityUniversalAuth: IdentityUniversalAuthsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityUniversalAuth = await server.services.identityUa.attachUa({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
...req.body,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
return { identityUniversalAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/universal-auth/identities/:identityId",
|
||||||
|
method: "PATCH",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
clientSecretTrustedIps: z
|
||||||
|
.object({
|
||||||
|
ipAddress: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.min(1)
|
||||||
|
.optional(),
|
||||||
|
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(),
|
||||||
|
accessTokenMaxTTL: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.refine((value) => value !== 0, {
|
||||||
|
message: "accessTokenMaxTTL must have a non zero number"
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityUniversalAuth: IdentityUniversalAuthsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityUniversalAuth = await server.services.identityUa.updateUa({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
...req.body,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
return { identityUniversalAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/universal-auth/identities/:identityId",
|
||||||
|
method: "GET",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityUniversalAuth: IdentityUniversalAuthsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityUniversalAuth = await server.services.identityUa.getIdentityUa({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
return { identityUniversalAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/universal-auth/identities/:identityId/client-secrets",
|
||||||
|
method: "POST",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
description: z.string().trim().default(""),
|
||||||
|
numUsesLimit: z.number().min(0).default(0),
|
||||||
|
ttl: z.number().min(0).default(0)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
clientSecret: z.string(),
|
||||||
|
clientSecretData: sanitizedClientSecretSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const { clientSecret, clientSecretData } =
|
||||||
|
await server.services.identityUa.createUaClientSecret({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
return { clientSecret, clientSecretData };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/universal-auth/identities/:identityId/client-secrets",
|
||||||
|
method: "GET",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
clientSecretData: sanitizedClientSecretSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const clientSecretData = await server.services.identityUa.getUaClientSecrets({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
return { clientSecretData };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
|
||||||
|
method: "POST",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string(),
|
||||||
|
clientSecretId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
clientSecretData: sanitizedClientSecretSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const clientSecretData = await server.services.identityUa.revokeUaClientSecret({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
clientSecretId: req.params.clientSecretId
|
||||||
|
});
|
||||||
|
return { clientSecretData };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import { registerAdminRouter } from "./admin-router";
|
import { registerAdminRouter } from "./admin-router";
|
||||||
import { registerAuthRoutes } from "./auth-router";
|
import { registerAuthRoutes } from "./auth-router";
|
||||||
import { registerProjectBotRouter } from "./bot-router";
|
import { registerProjectBotRouter } from "./bot-router";
|
||||||
|
import { registerIdentityAccessTokenRouter } from "./identity-access-token-router";
|
||||||
|
import { registerIdentityRouter } from "./identity-router";
|
||||||
|
import { registerIdentityUaRouter } from "./identity-ua";
|
||||||
import { registerIntegrationAuthRouter } from "./integration-auth-router";
|
import { registerIntegrationAuthRouter } from "./integration-auth-router";
|
||||||
import { registerIntegrationRouter } from "./integration-router";
|
import { registerIntegrationRouter } from "./integration-router";
|
||||||
import { registerInviteOrgRouter } from "./invite-org-router";
|
import { registerInviteOrgRouter } from "./invite-org-router";
|
||||||
@@ -12,13 +15,22 @@ import { registerProjectMembershipRouter } from "./project-membership-router";
|
|||||||
import { registerProjectRouter } from "./project-router";
|
import { registerProjectRouter } from "./project-router";
|
||||||
import { registerSecretFolderRouter } from "./secret-folder-router";
|
import { registerSecretFolderRouter } from "./secret-folder-router";
|
||||||
import { registerSecretImportRouter } from "./secret-import-router";
|
import { registerSecretImportRouter } from "./secret-import-router";
|
||||||
|
import { registerSecretTagRouter } from "./secret-tag-router";
|
||||||
import { registerSsoRouter } from "./sso-router";
|
import { registerSsoRouter } from "./sso-router";
|
||||||
import { registerUserActionRouter } from "./user-action-router";
|
import { registerUserActionRouter } from "./user-action-router";
|
||||||
import { registerUserRouter } from "./user-router";
|
import { registerUserRouter } from "./user-router";
|
||||||
|
import { registerWebhookRouter } from "./webhook-router";
|
||||||
|
|
||||||
export const registerV1Routes = async (server: FastifyZodProvider) => {
|
export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||||
await server.register(registerSsoRouter, { prefix: "/sso" });
|
await server.register(registerSsoRouter, { prefix: "/sso" });
|
||||||
await server.register(registerAuthRoutes, { prefix: "/auth" });
|
await server.register(
|
||||||
|
async (authServer) => {
|
||||||
|
await authServer.register(registerAuthRoutes);
|
||||||
|
await authServer.register(registerIdentityUaRouter);
|
||||||
|
await authServer.register(registerIdentityAccessTokenRouter);
|
||||||
|
},
|
||||||
|
{ prefix: "/auth" }
|
||||||
|
);
|
||||||
await server.register(registerPasswordRouter, { prefix: "/password" });
|
await server.register(registerPasswordRouter, { prefix: "/password" });
|
||||||
await server.register(registerOrgRouter, { prefix: "/organization" });
|
await server.register(registerOrgRouter, { prefix: "/organization" });
|
||||||
await server.register(registerAdminRouter, { prefix: "/admin" });
|
await server.register(registerAdminRouter, { prefix: "/admin" });
|
||||||
@@ -34,6 +46,7 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
|||||||
await projectServer.register(registerProjectEnvRouter);
|
await projectServer.register(registerProjectEnvRouter);
|
||||||
await projectServer.register(registerProjectKeyRouter);
|
await projectServer.register(registerProjectKeyRouter);
|
||||||
await projectServer.register(registerProjectMembershipRouter);
|
await projectServer.register(registerProjectMembershipRouter);
|
||||||
|
await projectServer.register(registerSecretTagRouter);
|
||||||
},
|
},
|
||||||
{ prefix: "/workspace" }
|
{ prefix: "/workspace" }
|
||||||
);
|
);
|
||||||
@@ -41,4 +54,6 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerProjectBotRouter, { prefix: "/bot" });
|
await server.register(registerProjectBotRouter, { prefix: "/bot" });
|
||||||
await server.register(registerIntegrationRouter, { prefix: "/integration" });
|
await server.register(registerIntegrationRouter, { prefix: "/integration" });
|
||||||
await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
|
await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
|
||||||
|
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
|
||||||
|
await server.register(registerIdentityRouter, { prefix: "/identities" });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IntegrationAuthsSchema } from "@app/db/schemas";
|
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { integrationAuthPubSchema } from "../sanitizedSchemas";
|
||||||
|
|
||||||
export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) => {
|
export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/integration-options",
|
url: "/integration-options",
|
||||||
@@ -32,18 +33,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const integrationPublicSchema = IntegrationAuthsSchema.pick({
|
|
||||||
projectId: true,
|
|
||||||
integration: true,
|
|
||||||
teamId: true,
|
|
||||||
url: true,
|
|
||||||
namespace: true,
|
|
||||||
accountId: true,
|
|
||||||
metadata: true,
|
|
||||||
createdAt: true,
|
|
||||||
updatedAt: true
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:integrationAuthId",
|
url: "/:integrationAuthId",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -54,7 +43,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
integrationAuth: integrationPublicSchema
|
integrationAuth: integrationAuthPubSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -78,7 +67,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
integrationAuth: integrationPublicSchema
|
integrationAuth: integrationAuthPubSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -108,7 +97,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
integrationAuth: integrationPublicSchema
|
integrationAuth: integrationAuthPubSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
IntegrationsSchema,
|
||||||
ProjectKeysSchema,
|
ProjectKeysSchema,
|
||||||
ProjectMembershipsSchema,
|
ProjectMembershipsSchema,
|
||||||
ProjectsSchema,
|
ProjectsSchema,
|
||||||
@@ -10,6 +11,9 @@ import {
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { integrationAuthPubSchema } from "../sanitizedSchemas";
|
||||||
|
import { sanitizedServiceTokenSchema } from "../v2/service-token-router";
|
||||||
|
|
||||||
const projectWithEnv = ProjectsSchema.merge(
|
const projectWithEnv = ProjectsSchema.merge(
|
||||||
z.object({
|
z.object({
|
||||||
environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array()
|
environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array()
|
||||||
@@ -264,4 +268,76 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
return { invitee, latestKey };
|
return { invitee, latestKey };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:workspaceId/integrations",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
integrations: IntegrationsSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const integrations = await server.services.integration.listIntegrationByProject({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
});
|
||||||
|
return { integrations };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:workspaceId/authorizations",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
authorizations: integrationAuthPubSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const authorizations = await server.services.integration.listIntegrationByProject({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
});
|
||||||
|
return { authorizations };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:workspaceId/service-token-data",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
serviceTokenData: sanitizedServiceTokenSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const serviceTokenData = await server.services.serviceToken.getProjectServiceTokens({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
});
|
||||||
|
return { serviceTokenData };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { SecretTagsSchema } from "@app/db/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/:projectId/tags",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
workspaceTags: SecretTagsSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const workspaceTags = await server.services.secretTag.getProjectTags({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
});
|
||||||
|
return { workspaceTags };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:projectId/tags",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim(),
|
||||||
|
slug: z.string().trim(),
|
||||||
|
color: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
workspaceTag: SecretTagsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const workspaceTag = await server.services.secretTag.createTag({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
projectId: req.params.projectId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
return { workspaceTag };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:projectId/tags/:tagId",
|
||||||
|
method: "DELETE",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string().trim(),
|
||||||
|
tagId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
workspaceTag: SecretTagsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const workspaceTag = await server.services.secretTag.deleteTag({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
id: req.params.tagId
|
||||||
|
});
|
||||||
|
return { workspaceTag };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { WebhooksSchema } from "@app/db/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const sanitizedWebhookSchema = WebhooksSchema.omit({
|
||||||
|
encryptedSecretKey: true,
|
||||||
|
iv: true,
|
||||||
|
tag: true,
|
||||||
|
algorithm: true,
|
||||||
|
keyEncoding: true,
|
||||||
|
}).merge(
|
||||||
|
z.object({
|
||||||
|
projectId:z.string(),
|
||||||
|
environment: z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
webhookUrl: z.string().url().trim(),
|
||||||
|
webhookSecretKey: z.string().trim().optional(),
|
||||||
|
secretPath: z.string().trim().default("/")
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
webhook: sanitizedWebhookSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const webhook = await server.services.webhook.createWebhook({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
projectId: req.body.workspaceId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
return { message: "Successfully created webhook", webhook };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:webhookId",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
webhookId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
isDisabled: z.boolean().default(false)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
webhook: sanitizedWebhookSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const webhook = await server.services.webhook.updateWebhook({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
id: req.params.webhookId,
|
||||||
|
isDisabled: req.body.isDisabled
|
||||||
|
});
|
||||||
|
return { message: "Successfully updated webhook", webhook };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/:webhookId",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
webhookId: z.string().trim()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const webhook = await server.services.webhook.deleteWebhook({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
id: req.params.webhookId
|
||||||
|
});
|
||||||
|
return { message: "Successfully deleted webhook", webhook };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/:webhookId/test",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
webhookId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
webhook: sanitizedWebhookSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const webhook = await server.services.webhook.testWebhook({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
id: req.params.webhookId
|
||||||
|
});
|
||||||
|
return { message: "Successfully tested webhook", webhook };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim().optional(),
|
||||||
|
secretPath: z.string().trim().optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
webhooks: sanitizedWebhookSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const webhooks = await server.services.webhook.listWebhooks({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
...req.query,
|
||||||
|
projectId: req.query.workspaceId
|
||||||
|
});
|
||||||
|
return { message: "Successfully fetched webhook", webhooks };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:orgId/identity-memberships",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
orgId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityMemberships: IdentityOrgMembershipsSchema.merge(
|
||||||
|
z.object({
|
||||||
|
customRole: OrgRolesSchema.pick({
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
permissions: true,
|
||||||
|
description: true
|
||||||
|
}).optional(),
|
||||||
|
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
||||||
|
})
|
||||||
|
).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityMemberships = await server.services.identity.listOrgIdentities({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
orgId: req.params.orgId
|
||||||
|
});
|
||||||
|
return { identityMemberships };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
IdentitiesSchema,
|
||||||
|
IdentityProjectMembershipsSchema,
|
||||||
|
ProjectMembershipRole,
|
||||||
|
ProjectRolesSchema
|
||||||
|
} from "@app/db/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerIdentityProjectRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/:projectId/identity-memberships/:identityId",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string().trim(),
|
||||||
|
identityId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
role: z.string().trim().min(1).default(ProjectMembershipRole.NoAccess)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityMembership: IdentityProjectMembershipsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityMembership = await server.services.identityProject.createProjectIdentity({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
projectId: req.params.projectId,
|
||||||
|
role: req.body.role
|
||||||
|
});
|
||||||
|
return { identityMembership };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:projectId/identity-memberships/:identityId",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string().trim(),
|
||||||
|
identityId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
role: z.string().trim().min(1).default(ProjectMembershipRole.NoAccess)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityMembership: IdentityProjectMembershipsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityMembership = await server.services.identityProject.updateProjectIdentity({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
projectId: req.params.projectId,
|
||||||
|
role: req.body.role
|
||||||
|
});
|
||||||
|
return { identityMembership };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/:projectId/identity-memberships/:identityId",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string().trim(),
|
||||||
|
identityId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityMembership: IdentityProjectMembershipsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityMembership = await server.services.identityProject.deleteProjectIdentity({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
});
|
||||||
|
return { identityMembership };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:projectId/identity-memberships",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityMemberships: IdentityProjectMembershipsSchema.merge(
|
||||||
|
z.object({
|
||||||
|
customRole: ProjectRolesSchema.pick({
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
permissions: true,
|
||||||
|
description: true
|
||||||
|
}).optional(),
|
||||||
|
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
|
||||||
|
})
|
||||||
|
).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityMemberships = await server.services.identityProject.listProjectIdentities({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
});
|
||||||
|
return { identityMemberships };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,13 +1,26 @@
|
|||||||
|
import { registerIdentityOrgRouter } from "./identity-org-router";
|
||||||
|
import { registerIdentityProjectRouter } from "./identity-project-router";
|
||||||
import { registerMfaRouter } from "./mfa-router";
|
import { registerMfaRouter } from "./mfa-router";
|
||||||
|
import { registerOrgRouter } from "./organization-router";
|
||||||
import { registerProjectRouter } from "./project-router";
|
import { registerProjectRouter } from "./project-router";
|
||||||
|
import { registerServiceTokenRouter } from "./service-token-router";
|
||||||
import { registerUserRouter } from "./user-router";
|
import { registerUserRouter } from "./user-router";
|
||||||
|
|
||||||
export const registerV2Routes = async (server: FastifyZodProvider) => {
|
export const registerV2Routes = async (server: FastifyZodProvider) => {
|
||||||
await server.register(registerMfaRouter, { prefix: "/auth" });
|
await server.register(registerMfaRouter, { prefix: "/auth" });
|
||||||
await server.register(registerUserRouter, { prefix: "/users" });
|
await server.register(registerUserRouter, { prefix: "/users" });
|
||||||
|
await server.register(registerServiceTokenRouter, { prefix: "/service-token" });
|
||||||
|
await server.register(
|
||||||
|
async (orgRouter) => {
|
||||||
|
await orgRouter.register(registerOrgRouter);
|
||||||
|
await orgRouter.register(registerIdentityOrgRouter);
|
||||||
|
},
|
||||||
|
{ prefix: "/organizations" }
|
||||||
|
);
|
||||||
await server.register(
|
await server.register(
|
||||||
async (projectServer) => {
|
async (projectServer) => {
|
||||||
projectServer.register(registerProjectRouter);
|
projectServer.register(registerProjectRouter);
|
||||||
|
projectServer.register(registerIdentityProjectRouter);
|
||||||
},
|
},
|
||||||
{ prefix: "/workspace" }
|
{ prefix: "/workspace" }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { ServiceTokensSchema } from "@app/db/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const sanitizedServiceTokenSchema = ServiceTokensSchema.omit({
|
||||||
|
secretHash: true,
|
||||||
|
encryptedKey: true,
|
||||||
|
iv: true,
|
||||||
|
tag: true
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registerServiceTokenRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "GET",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
serviceTokenId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: sanitizedServiceTokenSchema
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const serviceTokenData = await server.services.serviceToken.getServiceToken({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type
|
||||||
|
});
|
||||||
|
return serviceTokenData;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "POST",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim(),
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
scopes: z
|
||||||
|
.object({
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.min(1),
|
||||||
|
encryptedKey: z.string().trim(),
|
||||||
|
iv: z.string().trim(),
|
||||||
|
tag: z.string().trim(),
|
||||||
|
expiresIn: z.number(),
|
||||||
|
permissions: z.enum(["read", "write"]).array()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
serviceToken: z.string(),
|
||||||
|
serviceTokenData: sanitizedServiceTokenSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const { serviceToken, token } = await server.services.serviceToken.createServiceToken({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
...req.body,
|
||||||
|
projectId: req.body.workspaceId
|
||||||
|
});
|
||||||
|
return { serviceToken: token, serviceTokenData: serviceToken };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:serviceTokenId",
|
||||||
|
method: "DELETE",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
serviceTokenId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
serviceTokenData: sanitizedServiceTokenSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const serviceTokenData = await server.services.serviceToken.deleteServiceToken({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
id: req.params.serviceTokenId
|
||||||
|
});
|
||||||
|
return { serviceTokenData };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -25,7 +25,8 @@ export enum AuthMode {
|
|||||||
SERVICE_TOKEN = "serviceToken",
|
SERVICE_TOKEN = "serviceToken",
|
||||||
SERVICE_ACCESS_TOKEN = "serviceAccessToken",
|
SERVICE_ACCESS_TOKEN = "serviceAccessToken",
|
||||||
API_KEY = "apiKey",
|
API_KEY = "apiKey",
|
||||||
API_KEY_V2 = "apiKeyV2"
|
API_KEY_V2 = "apiKeyV2",
|
||||||
|
IDENTITY_ACCESS_TOKEN = "identityAccessToken"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ActorType { // would extend to AWS, Azure, ...
|
export enum ActorType { // would extend to AWS, Azure, ...
|
||||||
|
|||||||
@@ -82,11 +82,14 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await identityAccessTokenDal.updateById(identityAccessToken.id, {
|
const updatedIdentityAccessToken = await identityAccessTokenDal.updateById(
|
||||||
accessTokenLastRenewedAt: new Date()
|
identityAccessToken.id,
|
||||||
});
|
{
|
||||||
|
accessTokenLastRenewedAt: new Date()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return identityAccessTokenDal;
|
return { accessToken, identityAccessToken: updatedIdentityAccessToken };
|
||||||
};
|
};
|
||||||
|
|
||||||
return { renewAccessToken };
|
return { renewAccessToken };
|
||||||
|
|||||||
@@ -1,10 +1,74 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName } from "@app/db/schemas";
|
import { TableName } from "@app/db/schemas";
|
||||||
import { ormify } from "@app/lib/knex";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
export type TIdentityProjectDalFactory = ReturnType<typeof identityProjectDalFactory>;
|
export type TIdentityProjectDalFactory = ReturnType<typeof identityProjectDalFactory>;
|
||||||
|
|
||||||
export const identityProjectDalFactory = (db: TDbClient) => {
|
export const identityProjectDalFactory = (db: TDbClient) => {
|
||||||
const identityProjectOrm = ormify(db, TableName.IdentityProjectMembership);
|
const identityProjectOrm = ormify(db, TableName.IdentityProjectMembership);
|
||||||
return identityProjectOrm;
|
|
||||||
|
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db)(TableName.IdentityProjectMembership)
|
||||||
|
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||||
|
.join(
|
||||||
|
TableName.Identity,
|
||||||
|
`${TableName.IdentityProjectMembership}.identityId`,
|
||||||
|
`${TableName.Identity}.id`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.ProjectRoles,
|
||||||
|
`${TableName.IdentityProjectMembership}.roleId`,
|
||||||
|
`${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,
|
||||||
|
identityId,
|
||||||
|
identity: {
|
||||||
|
id: identityId,
|
||||||
|
name: identityName,
|
||||||
|
authMethod: identityAuthMethod
|
||||||
|
},
|
||||||
|
customRole: el.roleId
|
||||||
|
? {
|
||||||
|
id: crId,
|
||||||
|
name: crName,
|
||||||
|
slug: crSlug,
|
||||||
|
permissions: crPermission,
|
||||||
|
description: crDescription
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindByProjectId" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...identityProjectOrm, findByProjectId };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -138,9 +138,10 @@ export const identityProjectServiceFactory = ({
|
|||||||
const deleteProjectIdentity = async ({
|
const deleteProjectIdentity = async ({
|
||||||
identityId,
|
identityId,
|
||||||
actorId,
|
actorId,
|
||||||
actor
|
actor,
|
||||||
|
projectId
|
||||||
}: TDeleteProjectIdentityDTO) => {
|
}: TDeleteProjectIdentityDTO) => {
|
||||||
const identityProjectMembership = await identityProjectDal.findById(identityId);
|
const identityProjectMembership = await identityProjectDal.findOne({ identityId, projectId });
|
||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ export const identityProjectServiceFactory = ({
|
|||||||
ProjectPermissionSub.Identity
|
ProjectPermissionSub.Identity
|
||||||
);
|
);
|
||||||
|
|
||||||
const identityMemberhips = await identityProjectDal.find({ projectId });
|
const identityMemberhips = await identityProjectDal.findByProjectId(projectId);
|
||||||
return identityMemberhips;
|
return identityMemberhips;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ export type TUpdateProjectIdentityDTO = {
|
|||||||
|
|
||||||
export type TDeleteProjectIdentityDTO = {
|
export type TDeleteProjectIdentityDTO = {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TListProjectIdentityDTO = TProjectPermission;
|
export type TListProjectIdentityDTO = TProjectPermission;
|
||||||
|
|||||||
+2
-2
@@ -5,9 +5,9 @@ import { TableName } from "@app/db/schemas";
|
|||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify } from "@app/lib/knex";
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
export type TUaClientSecretDalFactory = ReturnType<typeof uaClientSecretDalFactory>;
|
export type TIdentityUaClientSecretDalFactory = ReturnType<typeof identityUaClientSecretDalFactory>;
|
||||||
|
|
||||||
export const uaClientSecretDalFactory = (db: TDbClient) => {
|
export const identityUaClientSecretDalFactory = (db: TDbClient) => {
|
||||||
const uaClientSecretOrm = ormify(db, TableName.IdentityUaClientSecret);
|
const uaClientSecretOrm = ormify(db, TableName.IdentityUaClientSecret);
|
||||||
|
|
||||||
const incrementUsage = async (id: string, tx?: Knex) => {
|
const incrementUsage = async (id: string, tx?: Knex) => {
|
||||||
+2
-2
@@ -2,9 +2,9 @@ import { TDbClient } from "@app/db";
|
|||||||
import { TableName } from "@app/db/schemas";
|
import { TableName } from "@app/db/schemas";
|
||||||
import { ormify } from "@app/lib/knex";
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
export type TUniversalAuthDalFactory = ReturnType<typeof universalAuthDalFactory>;
|
export type TIdentityUaDalFactory = ReturnType<typeof identityUaDalFactory>;
|
||||||
|
|
||||||
export const universalAuthDalFactory = (db: TDbClient) => {
|
export const identityUaDalFactory = (db: TDbClient) => {
|
||||||
const universalAuthOrm = ormify(db, TableName.IdentityUniversalAuth);
|
const universalAuthOrm = ormify(db, TableName.IdentityUniversalAuth);
|
||||||
|
|
||||||
return universalAuthOrm;
|
return universalAuthOrm;
|
||||||
+42
-36
@@ -19,8 +19,8 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
|||||||
import { TIdentityDalFactory } from "../identity/identity-dal";
|
import { TIdentityDalFactory } from "../identity/identity-dal";
|
||||||
import { TIdentityOrgDalFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDalFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDalFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDalFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TUaClientSecretDalFactory } from "./ua-client-secret-dal";
|
import { TIdentityUaClientSecretDalFactory } from "./identity-ua-client-secret-dal";
|
||||||
import { TUniversalAuthDalFactory } from "./universal-auth-dal";
|
import { TIdentityUaDalFactory } from "./identity-ua-dal";
|
||||||
import {
|
import {
|
||||||
TAttachUaDTO,
|
TAttachUaDTO,
|
||||||
TCreateUaClientSecretDTO,
|
TCreateUaClientSecretDTO,
|
||||||
@@ -28,33 +28,33 @@ import {
|
|||||||
TGetUaDTO,
|
TGetUaDTO,
|
||||||
TRevokeUaClientSecretDTO,
|
TRevokeUaClientSecretDTO,
|
||||||
TUpdateUaDTO
|
TUpdateUaDTO
|
||||||
} from "./universal-auth-types";
|
} from "./identity-ua-types";
|
||||||
|
|
||||||
type TUniversalAuthServiceFactoryDep = {
|
type TIdentityUaServiceFactoryDep = {
|
||||||
universalAuthDal: TUniversalAuthDalFactory;
|
identityUaDal: TIdentityUaDalFactory;
|
||||||
uaClientSecretDal: TUaClientSecretDalFactory;
|
identityUaClientSecretDal: TIdentityUaClientSecretDalFactory;
|
||||||
identityAccessTokenDal: TIdentityAccessTokenDalFactory;
|
identityAccessTokenDal: TIdentityAccessTokenDalFactory;
|
||||||
identityOrgDal: TIdentityOrgDalFactory;
|
identityOrgMembershipDal: TIdentityOrgDalFactory;
|
||||||
identityDal: Pick<TIdentityDalFactory, "updateById">;
|
identityDal: Pick<TIdentityDalFactory, "updateById">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUniversalAuthServiceFactory = ReturnType<typeof universalAuthServiceFactory>;
|
export type TIdentityUaServiceFactory = ReturnType<typeof identityUaServiceFactory>;
|
||||||
|
|
||||||
export const universalAuthServiceFactory = ({
|
export const identityUaServiceFactory = ({
|
||||||
universalAuthDal,
|
identityUaDal,
|
||||||
uaClientSecretDal,
|
identityUaClientSecretDal,
|
||||||
identityAccessTokenDal,
|
identityAccessTokenDal,
|
||||||
identityOrgDal,
|
identityOrgMembershipDal,
|
||||||
identityDal,
|
identityDal,
|
||||||
permissionService
|
permissionService
|
||||||
}: TUniversalAuthServiceFactoryDep) => {
|
}: TIdentityUaServiceFactoryDep) => {
|
||||||
const login = async (clientId: string, clientSecret: string) => {
|
const login = async (clientId: string, clientSecret: string) => {
|
||||||
const identityUa = await universalAuthDal.findOne({ clientId });
|
const identityUa = await identityUaDal.findOne({ clientId });
|
||||||
if (!identityUa) throw new UnauthorizedError();
|
if (!identityUa) throw new UnauthorizedError();
|
||||||
|
|
||||||
// TODO(akhilmhdh-pg): add ip checking
|
// TODO(akhilmhdh-pg): add ip checking
|
||||||
const clientSecrtInfo = await uaClientSecretDal.find({
|
const clientSecrtInfo = await identityUaClientSecretDal.find({
|
||||||
identityUAId: identityUa.id,
|
identityUAId: identityUa.id,
|
||||||
isClientSecretRevoked: false
|
isClientSecretRevoked: false
|
||||||
});
|
});
|
||||||
@@ -73,7 +73,7 @@ export const universalAuthServiceFactory = ({
|
|||||||
const expirationTime = new Date(clientSecretCreated.getTime() + ttlInMilliseconds);
|
const expirationTime = new Date(clientSecretCreated.getTime() + ttlInMilliseconds);
|
||||||
|
|
||||||
if (currentDate > expirationTime) {
|
if (currentDate > expirationTime) {
|
||||||
await uaClientSecretDal.updateById(validClientSecretInfo.id, {
|
await identityUaClientSecretDal.updateById(validClientSecretInfo.id, {
|
||||||
isClientSecretRevoked: true
|
isClientSecretRevoked: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -86,15 +86,17 @@ export const universalAuthServiceFactory = ({
|
|||||||
if (clientSecretNumUsesLimit > 0 && clientSecretNumUses === clientSecretNumUsesLimit) {
|
if (clientSecretNumUsesLimit > 0 && clientSecretNumUses === clientSecretNumUsesLimit) {
|
||||||
// number of times client secret can be used for
|
// number of times client secret can be used for
|
||||||
// a login operation reached
|
// a login operation reached
|
||||||
await uaClientSecretDal.updateById(validClientSecretInfo.id, { isClientSecretRevoked: true });
|
await identityUaClientSecretDal.updateById(validClientSecretInfo.id, {
|
||||||
|
isClientSecretRevoked: true
|
||||||
|
});
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message:
|
message:
|
||||||
"Failed to authenticate identity credentials due to client secret number of uses limit reached"
|
"Failed to authenticate identity credentials due to client secret number of uses limit reached"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const identityAccessToken = await universalAuthDal.transaction(async (tx) => {
|
const identityAccessToken = await identityUaDal.transaction(async (tx) => {
|
||||||
const uaClientSecretDoc = await uaClientSecretDal.incrementUsage(
|
const uaClientSecretDoc = await identityUaClientSecretDal.incrementUsage(
|
||||||
validClientSecretInfo.id,
|
validClientSecretInfo.id,
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -143,7 +145,7 @@ export const universalAuthServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor
|
actor
|
||||||
}: TAttachUaDTO) => {
|
}: TAttachUaDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgDal.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDal.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
||||||
if (identityMembershipOrg.identity.authMethod)
|
if (identityMembershipOrg.identity.authMethod)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -192,8 +194,8 @@ export const universalAuthServiceFactory = ({
|
|||||||
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityUa = await universalAuthDal.transaction(async (tx) => {
|
const identityUa = await identityUaDal.transaction(async (tx) => {
|
||||||
const doc = await universalAuthDal.create(
|
const doc = await identityUaDal.create(
|
||||||
{
|
{
|
||||||
identityId: identityMembershipOrg.identityId,
|
identityId: identityMembershipOrg.identityId,
|
||||||
clientId: crypto.randomUUID(),
|
clientId: crypto.randomUUID(),
|
||||||
@@ -227,14 +229,14 @@ export const universalAuthServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor
|
actor
|
||||||
}: TUpdateUaDTO) => {
|
}: TUpdateUaDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgDal.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDal.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
||||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to updated universal auth"
|
message: "Failed to updated universal auth"
|
||||||
});
|
});
|
||||||
|
|
||||||
const uaIdentityAuth = await universalAuthDal.findOne({ identityId });
|
const uaIdentityAuth = await identityUaDal.findOne({ identityId });
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) > 0 &&
|
(accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) > 0 &&
|
||||||
@@ -282,7 +284,7 @@ export const universalAuthServiceFactory = ({
|
|||||||
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||||
});
|
});
|
||||||
|
|
||||||
const updatedUaAuth = await universalAuthDal.updateById(uaIdentityAuth.id, {
|
const updatedUaAuth = await identityUaDal.updateById(uaIdentityAuth.id, {
|
||||||
clientSecretTrustedIps: reformattedClientSecretTrustedIps
|
clientSecretTrustedIps: reformattedClientSecretTrustedIps
|
||||||
? JSON.stringify(reformattedClientSecretTrustedIps)
|
? JSON.stringify(reformattedClientSecretTrustedIps)
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -297,14 +299,14 @@ export const universalAuthServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getIdentityUa = async ({ identityId, actorId, actor }: TGetUaDTO) => {
|
const getIdentityUa = async ({ identityId, actorId, actor }: TGetUaDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgDal.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDal.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
||||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "The identity does not have universal auth"
|
message: "The identity does not have universal auth"
|
||||||
});
|
});
|
||||||
|
|
||||||
const uaIdentityAuth = await universalAuthDal.findOne({ identityId });
|
const uaIdentityAuth = await identityUaDal.findOne({ identityId });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
@@ -326,7 +328,7 @@ export const universalAuthServiceFactory = ({
|
|||||||
description,
|
description,
|
||||||
numUsesLimit
|
numUsesLimit
|
||||||
}: TCreateUaClientSecretDTO) => {
|
}: TCreateUaClientSecretDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgDal.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDal.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
||||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -356,11 +358,11 @@ export const universalAuthServiceFactory = ({
|
|||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const clientSecret = crypto.randomBytes(32).toString("hex");
|
const clientSecret = crypto.randomBytes(32).toString("hex");
|
||||||
const clientSecretHash = await bcrypt.hash(clientSecret, appCfg.SALT_ROUNDS);
|
const clientSecretHash = await bcrypt.hash(clientSecret, appCfg.SALT_ROUNDS);
|
||||||
const identityUniversalAuth = await universalAuthDal.findOne({
|
const identityUniversalAuth = await identityUaDal.findOne({
|
||||||
identityId
|
identityId
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityUaClientSecret = await uaClientSecretDal.create({
|
const identityUaClientSecret = await identityUaClientSecretDal.create({
|
||||||
identityUAId: identityUniversalAuth.id,
|
identityUAId: identityUniversalAuth.id,
|
||||||
description,
|
description,
|
||||||
clientSecretPrefix: clientSecret.slice(0, 4),
|
clientSecretPrefix: clientSecret.slice(0, 4),
|
||||||
@@ -370,11 +372,15 @@ export const universalAuthServiceFactory = ({
|
|||||||
clientSecretTTL: ttl,
|
clientSecretTTL: ttl,
|
||||||
isClientSecretRevoked: false
|
isClientSecretRevoked: false
|
||||||
});
|
});
|
||||||
return { clientSecret: identityUaClientSecret, uaAuth: identityUniversalAuth };
|
return {
|
||||||
|
clientSecret,
|
||||||
|
clientSecretData: identityUaClientSecret,
|
||||||
|
uaAuth: identityUniversalAuth
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUaClientSecrets = async ({ actor, actorId, identityId }: TGetUaClientSecretsDTO) => {
|
const getUaClientSecrets = async ({ actor, actorId, identityId }: TGetUaClientSecretsDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgDal.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDal.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
||||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -401,11 +407,11 @@ export const universalAuthServiceFactory = ({
|
|||||||
message: "Failed to add identity to project with more privileged role"
|
message: "Failed to add identity to project with more privileged role"
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityUniversalAuth = await universalAuthDal.findOne({
|
const identityUniversalAuth = await identityUaDal.findOne({
|
||||||
identityId
|
identityId
|
||||||
});
|
});
|
||||||
|
|
||||||
const clientSecrets = await uaClientSecretDal.findOne({
|
const clientSecrets = await identityUaClientSecretDal.find({
|
||||||
identityUAId: identityUniversalAuth.id,
|
identityUAId: identityUniversalAuth.id,
|
||||||
isClientSecretRevoked: false
|
isClientSecretRevoked: false
|
||||||
});
|
});
|
||||||
@@ -418,7 +424,7 @@ export const universalAuthServiceFactory = ({
|
|||||||
actor,
|
actor,
|
||||||
clientSecretId
|
clientSecretId
|
||||||
}: TRevokeUaClientSecretDTO) => {
|
}: TRevokeUaClientSecretDTO) => {
|
||||||
const identityMembershipOrg = await identityOrgDal.findOne({ identityId });
|
const identityMembershipOrg = await identityOrgMembershipDal.findOne({ identityId });
|
||||||
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
||||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -445,7 +451,7 @@ export const universalAuthServiceFactory = ({
|
|||||||
message: "Failed to add identity to project with more privileged role"
|
message: "Failed to add identity to project with more privileged role"
|
||||||
});
|
});
|
||||||
|
|
||||||
const clientSecret = await uaClientSecretDal.updateById(clientSecretId, {
|
const clientSecret = await identityUaClientSecretDal.updateById(clientSecretId, {
|
||||||
isClientSecretRevoked: true
|
isClientSecretRevoked: true
|
||||||
});
|
});
|
||||||
return clientSecret;
|
return clientSecret;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName,TIdentityOrgMemberships } from "@app/db/schemas";
|
import { TableName, TIdentityOrgMemberships } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
@@ -31,5 +31,65 @@ export const identityOrgDalFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...identityOrgOrm, findOne };
|
const findByOrgId = async (orgId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db)(TableName.IdentityOrgMembership)
|
||||||
|
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId)
|
||||||
|
.join(
|
||||||
|
TableName.Identity,
|
||||||
|
`${TableName.IdentityOrgMembership}.identityId`,
|
||||||
|
`${TableName.Identity}.id`
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.OrgRoles,
|
||||||
|
`${TableName.IdentityOrgMembership}.roleId`,
|
||||||
|
`${TableName.OrgRoles}.id`
|
||||||
|
)
|
||||||
|
.select(selectAllTableCols(TableName.IdentityOrgMembership))
|
||||||
|
// cr stands for custom role
|
||||||
|
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
||||||
|
.select(db.ref("name").as("crName").withSchema(TableName.OrgRoles))
|
||||||
|
.select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles))
|
||||||
|
.select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles))
|
||||||
|
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
|
||||||
|
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles))
|
||||||
|
.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,
|
||||||
|
identityId,
|
||||||
|
identity: {
|
||||||
|
id: identityId,
|
||||||
|
name: identityName,
|
||||||
|
authMethod: identityAuthMethod
|
||||||
|
},
|
||||||
|
customRole: el.roleId
|
||||||
|
? {
|
||||||
|
id: crId,
|
||||||
|
name: crName,
|
||||||
|
slug: crSlug,
|
||||||
|
permissions: crPermission,
|
||||||
|
description: crDescription
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindByOrgId" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...identityOrgOrm, findOne, findByOrgId };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||||
|
import { TOrgPermission } from "@app/lib/types";
|
||||||
|
|
||||||
import { ActorType } from "../auth/auth-type";
|
import { ActorType } from "../auth/auth-type";
|
||||||
import { TIdentityDalFactory } from "./identity-dal";
|
import { TIdentityDalFactory } from "./identity-dal";
|
||||||
@@ -16,7 +17,7 @@ import { TCreateIdentityDTO, TDeleteIdentityDTO, TUpdateIdentityDTO } from "./id
|
|||||||
|
|
||||||
type TIdentityServiceFactoryDep = {
|
type TIdentityServiceFactoryDep = {
|
||||||
identityDal: TIdentityDalFactory;
|
identityDal: TIdentityDalFactory;
|
||||||
identityOrgDal: TIdentityOrgDalFactory;
|
identityOrgMembershipDal: TIdentityOrgDalFactory;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ export type TIdentityServiceFactory = ReturnType<typeof identityServiceFactory>;
|
|||||||
|
|
||||||
export const identityServiceFactory = ({
|
export const identityServiceFactory = ({
|
||||||
identityDal,
|
identityDal,
|
||||||
identityOrgDal,
|
identityOrgMembershipDal,
|
||||||
permissionService
|
permissionService
|
||||||
}: TIdentityServiceFactoryDep) => {
|
}: TIdentityServiceFactoryDep) => {
|
||||||
const createIdentity = async ({ name, role, actor, orgId, actorId }: TCreateIdentityDTO) => {
|
const createIdentity = async ({ name, role, actor, orgId, actorId }: TCreateIdentityDTO) => {
|
||||||
@@ -43,7 +44,7 @@ export const identityServiceFactory = ({
|
|||||||
|
|
||||||
const identity = await identityDal.transaction(async (tx) => {
|
const identity = await identityDal.transaction(async (tx) => {
|
||||||
const newIdentity = await identityDal.create({ name }, tx);
|
const newIdentity = await identityDal.create({ name }, tx);
|
||||||
await identityOrgDal.create(
|
await identityOrgMembershipDal.create(
|
||||||
{
|
{
|
||||||
identityId: newIdentity.id,
|
identityId: newIdentity.id,
|
||||||
orgId,
|
orgId,
|
||||||
@@ -60,7 +61,7 @@ export const identityServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateIdentity = async ({ id, role, name, actor, actorId }: TUpdateIdentityDTO) => {
|
const updateIdentity = async ({ id, role, name, actor, actorId }: TUpdateIdentityDTO) => {
|
||||||
const identityOrgMembership = await identityOrgDal.findById(id);
|
const identityOrgMembership = await identityOrgMembershipDal.findById(id);
|
||||||
if (!identityOrgMembership)
|
if (!identityOrgMembership)
|
||||||
throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ export const identityServiceFactory = ({
|
|||||||
const identity = await identityDal.transaction(async (tx) => {
|
const identity = await identityDal.transaction(async (tx) => {
|
||||||
const newIdentity = await identityDal.updateById(id, { name }, tx);
|
const newIdentity = await identityDal.updateById(id, { name }, tx);
|
||||||
if (role) {
|
if (role) {
|
||||||
await identityOrgDal.update(
|
await identityOrgMembershipDal.update(
|
||||||
{ identityId: id },
|
{ identityId: id },
|
||||||
{
|
{
|
||||||
role: customRole ? OrgMembershipRole.Custom : role,
|
role: customRole ? OrgMembershipRole.Custom : role,
|
||||||
@@ -115,7 +116,7 @@ export const identityServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => {
|
const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => {
|
||||||
const identityOrgMembership = await identityOrgDal.findById(id);
|
const identityOrgMembership = await identityOrgMembershipDal.findById(id);
|
||||||
if (!identityOrgMembership)
|
if (!identityOrgMembership)
|
||||||
throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
||||||
|
|
||||||
@@ -141,9 +142,21 @@ export const identityServiceFactory = ({
|
|||||||
return deletedIdentity;
|
return deletedIdentity;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const listOrgIdentities = async ({ orgId, actor, actorId }: TOrgPermission) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionActions.Read,
|
||||||
|
OrgPermissionSubjects.Identity
|
||||||
|
);
|
||||||
|
|
||||||
|
const identityMemberhips = await identityOrgMembershipDal.findByOrgId(orgId);
|
||||||
|
return identityMemberhips;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createIdentity,
|
createIdentity,
|
||||||
updateIdentity,
|
updateIdentity,
|
||||||
deleteIdentity
|
deleteIdentity,
|
||||||
|
listOrgIdentities
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ export type TCreateIdentityDTO = {
|
|||||||
|
|
||||||
export type TUpdateIdentityDTO = {
|
export type TUpdateIdentityDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
role: string;
|
role?: string;
|
||||||
name: string;
|
name?: string;
|
||||||
} & Omit<TOrgPermission, "orgId">;
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
export type TDeleteIdentityDTO = {
|
export type TDeleteIdentityDTO = {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
encryptSymmetric128BitHexKeyUTF8
|
encryptSymmetric128BitHexKeyUTF8
|
||||||
} from "@app/lib/crypto";
|
} from "@app/lib/crypto";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
import { TIntegrationDalFactory } from "../integration/integration-dal";
|
import { TIntegrationDalFactory } from "../integration/integration-dal";
|
||||||
import { TProjectBotDalFactory } from "../project-bot/project-bot-dal";
|
import { TProjectBotDalFactory } from "../project-bot/project-bot-dal";
|
||||||
@@ -47,7 +48,7 @@ import {
|
|||||||
TTeamCityBuildConfig,
|
TTeamCityBuildConfig,
|
||||||
TVercelBranches
|
TVercelBranches
|
||||||
} from "./integration-auth-types";
|
} from "./integration-auth-types";
|
||||||
import { getIntegrationOptions,Integrations, IntegrationUrls } from "./integration-list";
|
import { getIntegrationOptions, Integrations, IntegrationUrls } from "./integration-list";
|
||||||
import { getTeams } from "./integration-team";
|
import { getTeams } from "./integration-team";
|
||||||
import { exchangeCode, exchangeRefresh } from "./integration-token";
|
import { exchangeCode, exchangeRefresh } from "./integration-token";
|
||||||
|
|
||||||
@@ -68,6 +69,20 @@ export const integrationAuthServiceFactory = ({
|
|||||||
projectBotDal,
|
projectBotDal,
|
||||||
projectBotService
|
projectBotService
|
||||||
}: TIntegrationAuthServiceFactoryDep) => {
|
}: TIntegrationAuthServiceFactoryDep) => {
|
||||||
|
const listIntegrationAuthByProjectId = async ({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
projectId
|
||||||
|
}: TProjectPermission) => {
|
||||||
|
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
const authorizations = await integrationAuthDal.find({ projectId });
|
||||||
|
return authorizations;
|
||||||
|
};
|
||||||
|
|
||||||
const getIntegrationAuth = async ({ actor, id, actorId }: TGetIntegrationAuthDTO) => {
|
const getIntegrationAuth = async ({ actor, id, actorId }: TGetIntegrationAuthDTO) => {
|
||||||
const integrationAuth = await integrationAuthDal.findById(id);
|
const integrationAuth = await integrationAuthDal.findById(id);
|
||||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||||
@@ -983,6 +998,7 @@ export const integrationAuthServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
listIntegrationAuthByProjectId,
|
||||||
getIntegrationOptions,
|
getIntegrationOptions,
|
||||||
getIntegrationAuth,
|
getIntegrationAuth,
|
||||||
oauthExchange,
|
oauthExchange,
|
||||||
|
|||||||
@@ -60,5 +60,34 @@ export const integrationDalFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...integrationOrm, find, findOne, findById };
|
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const integrations = await (tx || db)(TableName.Integration)
|
||||||
|
.where(`${TableName.Environment}.projectId`, projectId)
|
||||||
|
.join(
|
||||||
|
TableName.Environment,
|
||||||
|
`${TableName.Integration}.envId`,
|
||||||
|
`${TableName.Environment}.id`
|
||||||
|
)
|
||||||
|
.select(db.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
|
.select(db.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
|
.select(db.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||||
|
.select(db.ref("projectId").withSchema(TableName.Environment))
|
||||||
|
.select(selectAllTableCols(TableName.Integration));
|
||||||
|
|
||||||
|
return integrations.map(({ envId, envSlug, envName, ...el }) => ({
|
||||||
|
...el,
|
||||||
|
envId,
|
||||||
|
environment: {
|
||||||
|
id: envId,
|
||||||
|
slug: envSlug,
|
||||||
|
name: envName
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindByProjectId" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...integrationOrm, find, findOne, findById, findByProjectId };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
} from "@app/ee/services/permission/project-permission";
|
} from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
import { TIntegrationAuthDalFactory } from "../integration-auth/integration-auth-dal";
|
import { TIntegrationAuthDalFactory } from "../integration-auth/integration-auth-dal";
|
||||||
import { TSecretFolderDalFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDalFactory } from "../secret-folder/secret-folder-dal";
|
||||||
@@ -152,9 +153,21 @@ export const integrationServiceFactory = ({
|
|||||||
return deletedIntegration;
|
return deletedIntegration;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const listIntegrationByProject = async ({ actor, actorId, projectId }: TProjectPermission) => {
|
||||||
|
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
|
const integrations = await integrationDal.findByProjectId(projectId);
|
||||||
|
return integrations;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createIntegration,
|
createIntegration,
|
||||||
updateIntegration,
|
updateIntegration,
|
||||||
deleteIntegration
|
deleteIntegration,
|
||||||
|
listIntegrationByProject
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName,TProjectBots } from "@app/db/schemas";
|
import { TableName, TProjectBots } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
@@ -14,8 +14,8 @@ export const projectBotDalFactory = (db: TDbClient) => {
|
|||||||
try {
|
try {
|
||||||
const bot = await (tx || db)(TableName.ProjectBot)
|
const bot = await (tx || db)(TableName.ProjectBot)
|
||||||
.where(filter)
|
.where(filter)
|
||||||
.join(TableName.Users, `${TableName.ProjectBot}.senderId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.ProjectBot}.senderId`, `${TableName.Users}.id`)
|
||||||
.join(
|
.leftJoin(
|
||||||
TableName.UserEncryptionKey,
|
TableName.UserEncryptionKey,
|
||||||
`${TableName.UserEncryptionKey}.userId`,
|
`${TableName.UserEncryptionKey}.userId`,
|
||||||
`${TableName.Users}.id`
|
`${TableName.Users}.id`
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export const projectBotServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
isActive
|
isActive
|
||||||
}: TSetActiveStateDTO) => {
|
}: TSetActiveStateDTO) => {
|
||||||
const bot = await projectBotDal.findOne({ id: botId });
|
const bot = await projectBotDal.findById(botId);
|
||||||
if (!bot) throw new BadRequestError({ message: "Bot not found" });
|
if (!bot) throw new BadRequestError({ message: "Bot not found" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
@@ -153,12 +153,12 @@ export const projectBotServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
if (!botKey?.nonce || !botKey?.encryptionKey) {
|
if (!botKey?.nonce || !botKey?.encryptedKey) {
|
||||||
throw new BadRequestError({ message: "Failed to set bot active - missing bot key" });
|
throw new BadRequestError({ message: "Failed to set bot active - missing bot key" });
|
||||||
}
|
}
|
||||||
const doc = await projectBotDal.updateById(botId, {
|
const doc = await projectBotDal.updateById(botId, {
|
||||||
isActive: true,
|
isActive: true,
|
||||||
encryptedProjectKey: botKey.encryptionKey,
|
encryptedProjectKey: botKey.encryptedKey,
|
||||||
encryptedProjectKeyNonce: botKey.nonce,
|
encryptedProjectKeyNonce: botKey.nonce,
|
||||||
senderId: actorId
|
senderId: actorId
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export type TSetActiveStateDTO = {
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
botKey?: {
|
botKey?: {
|
||||||
nonce?: string;
|
nonce?: string;
|
||||||
encryptionKey?: string;
|
encryptedKey?: string;
|
||||||
};
|
};
|
||||||
botId: string;
|
botId: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|||||||
@@ -76,12 +76,12 @@ export const secretDalFactory = (db: TDbClient) => {
|
|||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
bd.whereNull("userId").orWhere({ userId: userId || null });
|
bd.whereNull("userId").orWhere({ userId: userId || null });
|
||||||
})
|
})
|
||||||
.join(
|
.leftJoin(
|
||||||
TableName.JnSecretTag,
|
TableName.JnSecretTag,
|
||||||
`${TableName.Secret}.id`,
|
`${TableName.Secret}.id`,
|
||||||
`${TableName.JnSecretTag}.${TableName.Secret}Id`
|
`${TableName.JnSecretTag}.${TableName.Secret}Id`
|
||||||
)
|
)
|
||||||
.join(
|
.leftJoin(
|
||||||
TableName.SecretTag,
|
TableName.SecretTag,
|
||||||
`${TableName.JnSecretTag}.${TableName.SecretTag}Id`,
|
`${TableName.JnSecretTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import {
|
|||||||
SecretType,
|
SecretType,
|
||||||
TableName,
|
TableName,
|
||||||
TSecretBlindIndexes,
|
TSecretBlindIndexes,
|
||||||
TSecrets} from "@app/db/schemas";
|
TSecrets
|
||||||
|
} from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
@@ -156,10 +157,12 @@ export const secretServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await secretTagDal.saveTagsToSecret(
|
if (tags.length) {
|
||||||
tags.map(({ id }) => ({ secretsId: doc.id, secret_tagsId: id })),
|
await secretTagDal.saveTagsToSecret(
|
||||||
tx
|
tags.map(({ id }) => ({ secretsId: doc.id, secret_tagsId: id })),
|
||||||
);
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
await secretVersionDal.create(
|
await secretVersionDal.create(
|
||||||
{
|
{
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export type TCreateServiceTokenDTO = {
|
|||||||
iv: string;
|
iv: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
expiresIn?: number | null;
|
expiresIn?: number | null;
|
||||||
permissions: ["read" | "write"];
|
permissions: ("read" | "write")[];
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TGetServiceTokenInfoDTO = Omit<TProjectPermission, "projectId">;
|
export type TGetServiceTokenInfoDTO = Omit<TProjectPermission, "projectId">;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName,TWebhooks } from "@app/db/schemas";
|
import { TableName, TWebhooks } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
@@ -18,13 +18,14 @@ export const webhookDalFactory = (db: TDbClient) => {
|
|||||||
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||||
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
||||||
.select(selectAllTableCols(TableName.Integration));
|
.select(selectAllTableCols(TableName.Webhook));
|
||||||
|
|
||||||
const find = async (filter: Partial<TWebhooks>, tx?: Knex) => {
|
const find = async (filter: Partial<TWebhooks>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await webhookFindQuery(tx || db, filter);
|
const docs = await webhookFindQuery(tx || db, filter);
|
||||||
return docs.map(({ envId, envSlug, envName, ...el }) => ({
|
return docs.map(({ envId, envSlug, envName, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
|
envId,
|
||||||
environment: {
|
environment: {
|
||||||
id: envId,
|
id: envId,
|
||||||
slug: envSlug,
|
slug: envSlug,
|
||||||
@@ -50,11 +51,13 @@ export const webhookDalFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await webhookFindQuery(tx || db, { id }).first();
|
const doc = await webhookFindQuery(tx || db, {
|
||||||
|
[`${TableName.Webhook}.id` as "id"]: id
|
||||||
|
}).first();
|
||||||
if (!doc) return;
|
if (!doc) return;
|
||||||
|
|
||||||
const { envName: name, envSlug: slug, envId, ...el } = doc;
|
const { envName: name, envSlug: slug, envId, ...el } = doc;
|
||||||
return { ...el, environment: { id: envId, name, slug } };
|
return { ...el, envId, environment: { id: envId, name, slug } };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Find by id webhook" });
|
throw new DatabaseError({ error, name: "Find by id webhook" });
|
||||||
}
|
}
|
||||||
@@ -82,9 +85,17 @@ export const webhookDalFactory = (db: TDbClient) => {
|
|||||||
.select(db.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
.select(db.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
.select(db.ref("id").withSchema(TableName.Environment).as("envId"))
|
.select(db.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||||
.select(db.ref("projectId").withSchema(TableName.Environment))
|
.select(db.ref("projectId").withSchema(TableName.Environment))
|
||||||
.select(selectAllTableCols(TableName.Integration));
|
.select(selectAllTableCols(TableName.Webhook));
|
||||||
|
|
||||||
return webhooks;
|
return webhooks.map(({ envId, envSlug, envName, ...el }) => ({
|
||||||
|
...el,
|
||||||
|
envId,
|
||||||
|
environment: {
|
||||||
|
id: envId,
|
||||||
|
slug: envSlug,
|
||||||
|
name: envName
|
||||||
|
}
|
||||||
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Find all webhooks" });
|
throw new DatabaseError({ error, name: "Find all webhooks" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export const webhookServiceFactory = ({
|
|||||||
|
|
||||||
const webhook = await webhookDal.create(insertDoc);
|
const webhook = await webhookDal.create(insertDoc);
|
||||||
// TODO(akhilmhdh-pg): add audit log
|
// TODO(akhilmhdh-pg): add audit log
|
||||||
return webhook;
|
return { ...webhook,projectId, environment: env };
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateWebhook = async ({ actorId, actor, id, isDisabled }: TUpdateWebhookDTO) => {
|
const updateWebhook = async ({ actorId, actor, id, isDisabled }: TUpdateWebhookDTO) => {
|
||||||
@@ -101,7 +101,7 @@ export const webhookServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const updatedWebhook = await webhookDal.updateById(id, { isDisabled });
|
const updatedWebhook = await webhookDal.updateById(id, { isDisabled });
|
||||||
return updatedWebhook;
|
return { ...webhook,...updatedWebhook };
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteWebhook = async ({ id, actor, actorId }: TDeleteWebhookDTO) => {
|
const deleteWebhook = async ({ id, actor, actorId }: TDeleteWebhookDTO) => {
|
||||||
@@ -119,7 +119,7 @@ export const webhookServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const deletedWebhook = await webhookDal.deleteById(id);
|
const deletedWebhook = await webhookDal.deleteById(id);
|
||||||
return deletedWebhook;
|
return { ...webhook,...deletedWebhook };
|
||||||
};
|
};
|
||||||
|
|
||||||
const testWebhook = async ({ id, actor, actorId }: TTestWebhookDTO) => {
|
const testWebhook = async ({ id, actor, actorId }: TTestWebhookDTO) => {
|
||||||
@@ -150,7 +150,7 @@ export const webhookServiceFactory = ({
|
|||||||
lastStatus: isSuccess ? "success" : "failed",
|
lastStatus: isSuccess ? "success" : "failed",
|
||||||
lastRunErrorMessage: isSuccess ? null : webhookError
|
lastRunErrorMessage: isSuccess ? null : webhookError
|
||||||
});
|
});
|
||||||
return updatedWebhook;
|
return {...webhook,...updatedWebhook}
|
||||||
};
|
};
|
||||||
|
|
||||||
const listWebhooks = async ({
|
const listWebhooks = async ({
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const AddTagPopoverContent = ({
|
|||||||
<div className="ml-7 flex items-center gap-3">
|
<div className="ml-7 flex items-center gap-3">
|
||||||
<div
|
<div
|
||||||
className="h-[10px] w-[10px] rounded-full"
|
className="h-[10px] w-[10px] rounded-full"
|
||||||
style={{ background: wsTag?.tagColor ? wsTag.tagColor : "#bec2c8" }}
|
style={{ background: wsTag?.color ? wsTag.color : "#bec2c8" }}
|
||||||
>
|
>
|
||||||
{" "}
|
{" "}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export enum IdentityAuthMethod {
|
export enum IdentityAuthMethod {
|
||||||
UNIVERSAL_AUTH = "universal-auth"
|
UNIVERSAL_AUTH = "universal"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export type IntegrationAuth = {
|
export type IntegrationAuth = {
|
||||||
id: string;
|
id: string;
|
||||||
integration: string;
|
integration: string;
|
||||||
workspace: string;
|
projectId: string;
|
||||||
__v: number;
|
__v: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ export type TCloudIntegration = {
|
|||||||
|
|
||||||
export type TIntegration = {
|
export type TIntegration = {
|
||||||
id: string;
|
id: string;
|
||||||
workspace: string;
|
projectId: string;
|
||||||
environment: string;
|
envId: string;
|
||||||
|
environment: { slug: string; name: string; id: string };
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
url: any;
|
url: any;
|
||||||
app: string;
|
app: string;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const serviceTokenKeys = {
|
|||||||
|
|
||||||
const fetchWorkspaceServiceTokens = async (workspaceID: string) => {
|
const fetchWorkspaceServiceTokens = async (workspaceID: string) => {
|
||||||
const { data } = await apiRequest.get<{ serviceTokenData: ServiceToken[] }>(
|
const { data } = await apiRequest.get<{ serviceTokenData: ServiceToken[] }>(
|
||||||
`/api/v2/workspace/${workspaceID}/service-token-data`
|
`/api/v1/workspace/${workspaceID}/service-token-data`
|
||||||
);
|
);
|
||||||
|
|
||||||
return data.serviceTokenData;
|
return data.serviceTokenData;
|
||||||
@@ -29,10 +29,11 @@ export const useGetUserWsServiceTokens = ({ workspaceID }: UseGetWorkspaceServic
|
|||||||
queryFn: () => fetchWorkspaceServiceTokens(workspaceID),
|
queryFn: () => fetchWorkspaceServiceTokens(workspaceID),
|
||||||
enabled: Boolean(workspaceID)
|
enabled: Boolean(workspaceID)
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// mutation
|
// mutation
|
||||||
export const useCreateServiceToken = () => { // TODO: deprecate
|
export const useCreateServiceToken = () => {
|
||||||
|
// TODO: deprecate
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<CreateServiceTokenRes, {}, CreateServiceTokenDTO>({
|
return useMutation<CreateServiceTokenRes, {}, CreateServiceTokenDTO>({
|
||||||
@@ -41,8 +42,8 @@ export const useCreateServiceToken = () => { // TODO: deprecate
|
|||||||
data.serviceToken += `.${body.randomBytes}`;
|
data.serviceToken += `.${body.randomBytes}`;
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: ({ serviceTokenData: { workspace } }) => {
|
onSuccess: ({ serviceTokenData: { projectId } }) => {
|
||||||
queryClient.invalidateQueries(serviceTokenKeys.getAllWorkspaceServiceToken(workspace));
|
queryClient.invalidateQueries(serviceTokenKeys.getAllWorkspaceServiceToken(projectId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -55,8 +56,8 @@ export const useDeleteServiceToken = () => {
|
|||||||
const { data } = await apiRequest.delete(`/api/v2/service-token/${serviceTokenId}`);
|
const { data } = await apiRequest.delete(`/api/v2/service-token/${serviceTokenId}`);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: ({ serviceTokenData: { workspace } }) => {
|
onSuccess: ({ serviceTokenData: { projectId } }) => {
|
||||||
queryClient.invalidateQueries(serviceTokenKeys.getAllWorkspaceServiceToken(workspace));
|
queryClient.invalidateQueries(serviceTokenKeys.getAllWorkspaceServiceToken(projectId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export type ServiceTokenScope = {
|
|||||||
export type ServiceToken = {
|
export type ServiceToken = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
workspace: string;
|
projectId: string;
|
||||||
scopes: ServiceTokenScope[];
|
scopes: ServiceTokenScope[];
|
||||||
user: string;
|
user: string;
|
||||||
expiresAt: string;
|
expiresAt: string;
|
||||||
|
|||||||
@@ -2,13 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import {
|
import { CreateTagDTO, DeleteTagDTO, UserWsTags, WsTag } from "./types";
|
||||||
CreateTagDTO,
|
|
||||||
CreateTagRes,
|
|
||||||
DeleteTagDTO,
|
|
||||||
DeleteWsTagRes,
|
|
||||||
UserWsTags,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
const workspaceTags = {
|
const workspaceTags = {
|
||||||
getWsTags: (workspaceID: string) => ["workspace-tags", { workspaceID }] as const
|
getWsTags: (workspaceID: string) => ["workspace-tags", { workspaceID }] as const
|
||||||
@@ -16,7 +10,7 @@ const workspaceTags = {
|
|||||||
|
|
||||||
const fetchWsTag = async (workspaceID: string) => {
|
const fetchWsTag = async (workspaceID: string) => {
|
||||||
const { data } = await apiRequest.get<{ workspaceTags: UserWsTags }>(
|
const { data } = await apiRequest.get<{ workspaceTags: UserWsTags }>(
|
||||||
`/api/v2/workspace/${workspaceID}/tags`
|
`/api/v1/workspace/${workspaceID}/tags`
|
||||||
);
|
);
|
||||||
|
|
||||||
return data.workspaceTags;
|
return data.workspaceTags;
|
||||||
@@ -28,38 +22,41 @@ export const useGetWsTags = (workspaceID: string) => {
|
|||||||
queryFn: () => fetchWsTag(workspaceID),
|
queryFn: () => fetchWsTag(workspaceID),
|
||||||
enabled: Boolean(workspaceID)
|
enabled: Boolean(workspaceID)
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useCreateWsTag = () => {
|
export const useCreateWsTag = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<CreateTagRes, {}, CreateTagDTO>({
|
return useMutation<WsTag, {}, CreateTagDTO>({
|
||||||
mutationFn: async ({ workspaceID, tagName, tagColor, tagSlug }) => {
|
mutationFn: async ({ workspaceID, tagName, tagColor, tagSlug }) => {
|
||||||
const { data } = await apiRequest.post(`/api/v2/workspace/${workspaceID}/tags`, {
|
const { data } = await apiRequest.post<{ workspaceTag: WsTag }>(
|
||||||
name: tagName,
|
`/api/v1/workspace/${workspaceID}/tags`,
|
||||||
tagColor: tagColor || "",
|
{
|
||||||
slug: tagSlug
|
name: tagName,
|
||||||
})
|
color: tagColor || "",
|
||||||
return data;
|
slug: tagSlug
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data.workspaceTag;
|
||||||
},
|
},
|
||||||
onSuccess: (tagData) => {
|
onSuccess: (tagData) => {
|
||||||
queryClient.invalidateQueries(workspaceTags.getWsTags(tagData?.workspace));
|
queryClient.invalidateQueries(workspaceTags.getWsTags(tagData?.projectId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useDeleteWsTag = () => {
|
export const useDeleteWsTag = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<DeleteWsTagRes, {}, DeleteTagDTO>({
|
return useMutation<WsTag, {}, DeleteTagDTO>({
|
||||||
mutationFn: async ({ tagID }) => {
|
mutationFn: async ({ tagID, projectId }) => {
|
||||||
const { data } = await apiRequest.delete(`/api/v2/workspace/tags/${tagID}`);
|
const { data } = await apiRequest.delete<{ workspaceTag: WsTag }>(
|
||||||
return data
|
`/api/v1/workspace/${projectId}/tags/${tagID}`
|
||||||
|
);
|
||||||
|
return data.workspaceTag;
|
||||||
},
|
},
|
||||||
onSuccess: (tagData) => {
|
onSuccess: (tagData) => {
|
||||||
queryClient.invalidateQueries(workspaceTags.getWsTags(tagData?.workspace));
|
queryClient.invalidateQueries(workspaceTags.getWsTags(tagData?.projectId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ export type WsTag = {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
tagColor?: string;
|
color?: string;
|
||||||
workspace: string;
|
projectId: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
__v: number;
|
__v: number;
|
||||||
@@ -20,26 +20,7 @@ export type CreateTagDTO = {
|
|||||||
tagColor: string;
|
tagColor: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateTagRes = {
|
export type DeleteTagDTO = { tagID: string; projectId: string };
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
workspace: string;
|
|
||||||
createdAt: string;
|
|
||||||
tagColor?: string;
|
|
||||||
user: string;
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DeleteTagDTO = { tagID: string };
|
|
||||||
|
|
||||||
export type DeleteWsTagRes = {
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
workspace: string;
|
|
||||||
createdAt: string;
|
|
||||||
user: string;
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SecretTags = {
|
export type SecretTags = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
export type TWebhook = {
|
export type TWebhook = {
|
||||||
id: string;
|
id: string;
|
||||||
workspace: string;
|
projectId: string;
|
||||||
environment: string;
|
environment: {
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
envId: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
url: string;
|
url: string;
|
||||||
lastStatus: "success" | "failed";
|
lastStatus: "success" | "failed";
|
||||||
|
|||||||
+1
-1
@@ -87,7 +87,7 @@ export const IntegrationsSection = ({
|
|||||||
<div>
|
<div>
|
||||||
<FormControl label="Environment">
|
<FormControl label="Environment">
|
||||||
<Select
|
<Select
|
||||||
value={integration.environment}
|
value={integration.environment.slug}
|
||||||
isDisabled={integration.isActive}
|
isDisabled={integration.isActive}
|
||||||
className="min-w-[8rem] border border-mineshaft-700"
|
className="min-w-[8rem] border border-mineshaft-700"
|
||||||
>
|
>
|
||||||
|
|||||||
+6
-6
@@ -97,14 +97,14 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
</Td>
|
</Td>
|
||||||
<Td>{secretVersion?.comment}</Td>
|
<Td>{secretVersion?.comment}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{secretVersion?.tags?.map(({ name, id: tagId, tagColor }) => (
|
{secretVersion?.tags?.map(({ name, id: tagId, color }) => (
|
||||||
<Tag
|
<Tag
|
||||||
className="flex w-min items-center space-x-2"
|
className="flex w-min items-center space-x-2"
|
||||||
key={`${secretVersion.id}-${tagId}`}
|
key={`${secretVersion.id}-${tagId}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-3 w-3 rounded-full"
|
className="h-3 w-3 rounded-full"
|
||||||
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
style={{ backgroundColor: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
<div className="text-sm">{name}</div>
|
<div className="text-sm">{name}</div>
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -119,14 +119,14 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
</Td>
|
</Td>
|
||||||
<Td>{newVersion?.secretComment}</Td>
|
<Td>{newVersion?.secretComment}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{newVersion?.tags?.map(({ name, id: tagId, tagColor }) => (
|
{newVersion?.tags?.map(({ name, id: tagId, color }) => (
|
||||||
<Tag
|
<Tag
|
||||||
className="flex w-min items-center space-x-2"
|
className="flex w-min items-center space-x-2"
|
||||||
key={`${newVersion.id}-${tagId}`}
|
key={`${newVersion.id}-${tagId}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-3 w-3 rounded-full"
|
className="h-3 w-3 rounded-full"
|
||||||
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
style={{ backgroundColor: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
<div className="text-sm">{name}</div>
|
<div className="text-sm">{name}</div>
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -151,7 +151,7 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{(op === CommitType.CREATE ? newVersion?.tags : secretVersion?.tags)?.map(
|
{(op === CommitType.CREATE ? newVersion?.tags : secretVersion?.tags)?.map(
|
||||||
({ name, id: tagId, tagColor }) => (
|
({ name, id: tagId, color}) => (
|
||||||
<Tag
|
<Tag
|
||||||
className="flex w-min items-center space-x-2"
|
className="flex w-min items-center space-x-2"
|
||||||
key={`${
|
key={`${
|
||||||
@@ -160,7 +160,7 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-3 w-3 rounded-full"
|
className="h-3 w-3 rounded-full"
|
||||||
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
style={{ backgroundColor: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
<div className="text-sm">{name}</div>
|
<div className="text-sm">{name}</div>
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export const ActionBar = ({
|
|||||||
</DropdownSubMenuTrigger>
|
</DropdownSubMenuTrigger>
|
||||||
<DropdownSubMenuContent className="rounded-l-none">
|
<DropdownSubMenuContent className="rounded-l-none">
|
||||||
<DropdownMenuLabel>Apply tags to filter secrets</DropdownMenuLabel>
|
<DropdownMenuLabel>Apply tags to filter secrets</DropdownMenuLabel>
|
||||||
{tags.map(({ id, name, tagColor }) => (
|
{tags.map(({ id, name, color }) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(evt) => {
|
onClick={(evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
@@ -246,7 +246,7 @@ export const ActionBar = ({
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div
|
<div
|
||||||
className="mr-2 h-2 w-2 rounded-full"
|
className="mr-2 h-2 w-2 rounded-full"
|
||||||
style={{ background: tagColor || "#bec2c8" }}
|
style={{ background: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
{name}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ export const SecretDetailSidebar = ({
|
|||||||
<DropdownMenuContent align="end" className="z-[100]">
|
<DropdownMenuContent align="end" className="z-[100]">
|
||||||
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
|
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
|
||||||
{tags.map((tag) => {
|
{tags.map((tag) => {
|
||||||
const { id: tagId, name, tagColor } = tag;
|
const { id: tagId, name, color } = tag;
|
||||||
|
|
||||||
const isSelected = selectedTagsGroupById?.[tagId];
|
const isSelected = selectedTagsGroupById?.[tagId];
|
||||||
return (
|
return (
|
||||||
@@ -282,7 +282,7 @@ export const SecretDetailSidebar = ({
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div
|
<div
|
||||||
className="mr-2 h-2 w-2 rounded-full"
|
className="mr-2 h-2 w-2 rounded-full"
|
||||||
style={{ background: tagColor || "#bec2c8" }}
|
style={{ background: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
{name}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ export const SecretItem = memo(
|
|||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
|
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
|
||||||
{tags.map((tag) => {
|
{tags.map((tag) => {
|
||||||
const { id: tagId, name, tagColor } = tag;
|
const { id: tagId, name, color } = tag;
|
||||||
|
|
||||||
const isTagSelected = selectedTagsGroupById?.[tagId];
|
const isTagSelected = selectedTagsGroupById?.[tagId];
|
||||||
return (
|
return (
|
||||||
@@ -332,7 +332,7 @@ export const SecretItem = memo(
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div
|
<div
|
||||||
className="mr-2 h-2 w-2 rounded-full"
|
className="mr-2 h-2 w-2 rounded-full"
|
||||||
style={{ background: tagColor || "#bec2c8" }}
|
style={{ background: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
{name}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -151,14 +151,14 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
|
|||||||
<Td className="border-r border-mineshaft-600">Tags</Td>
|
<Td className="border-r border-mineshaft-600">Tags</Td>
|
||||||
{isModified && (
|
{isModified && (
|
||||||
<Td className="border-r border-mineshaft-600">
|
<Td className="border-r border-mineshaft-600">
|
||||||
{preSecret?.tags?.map(({ name, id: tagId, tagColor }) => (
|
{preSecret?.tags?.map(({ name, id: tagId, color }) => (
|
||||||
<Tag
|
<Tag
|
||||||
className="flex w-min items-center space-x-2"
|
className="flex w-min items-center space-x-2"
|
||||||
key={`${preSecret.id}-${tagId}`}
|
key={`${preSecret.id}-${tagId}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-3 w-3 rounded-full"
|
className="h-3 w-3 rounded-full"
|
||||||
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
style={{ backgroundColor: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
<div className="text-sm">{name}</div>
|
<div className="text-sm">{name}</div>
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -166,14 +166,14 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
|
|||||||
</Td>
|
</Td>
|
||||||
)}
|
)}
|
||||||
<Td>
|
<Td>
|
||||||
{postSecret?.tags?.map(({ name, id: tagId, tagColor }) => (
|
{postSecret?.tags?.map(({ name, id: tagId, color }) => (
|
||||||
<Tag
|
<Tag
|
||||||
className="flex w-min items-center space-x-2"
|
className="flex w-min items-center space-x-2"
|
||||||
key={`${postSecret.id}-${tagId}`}
|
key={`${postSecret.id}-${tagId}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-3 w-3 rounded-full"
|
className="h-3 w-3 rounded-full"
|
||||||
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
style={{ backgroundColor: color || "#bec2c8" }}
|
||||||
/>
|
/>
|
||||||
<div className="text-sm">{name}</div>
|
<div className="text-sm">{name}</div>
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|||||||
+3
-1
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
import { PermissionDeniedBanner, ProjectPermissionCan } from "@app/components/permissions";
|
import { PermissionDeniedBanner, ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission, useWorkspace } from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useDeleteWsTag } from "@app/hooks/api";
|
import { useDeleteWsTag } from "@app/hooks/api";
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ export const SecretTagsSection = (): JSX.Element => {
|
|||||||
"CreateSecretTag",
|
"CreateSecretTag",
|
||||||
"deleteTagConfirmation"
|
"deleteTagConfirmation"
|
||||||
] as const);
|
] as const);
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
const permission = useProjectPermission();
|
const permission = useProjectPermission();
|
||||||
|
|
||||||
const deleteWsTag = useDeleteWsTag();
|
const deleteWsTag = useDeleteWsTag();
|
||||||
@@ -26,6 +27,7 @@ export const SecretTagsSection = (): JSX.Element => {
|
|||||||
const onDeleteApproved = async () => {
|
const onDeleteApproved = async () => {
|
||||||
try {
|
try {
|
||||||
await deleteWsTag.mutateAsync({
|
await deleteWsTag.mutateAsync({
|
||||||
|
projectId:currentWorkspace?.id || "",
|
||||||
tagID: (popUp?.deleteTagConfirmation?.data as DeleteModalData)?.id
|
tagID: (popUp?.deleteTagConfirmation?.data as DeleteModalData)?.id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -195,7 +195,7 @@ export const WebhooksTab = withProjectPermission(
|
|||||||
<Td className="max-w-xs overflow-hidden text-ellipsis hover:overflow-auto hover:break-all">
|
<Td className="max-w-xs overflow-hidden text-ellipsis hover:overflow-auto hover:break-all">
|
||||||
{url}
|
{url}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>{environment}</Td>
|
<Td>{environment.slug}</Td>
|
||||||
<Td>{secretPath}</Td>
|
<Td>{secretPath}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{!lastStatus ? (
|
{!lastStatus ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user