Fix merge conflicts

This commit is contained in:
Tuan Dang
2024-03-12 16:20:17 -07:00
81 changed files with 3330 additions and 915 deletions
+24 -3
View File
@@ -1,6 +1,7 @@
import slugify from "@sindresorhus/slugify";
import { z } from "zod";
import { OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -13,7 +14,17 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
organizationId: z.string().trim()
}),
body: z.object({
slug: z.string().trim(),
slug: z
.string()
.min(1)
.trim()
.refine(
(val) => Object.keys(OrgMembershipRole).includes(val),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid"
}),
name: z.string().trim(),
description: z.string().trim().optional(),
permissions: z.any().array()
@@ -45,7 +56,17 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
roleId: z.string().trim()
}),
body: z.object({
slug: z.string().trim().optional(),
slug: z
.string()
.trim()
.optional()
.refine(
(val) => typeof val === "undefined" || Object.keys(OrgMembershipRole).includes(val),
"Please choose a different slug, the slug you have entered is reserved."
)
.refine((val) => typeof val === "undefined" || slugify(val) === val, {
message: "Slug must be a valid"
}),
name: z.string().trim().optional(),
description: z.string().trim().optional(),
permissions: z.any().array()
@@ -1,7 +1,9 @@
import { z } from "zod";
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { IdentityProjectMembershipRoleSchema, ProjectUserMembershipRolesSchema, TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { selectAllTableCols } from "@app/lib/knex";
import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
export type TPermissionDALFactory = ReturnType<typeof permissionDALFactory>;
@@ -43,21 +45,72 @@ export const permissionDALFactory = (db: TDbClient) => {
const getProjectPermission = async (userId: string, projectId: string) => {
try {
const membership = await db(TableName.ProjectMembership)
.leftJoin(TableName.ProjectRoles, `${TableName.ProjectMembership}.roleId`, `${TableName.ProjectRoles}.id`)
const docs = await db(TableName.ProjectMembership)
.join(
TableName.ProjectUserMembershipRole,
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
`${TableName.ProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.ProjectUserMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
.where("userId", userId)
.where(`${TableName.ProjectMembership}.projectId`, projectId)
.select(selectAllTableCols(TableName.ProjectMembership))
.select(selectAllTableCols(TableName.ProjectUserMembershipRole))
.select(
db.ref("id").withSchema(TableName.ProjectMembership).as("membershipId"),
// TODO(roll-forward-migration): remove this field when we drop this in next migration after a week
db.ref("role").withSchema(TableName.ProjectMembership).as("oldRoleField"),
db.ref("createdAt").withSchema(TableName.ProjectMembership).as("membershipCreatedAt"),
db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"),
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
db.ref("orgId").withSchema(TableName.Project)
db.ref("orgId").withSchema(TableName.Project),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
)
.select("permissions")
.first();
.select("permissions");
return membership;
const permission = sqlNestRelationships({
data: docs,
key: "membershipId",
parentMapper: ({
orgId,
orgAuthEnforced,
membershipId,
membershipCreatedAt,
membershipUpdatedAt,
oldRoleField
}) => ({
orgId,
orgAuthEnforced,
userId,
role: oldRoleField,
id: membershipId,
projectId,
createdAt: membershipCreatedAt,
updatedAt: membershipUpdatedAt
}),
childrenMapper: [
{
key: "id",
label: "roles" as const,
mapper: (data) =>
ProjectUserMembershipRolesSchema.extend({
permissions: z.unknown(),
customRoleSlug: z.string().optional().nullable()
}).parse(data)
}
]
});
// when introducting cron mode change it here
const activeRoles = permission?.[0]?.roles.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
);
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
} catch (error) {
throw new DatabaseError({ error, name: "GetProjectPermission" });
}
@@ -65,18 +118,62 @@ export const permissionDALFactory = (db: TDbClient) => {
const getProjectIdentityPermission = async (identityId: string, projectId: string) => {
try {
const membership = await db(TableName.IdentityProjectMembership)
const docs = await db(TableName.IdentityProjectMembership)
.join(
TableName.IdentityProjectMembershipRole,
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
`${TableName.IdentityProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.IdentityProjectMembership}.roleId`,
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.where("identityId", identityId)
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
.select(selectAllTableCols(TableName.IdentityProjectMembership))
.select("permissions")
.first();
return membership;
.select(selectAllTableCols(TableName.IdentityProjectMembershipRole))
.select(
db.ref("id").withSchema(TableName.IdentityProjectMembership).as("membershipId"),
db.ref("role").withSchema(TableName.IdentityProjectMembership).as("oldRoleField"),
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership).as("membershipCreatedAt"),
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership).as("membershipUpdatedAt"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
)
.select("permissions");
const permission = sqlNestRelationships({
data: docs,
key: "membershipId",
parentMapper: ({ membershipId, membershipCreatedAt, membershipUpdatedAt, oldRoleField }) => ({
id: membershipId,
identityId,
projectId,
role: oldRoleField,
createdAt: membershipCreatedAt,
updatedAt: membershipUpdatedAt,
// just a prefilled value
orgAuthEnforced: false,
orgId: ""
}),
childrenMapper: [
{
key: "id",
label: "roles" as const,
mapper: (data) =>
IdentityProjectMembershipRoleSchema.extend({
permissions: z.unknown(),
customRoleSlug: z.string().optional().nullable()
}).parse(data)
}
]
});
// when introducting cron mode change it here
const activeRoles = permission?.[0]?.roles.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
);
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
} catch (error) {
throw new DatabaseError({ error, name: "GetProjectIdentityPermission" });
}
@@ -18,6 +18,7 @@ import { TServiceTokenDALFactory } from "@app/services/service-token/service-tok
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
import { TPermissionDALFactory } from "./permission-dal";
import { TBuildProjectPermissionDTO } from "./permission-types";
import {
buildServiceTokenProjectPermission,
projectAdminPermissions,
@@ -64,31 +65,35 @@ export const permissionServiceFactory = ({
}
};
const buildProjectPermission = (role: string, permission?: unknown) => {
switch (role) {
case ProjectMembershipRole.Admin:
return projectAdminPermissions;
case ProjectMembershipRole.Member:
return projectMemberPermissions;
case ProjectMembershipRole.Viewer:
return projectViewerPermission;
case ProjectMembershipRole.NoAccess:
return projectNoAccessPermissions;
case ProjectMembershipRole.Custom:
return createMongoAbility<ProjectPermissionSet>(
unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
permission as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
),
{
conditionsMatcher
const buildProjectPermission = (projectUserRoles: TBuildProjectPermissionDTO) => {
const rules = projectUserRoles
.map(({ role, permissions }) => {
switch (role) {
case ProjectMembershipRole.Admin:
return projectAdminPermissions;
case ProjectMembershipRole.Member:
return projectMemberPermissions;
case ProjectMembershipRole.Viewer:
return projectViewerPermission;
case ProjectMembershipRole.NoAccess:
return projectNoAccessPermissions;
case ProjectMembershipRole.Custom: {
return unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
);
}
);
default:
throw new BadRequestError({
name: "ProjectRoleInvalid",
message: "Project role not found"
});
}
default:
throw new BadRequestError({
name: "ProjectRoleInvalid",
message: "Project role not found"
});
}
})
.reduce((curr, prev) => prev.concat(curr), []);
return createMongoAbility<ProjectPermissionSet>(rules, {
conditionsMatcher
});
};
/*
@@ -145,33 +150,56 @@ export const permissionServiceFactory = ({
};
// user permission for a project in an organization
const getUserProjectPermission = async (userId: string, projectId: string, userOrgId?: string) => {
const membership = await permissionDAL.getProjectPermission(userId, projectId);
if (!membership) throw new UnauthorizedError({ name: "User not in project" });
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
const getUserProjectPermission = async (
userId: string,
projectId: string,
userOrgId?: string
): Promise<TProjectPermissionRT<ActorType.USER>> => {
const userProjectPermission = await permissionDAL.getProjectPermission(userId, projectId);
if (!userProjectPermission) throw new UnauthorizedError({ name: "User not in project" });
if (
userProjectPermission.roles.some(({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions)
) {
throw new BadRequestError({ name: "Custom permission not found" });
}
if (membership.orgAuthEnforced && membership.orgId !== userOrgId) {
if (userProjectPermission.orgAuthEnforced && userProjectPermission.orgId !== userOrgId) {
throw new BadRequestError({ name: "Cannot access org-scoped resource" });
}
return {
permission: buildProjectPermission(membership.role, membership.permissions),
membership
permission: buildProjectPermission(userProjectPermission.roles),
membership: userProjectPermission,
hasRole: (role: string) =>
userProjectPermission.roles.findIndex(
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
) !== -1
};
};
const getIdentityProjectPermission = async (identityId: string, projectId: string) => {
const membership = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
if (!membership) throw new UnauthorizedError({ name: "Identity not in project" });
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
const getIdentityProjectPermission = async (
identityId: string,
projectId: string
): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
if (!identityProjectPermission) throw new UnauthorizedError({ name: "Identity not in project" });
if (
identityProjectPermission.roles.some(
({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions
)
) {
throw new BadRequestError({ name: "Custom permission not found" });
}
return {
permission: buildProjectPermission(membership.role, membership.permissions),
membership
permission: buildProjectPermission(identityProjectPermission.roles),
membership: identityProjectPermission,
hasRole: (role: string) =>
identityProjectPermission.roles.findIndex(
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
) !== -1
};
};
@@ -191,14 +219,19 @@ export const permissionServiceFactory = ({
};
type TProjectPermissionRT<T extends ActorType> = T extends ActorType.SERVICE
? { permission: MongoAbility<ProjectPermissionSet, MongoQuery>; membership: undefined }
? {
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
membership: undefined;
hasRole: (arg: string) => boolean;
} // service token doesn't have both membership and roles
: {
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
membership: (T extends ActorType.USER ? TProjectMemberships : TIdentityProjectMemberships) & {
orgAuthEnforced: boolean;
orgAuthEnforced: boolean | null | undefined;
orgId: string;
permissions?: unknown;
roles: Array<{ role: string }>;
};
hasRole: (role: string) => boolean;
};
const getProjectPermission = async <T extends ActorType>(
@@ -228,11 +261,13 @@ export const permissionServiceFactory = ({
const projectRole = await projectRoleDAL.findOne({ slug: role, projectId });
if (!projectRole) throw new BadRequestError({ message: "Role not found" });
return {
permission: buildProjectPermission(ProjectMembershipRole.Custom, projectRole.permissions),
permission: buildProjectPermission([
{ role: ProjectMembershipRole.Custom, permissions: projectRole.permissions }
]),
role: projectRole
};
}
return { permission: buildProjectPermission(role, []) };
return { permission: buildProjectPermission([{ role, permissions: [] }]) };
};
return {
@@ -0,0 +1,4 @@
export type TBuildProjectPermissionDTO = {
permissions?: unknown;
role: string;
}[];
@@ -56,8 +56,8 @@ export type ProjectPermissionSet =
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
| [ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback];
const buildAdminPermission = () => {
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
const buildAdminPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
@@ -135,13 +135,13 @@ const buildAdminPermission = () => {
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Project);
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Project);
return build({ conditionsMatcher });
return rules;
};
export const projectAdminPermissions = buildAdminPermission();
export const projectAdminPermissions = buildAdminPermissionRules();
const buildMemberPermission = () => {
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
const buildMemberPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
@@ -196,13 +196,13 @@ const buildMemberPermission = () => {
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
return build({ conditionsMatcher });
return rules;
};
export const projectMemberPermissions = buildMemberPermission();
export const projectMemberPermissions = buildMemberPermissionRules();
const buildViewerPermission = () => {
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
const buildViewerPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
@@ -220,14 +220,14 @@ const buildViewerPermission = () => {
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
return build({ conditionsMatcher });
return rules;
};
export const projectViewerPermission = buildViewerPermission();
export const projectViewerPermission = buildViewerPermissionRules();
const buildNoAccessProjectPermission = () => {
const { build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
return build({ conditionsMatcher });
const { rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
return rules;
};
export const buildServiceTokenProjectPermission = (
@@ -129,14 +129,14 @@ export const secretApprovalRequestServiceFactory = ({
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
const { policy } = secretApprovalRequest;
const { membership } = await permissionService.getProjectPermission(
const { membership, hasRole } = await permissionService.getProjectPermission(
actor,
actorId,
secretApprovalRequest.projectId,
actorOrgId
);
if (
membership.role !== ProjectMembershipRole.Admin &&
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
@@ -156,14 +156,14 @@ export const secretApprovalRequestServiceFactory = ({
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
const { policy } = secretApprovalRequest;
const { membership } = await permissionService.getProjectPermission(
const { membership, hasRole } = await permissionService.getProjectPermission(
ActorType.USER,
actorId,
secretApprovalRequest.projectId,
actorOrgId
);
if (
membership.role !== ProjectMembershipRole.Admin &&
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
@@ -198,14 +198,14 @@ export const secretApprovalRequestServiceFactory = ({
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
const { policy } = secretApprovalRequest;
const { membership } = await permissionService.getProjectPermission(
const { membership, hasRole } = await permissionService.getProjectPermission(
ActorType.USER,
actorId,
secretApprovalRequest.projectId,
actorOrgId
);
if (
membership.role !== ProjectMembershipRole.Admin &&
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
@@ -236,9 +236,14 @@ export const secretApprovalRequestServiceFactory = ({
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
const { policy, folderId, projectId } = secretApprovalRequest;
const { membership } = await permissionService.getProjectPermission(ActorType.USER, actorId, projectId, actorOrgId);
const { membership, hasRole } = await permissionService.getProjectPermission(
ActorType.USER,
actorId,
projectId,
actorOrgId
);
if (
membership.role !== ProjectMembershipRole.Admin &&
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
@@ -1,3 +1,10 @@
import {
CreateAccessKeyCommand,
DeleteAccessKeyCommand,
GetAccessKeyLastUsedCommand,
IAMClient
} from "@aws-sdk/client-iam";
import { SecretKeyEncoding, SecretType } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import {
@@ -18,7 +25,12 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
import { TSecretRotationDALFactory } from "../secret-rotation-dal";
import { rotationTemplates } from "../templates";
import { TDbProviderClients, TProviderFunctionTypes, TSecretRotationProviderTemplate } from "../templates/types";
import {
TAwsProviderSystems,
TDbProviderClients,
TProviderFunctionTypes,
TSecretRotationProviderTemplate
} from "../templates/types";
import {
getDbSetQuery,
secretRotationDbFn,
@@ -127,7 +139,10 @@ export const secretRotationQueueFactory = ({
internal: {}
};
// when its a database we keep cycling the variables accordingly
/* Rotation Function For Database
* A database like sql cannot have multiple password for a user
* thus we ask users to create two users with required permission and then we keep cycling between these two db users
*/
if (provider.template.type === TProviderFunctionTypes.DB) {
const lastCred = variables.creds.at(-1);
if (lastCred && variables.creds.length === 1) {
@@ -170,6 +185,65 @@ export const secretRotationQueueFactory = ({
if (variables.creds.length === 2) variables.creds.pop();
}
/*
* Rotation Function For AWS Services
* Due to complexity in AWS Authorization hashing signature process we keep it as seperate entity instead of http template mode
* We first delete old key before creating a new one because aws iam has a quota limit of 2 keys
* */
if (provider.template.type === TProviderFunctionTypes.AWS) {
if (provider.template.client === TAwsProviderSystems.IAM) {
const client = new IAMClient({
region: newCredential.inputs.manager_user_aws_region as string,
credentials: {
accessKeyId: newCredential.inputs.manager_user_access_key as string,
secretAccessKey: newCredential.inputs.manager_user_secret_key as string
}
});
const iamUserName = newCredential.inputs.iam_username as string;
if (variables.creds.length === 2) {
const deleteCycleCredential = variables.creds.pop();
if (deleteCycleCredential) {
const deletedIamAccessKey = await client.send(
new DeleteAccessKeyCommand({
UserName: iamUserName,
AccessKeyId: deleteCycleCredential.outputs.iam_user_access_key as string
})
);
if (
!deletedIamAccessKey?.$metadata?.httpStatusCode ||
deletedIamAccessKey?.$metadata?.httpStatusCode > 300
) {
throw new DisableRotationErrors({
message: "Failed to delete aws iam access key. Check managed iam user policy"
});
}
}
}
const newIamAccessKey = await client.send(new CreateAccessKeyCommand({ UserName: iamUserName }));
if (!newIamAccessKey.AccessKey)
throw new DisableRotationErrors({ message: "Failed to create access key. Check managed iam user policy" });
// test
const testAccessKey = await client.send(
new GetAccessKeyLastUsedCommand({ AccessKeyId: newIamAccessKey.AccessKey.AccessKeyId })
);
if (testAccessKey?.UserName !== iamUserName)
throw new DisableRotationErrors({ message: "Failed to create access key. Check managed iam user policy" });
newCredential.outputs.iam_user_access_key = newIamAccessKey.AccessKey.AccessKeyId;
newCredential.outputs.iam_user_secret_key = newIamAccessKey.AccessKey.SecretAccessKey;
}
}
/* Rotation function of HTTP infisical template
* This is a generic http based template system for rotation
* we use this for sendgrid and for custom secret rotation
* This will ensure user provided rotation is easier to make
* */
if (provider.template.type === TProviderFunctionTypes.HTTP) {
if (provider.template.functions.set?.pre) {
secretRotationPreSetFn(provider.template.functions.set.pre, newCredential);
@@ -185,6 +259,9 @@ export const secretRotationQueueFactory = ({
}
}
}
// insert the new variables to start
// encrypt the data - save it
variables.creds.unshift({
outputs: newCredential.outputs,
internal: newCredential.internal
@@ -200,6 +277,7 @@ export const secretRotationQueueFactory = ({
key
)
}));
// map the final values to output keys in the board
await secretRotationDAL.transaction(async (tx) => {
await secretRotationDAL.updateById(
rotationId,
@@ -0,0 +1,21 @@
import { TAwsProviderSystems, TProviderFunctionTypes } from "./types";
export const AWS_IAM_TEMPLATE = {
type: TProviderFunctionTypes.AWS as const,
client: TAwsProviderSystems.IAM,
inputs: {
type: "object" as const,
properties: {
manager_user_access_key: { type: "string" as const },
manager_user_secret_key: { type: "string" as const },
manager_user_aws_region: { type: "string" as const },
iam_username: { type: "string" as const }
},
required: ["manager_user_access_key", "manager_user_secret_key", "manager_user_aws_region", "iam_username"],
additionalProperties: false
},
outputs: {
iam_user_access_key: { type: "string" },
iam_user_secret_key: { type: "string" }
}
};
@@ -1,3 +1,4 @@
import { AWS_IAM_TEMPLATE } from "./aws-iam";
import { MYSQL_TEMPLATE } from "./mysql";
import { POSTGRES_TEMPLATE } from "./postgres";
import { SENDGRID_TEMPLATE } from "./sendgrid";
@@ -24,5 +25,12 @@ export const rotationTemplates: TSecretRotationProviderTemplate[] = [
image: "mysql.png",
description: "Rotate MySQL@7/MariaDB user credentials",
template: MYSQL_TEMPLATE
},
{
name: "aws-iam",
title: "AWS IAM",
image: "aws-iam.svg",
description: "Rotate AWS IAM User credentials",
template: AWS_IAM_TEMPLATE
}
];
@@ -1,6 +1,7 @@
export enum TProviderFunctionTypes {
HTTP = "http",
DB = "database"
DB = "database",
AWS = "aws"
}
export enum TDbProviderClients {
@@ -10,6 +11,10 @@ export enum TDbProviderClients {
MySql = "mysql"
}
export enum TAwsProviderSystems {
IAM = "iam"
}
export enum TAssignOp {
Direct = "direct",
JmesPath = "jmesopath"
@@ -42,7 +47,7 @@ export type TSecretRotationProviderTemplate = {
title: string;
image?: string;
description?: string;
template: THttpProviderTemplate | TDbProviderTemplate;
template: THttpProviderTemplate | TDbProviderTemplate | TAwsProviderTemplate;
};
export type THttpProviderTemplate = {
@@ -70,3 +75,14 @@ export type TDbProviderTemplate = {
};
outputs: Record<string, unknown>;
};
export type TAwsProviderTemplate = {
type: TProviderFunctionTypes.AWS;
client: TAwsProviderSystems;
inputs: {
type: "object";
properties: Record<string, { type: string; [x: string]: unknown; desc?: string }>;
required?: string[];
};
outputs: Record<string, unknown>;
};