Compare commits

..

1 Commits

Author SHA1 Message Date
Vladyslav Matsiiako
26148b633b added tags for aws integrations 2024-04-17 21:34:54 -07:00
35 changed files with 465 additions and 235 deletions

View File

@@ -23,17 +23,16 @@ module.exports = {
root: true,
overrides: [
{
files: ["./e2e-test/**/*", "./src/db/migrations/**/*"],
files: ["./e2e-test/**/*"],
rules: {
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-call": "off"
"@typescript-eslint/no-unsafe-call": "off",
}
}
],
rules: {
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unsafe-enum-comparison": "off",

View File

@@ -1,47 +0,0 @@
import { Knex } from "knex";
import { ProjectMembershipRole, TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const doesProjectRoleFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "role");
const doesProjectRoleIdFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "roleId");
await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
if (doesProjectRoleFieldExist) t.dropColumn("roleId");
if (doesProjectRoleIdFieldExist) t.dropColumn("role");
});
const doesIdentityProjectRoleFieldExist = await knex.schema.hasColumn(TableName.IdentityProjectMembership, "role");
const doesIdentityProjectRoleIdFieldExist = await knex.schema.hasColumn(
TableName.IdentityProjectMembership,
"roleId"
);
await knex.schema.alterTable(TableName.IdentityProjectMembership, (t) => {
if (doesIdentityProjectRoleFieldExist) t.dropColumn("roleId");
if (doesIdentityProjectRoleIdFieldExist) t.dropColumn("role");
});
}
export async function down(knex: Knex): Promise<void> {
const doesProjectRoleFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "role");
const doesProjectRoleIdFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "roleId");
await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
if (!doesProjectRoleFieldExist) t.string("role").defaultTo(ProjectMembershipRole.Member);
if (!doesProjectRoleIdFieldExist) {
t.uuid("roleId");
t.foreign("roleId").references("id").inTable(TableName.ProjectRoles);
}
});
const doesIdentityProjectRoleFieldExist = await knex.schema.hasColumn(TableName.IdentityProjectMembership, "role");
const doesIdentityProjectRoleIdFieldExist = await knex.schema.hasColumn(
TableName.IdentityProjectMembership,
"roleId"
);
await knex.schema.alterTable(TableName.IdentityProjectMembership, (t) => {
if (!doesIdentityProjectRoleFieldExist) t.string("role").defaultTo(ProjectMembershipRole.Member);
if (!doesIdentityProjectRoleIdFieldExist) {
t.uuid("roleId");
t.foreign("roleId").references("id").inTable(TableName.ProjectRoles);
}
});
}

View File

@@ -9,6 +9,8 @@ import { TImmutableDBKeys } from "./models";
export const IdentityProjectMembershipsSchema = z.object({
id: z.string().uuid(),
role: z.string(),
roleId: z.string().uuid().nullable().optional(),
projectId: z.string(),
identityId: z.string().uuid(),
createdAt: z.date(),

View File

@@ -9,10 +9,12 @@ import { TImmutableDBKeys } from "./models";
export const ProjectMembershipsSchema = z.object({
id: z.string().uuid(),
role: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
userId: z.string().uuid(),
projectId: z.string()
projectId: z.string(),
roleId: z.string().uuid().nullable().optional()
});
export type TProjectMemberships = z.infer<typeof ProjectMembershipsSchema>;

View File

@@ -33,7 +33,8 @@ export async function seed(knex: Knex): Promise<void> {
const projectMembership = await knex(TableName.ProjectMembership)
.insert({
projectId: project.id,
userId: seedData1.id
userId: seedData1.id,
role: ProjectMembershipRole.Admin
})
.returning("*");
await knex(TableName.ProjectUserMembershipRole).insert({

View File

@@ -78,7 +78,8 @@ export async function seed(knex: Knex): Promise<void> {
const identityProjectMembership = await knex(TableName.IdentityProjectMembership)
.insert({
identityId: seedData1.machineIdentity.id,
projectId: seedData1.project.id
projectId: seedData1.project.id,
role: ProjectMembershipRole.Admin
})
.returning("*");

View File

@@ -19,6 +19,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
description: "Return project secret snapshots ids",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],
@@ -96,7 +97,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
description: "Return audit logs",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({

View File

@@ -69,6 +69,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
description: "Roll back project secrets to those captured in a secret snapshot version.",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],

View File

@@ -72,6 +72,7 @@ export const permissionDALFactory = (db: TDbClient) => {
.select(selectAllTableCols(TableName.GroupProjectMembershipRole))
.select(
db.ref("id").withSchema(TableName.GroupProjectMembership).as("membershipId"),
// TODO(roll-forward-migration): remove this field when we drop this in next migration after a week
db.ref("createdAt").withSchema(TableName.GroupProjectMembership).as("membershipCreatedAt"),
db.ref("updatedAt").withSchema(TableName.GroupProjectMembership).as("membershipUpdatedAt"),
db.ref("projectId").withSchema(TableName.GroupProjectMembership),
@@ -104,6 +105,7 @@ export const permissionDALFactory = (db: TDbClient) => {
.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("createdAt").withSchema(TableName.ProjectMembership).as("membershipCreatedAt"),
db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"),
db.ref("projectId").withSchema(TableName.ProjectMembership),
@@ -129,10 +131,11 @@ export const permissionDALFactory = (db: TDbClient) => {
const permission = sqlNestRelationships({
data: docs,
key: "projectId",
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt, role }) => ({
orgId,
orgAuthEnforced,
userId,
role,
id: membershipId,
projectId,
createdAt: membershipCreatedAt,
@@ -176,10 +179,18 @@ export const permissionDALFactory = (db: TDbClient) => {
? sqlNestRelationships({
data: groupDocs,
key: "projectId",
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
parentMapper: ({
orgId,
orgAuthEnforced,
membershipId,
membershipCreatedAt,
membershipUpdatedAt,
role
}) => ({
orgId,
orgAuthEnforced,
userId,
role,
id: membershipId,
projectId,
createdAt: membershipCreatedAt,
@@ -259,6 +270,7 @@ export const permissionDALFactory = (db: TDbClient) => {
.select(
db.ref("id").withSchema(TableName.IdentityProjectMembership).as("membershipId"),
db.ref("orgId").withSchema(TableName.Project).as("orgId"), // Now you can select orgId from Project
db.ref("role").withSchema(TableName.IdentityProjectMembership).as("oldRoleField"),
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership).as("membershipCreatedAt"),
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership).as("membershipUpdatedAt"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
@@ -287,10 +299,11 @@ export const permissionDALFactory = (db: TDbClient) => {
const permission = sqlNestRelationships({
data: docs,
key: "membershipId",
parentMapper: ({ membershipId, membershipCreatedAt, membershipUpdatedAt, orgId }) => ({
parentMapper: ({ membershipId, membershipCreatedAt, membershipUpdatedAt, oldRoleField, orgId }) => ({
id: membershipId,
identityId,
projectId,
role: oldRoleField,
createdAt: membershipCreatedAt,
updatedAt: membershipUpdatedAt,
orgId,

View File

@@ -589,7 +589,8 @@ export const INTEGRATION = {
secretSuffix: "The suffix for the saved secret. Used by GCP",
initialSyncBehavoir: "Type of syncing behavoir with the integration",
shouldAutoRedeploy: "Used by Render to trigger auto deploy",
secretGCPLabel: "The label for the GCP secrets"
secretGCPLabel: "The label for the GCP secrets",
secretAWSTag: "The tag for the AWS secrets"
}
},
UPDATE: {

View File

@@ -30,6 +30,12 @@ export const fastifySwagger = fp(async (fastify) => {
scheme: "bearer",
bearerFormat: "JWT",
description: "An access token in Infisical"
},
apiKeyAuth: {
type: "apiKey",
in: "header",
name: "X-API-Key",
description: "An API Key in Infisical"
}
}
}

View File

@@ -56,7 +56,14 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
labelValue: z.string()
})
.optional()
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel)
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel),
secretAWSTag: z
.object({
key: z.string(),
value: z.string()
})
.optional()
.describe(INTEGRATION.CREATE.metadata.secretAWSTag)
})
.optional()
}),

View File

@@ -18,7 +18,8 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
description: "Create environment",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -76,7 +77,8 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
description: "Update environment",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -142,7 +144,8 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
description: "Delete environment",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({

View File

@@ -26,7 +26,8 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
description: "Return project user memberships",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -34,28 +35,31 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
}),
response: {
200: z.object({
memberships: ProjectMembershipsSchema.extend({
user: UsersSchema.pick({
email: true,
firstName: true,
lastName: true,
id: true
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
roles: z.array(
memberships: ProjectMembershipsSchema.omit({ role: true })
.merge(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
user: UsersSchema.pick({
email: true,
firstName: true,
lastName: true,
id: true
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
)
})
)
})
.omit({ createdAt: true, updatedAt: true })
.array()
})
@@ -138,7 +142,8 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
description: "Update project user membership",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -211,7 +216,8 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
description: "Delete project user membership",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({

View File

@@ -70,29 +70,32 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
users: ProjectMembershipsSchema.extend({
user: UsersSchema.pick({
email: true,
username: true,
firstName: true,
lastName: true,
id: true
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
roles: z.array(
users: ProjectMembershipsSchema.omit({ role: true })
.merge(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
user: UsersSchema.pick({
username: true,
email: true,
firstName: true,
lastName: true,
id: true
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
)
})
)
})
.omit({ createdAt: true, updatedAt: true })
.array()
})

View File

@@ -19,7 +19,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Create folders",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
body: z.object({
@@ -75,7 +76,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Update folder",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -138,7 +140,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Delete a folder",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -197,7 +200,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
description: "Get folders",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({

View File

@@ -19,7 +19,8 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Create secret imports",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
body: z.object({
@@ -83,7 +84,8 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Update secret imports",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -157,7 +159,8 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Delete secret imports",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -220,7 +223,8 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
description: "Get secret imports",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({

View File

@@ -18,7 +18,8 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
description: "Return organization identity memberships",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({

View File

@@ -17,7 +17,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Return organization user memberships",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -65,7 +66,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Return projects in organization that user is part of",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -113,7 +115,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Update organization user memberships",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -155,7 +158,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
description: "Delete organization user memberships",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({

View File

@@ -36,6 +36,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
},
schema: {
description: "Return encrypted project key",
security: [
{
apiKeyAuth: []
}
],
params: z.object({
workspaceId: z.string().trim().describe(PROJECTS.GET_KEY.workspaceId)
}),

View File

@@ -85,6 +85,11 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
},
schema: {
description: "Return organizations that current user is part of",
security: [
{
apiKeyAuth: []
}
],
response: {
200: z.object({
organizations: OrganizationsSchema.array()
@@ -212,6 +217,11 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
},
schema: {
description: "Retrieve the current user on the request",
security: [
{
apiKeyAuth: []
}
],
response: {
200: z.object({
user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true }))

View File

@@ -158,7 +158,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "List secrets",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({
@@ -279,7 +280,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Get a secret by name",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -373,7 +375,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Create secret",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -461,7 +464,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Update secret",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({
@@ -546,7 +550,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
description: "Delete secret",
security: [
{
bearerAuth: []
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({

View File

@@ -93,7 +93,9 @@ export const identityProjectServiceFactory = ({
const identityProjectMembership = await identityProjectDAL.create(
{
identityId,
projectId: project.id
projectId: project.id,
role: isCustomRole ? ProjectMembershipRole.Custom : role,
roleId: customRole?.id
},
tx
);

View File

@@ -457,6 +457,8 @@ const syncSecretsAWSParameterStore = async ({
});
ssm.config.update(config);
const metadata = z.record(z.any()).parse(integration.metadata);
const params = {
Path: integration.path as string,
Recursive: false,
@@ -486,7 +488,8 @@ const syncSecretsAWSParameterStore = async ({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
Overwrite: true
// Overwrite: true,
Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
})
.promise();
// case: secret exists in AWS parameter store
@@ -499,6 +502,7 @@ const syncSecretsAWSParameterStore = async ({
Type: "SecureString",
Value: secrets[key].value,
Overwrite: true
// Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
})
.promise();
}
@@ -537,6 +541,7 @@ const syncSecretsAWSSecretManager = async ({
}) => {
let secretsManager;
const secKeyVal = getSecretKeyValuePair(secrets);
const metadata = z.record(z.any()).parse(integration.metadata);
try {
if (!accessId) return;
@@ -573,7 +578,8 @@ const syncSecretsAWSSecretManager = async ({
await secretsManager.send(
new CreateSecretCommand({
Name: integration.app as string,
SecretString: JSON.stringify(secKeyVal)
SecretString: JSON.stringify(secKeyVal),
Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
})
);
}

View File

@@ -22,6 +22,10 @@ export type TCreateIntegrationDTO = {
labelName: string;
labelValue: string;
};
secretAWSTag?: {
key: string;
value: string;
};
};
} & Omit<TProjectPermission, "projectId">;

View File

@@ -232,7 +232,8 @@ export const projectQueueFactory = ({
const projectMembership = await projectMembershipDAL.create(
{
projectId: project.id,
userId: ghostUser.user.id
userId: ghostUser.user.id,
role: ProjectMembershipRole.Admin
},
tx
);

View File

@@ -141,7 +141,8 @@ export const projectServiceFactory = ({
const projectMembership = await projectMembershipDAL.create(
{
userId: ghostUser.user.id,
projectId: project.id
projectId: project.id,
role: ProjectMembershipRole.Admin
},
tx
);
@@ -243,7 +244,8 @@ export const projectServiceFactory = ({
const userProjectMembership = await projectMembershipDAL.create(
{
projectId: project.id,
userId: user.id
userId: user.id,
role: projectAdmin.projectRole
},
tx
);
@@ -300,7 +302,9 @@ export const projectServiceFactory = ({
const identityProjectMembership = await identityProjectDAL.create(
{
identityId: actorId,
projectId: project.id
projectId: project.id,
role: isCustomRole ? ProjectMembershipRole.Custom : ProjectMembershipRole.Admin,
roleId: customRole?.id
},
tx
);

View File

@@ -66,8 +66,6 @@ services:
environment:
- DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable
command: npm run migration:latest
volumes:
- ./backend/src:/app/src
backend:
container_name: infisical-dev-api

View File

@@ -29,7 +29,8 @@ Prerequisites:
"ssm:PutParameter",
"ssm:DeleteParameter",
"ssm:GetParametersByPath",
"ssm:DeleteParameters"
"ssm:DeleteParameters",
"ssm:AddTagsToResource"
],
"Resource": "*"
}

View File

@@ -28,7 +28,8 @@ Prerequisites:
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:CreateSecret",
"secretsmanager:UpdateSecret"
"secretsmanager:UpdateSecret",
"secretsmanager:TagResource"
],
"Resource": "*"
}

View File

@@ -405,6 +405,13 @@
{
"group": "Endpoints",
"pages": [
{
"group": "Users",
"pages": [
"api-reference/endpoints/users/me",
"api-reference/endpoints/users/my-organizations"
]
},
{
"group": "Identities",
"pages": [
@@ -450,6 +457,7 @@
"api-reference/endpoints/workspaces/list-identity-memberships",
"api-reference/endpoints/workspaces/update-identity-membership",
"api-reference/endpoints/workspaces/delete-identity-membership",
"api-reference/endpoints/workspaces/workspace-key",
"api-reference/endpoints/workspaces/secret-snapshots",
"api-reference/endpoints/workspaces/rollback-snapshot"
]

View File

@@ -63,6 +63,10 @@ export const useCreateIntegration = () => {
secretSuffix?: string;
initialSyncBehavior?: string;
shouldAutoRedeploy?: boolean;
secretAWSTag?: {
key: string;
value: string;
};
};
}) => {
const {

View File

@@ -10,6 +10,7 @@ import {
faCircleInfo
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion";
import queryString from "query-string";
import { useCreateIntegration } from "@app/hooks/api";
@@ -21,11 +22,21 @@ import {
FormControl,
Input,
Select,
SelectItem
SelectItem,
Switch,
Tab,
TabList,
TabPanel,
Tabs
} from "../../../components/v2";
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
enum TabSections {
Connection = "connection",
Options = "options"
}
const awsRegions = [
{ name: "US East (Ohio)", slug: "us-east-2" },
{ name: "US East (N. Virginia)", slug: "us-east-1" },
@@ -76,6 +87,9 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const [pathErrorText, setPathErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [shouldTag, setShouldTag] = useState(false);
const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState("");
useEffect(() => {
if (workspace) {
@@ -110,7 +124,17 @@ export default function AWSParameterStoreCreateIntegrationPage() {
sourceEnvironment: selectedSourceEnvironment,
path,
region: selectedAWSRegion,
secretPath
secretPath,
metadata: {
...(shouldTag
? {
secretAWSTag: {
key: tagKey,
value: tagValue
}
}
: {})
}
});
setIsLoading(false);
@@ -157,56 +181,114 @@ export default function AWSParameterStoreCreateIntegrationPage() {
</Link>
</div>
</CardTitle>
<FormControl label="Project Environment" className="px-6">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
<Tabs defaultValue={TabSections.Connection} className="px-6">
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Connection}>Connection</Tab>
<Tab value={TabSections.Options}>Options</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Connection}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<FormControl label="Project Environment">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="Path"
errorText={pathErrorText}
isError={pathErrorText !== "" ?? false}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path" className="px-6">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region" className="px-6">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="Path"
errorText={pathErrorText}
isError={pathErrorText !== "" ?? false}
className="px-6"
>
<Input
placeholder={`/${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}/`}
value={path}
onChange={(e) => setPath(e.target.value)}
/>
</FormControl>
<Input
placeholder={`/${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}/`}
value={path}
onChange={(e) => setPath(e.target.value)}
/>
</FormControl>
</motion.div>
</TabPanel>
<TabPanel value={TabSections.Options}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: -30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<div className="mt-2 ml-1">
<Switch
id="tag-aws"
onCheckedChange={() => setShouldTag(!shouldTag)}
isChecked={shouldTag}
>
Tag in AWS Parameter Store
</Switch>
</div>
{shouldTag && (
<div className="mt-4">
<FormControl
label="Tag Key"
>
<Input
placeholder="managed-by"
value={tagKey}
onChange={(e) => setTagKey(e.target.value)}
/>
</FormControl>
<FormControl
label="Tag Value"
>
<Input
placeholder="managed-by"
value={tagValue}
onChange={(e) => setTagValue(e.target.value)}
/>
</FormControl>
</div>
)}
</motion.div>
</TabPanel>
</Tabs>
<Button
onClick={handleButtonClick}
color="mineshaft"

View File

@@ -10,6 +10,7 @@ import {
faCircleInfo
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion";
import queryString from "query-string";
import { useCreateIntegration } from "@app/hooks/api";
@@ -21,11 +22,21 @@ import {
FormControl,
Input,
Select,
SelectItem
SelectItem,
Switch,
Tab,
TabList,
TabPanel,
Tabs
} from "../../../components/v2";
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
enum TabSections {
Connection = "connection",
Options = "options"
}
const awsRegions = [
{ name: "US East (Ohio)", slug: "us-east-2" },
{ name: "US East (N. Virginia)", slug: "us-east-1" },
@@ -74,11 +85,14 @@ export default function AWSSecretManagerCreateIntegrationPage() {
const [selectedAWSRegion, setSelectedAWSRegion] = useState("");
const [targetSecretName, setTargetSecretName] = useState("");
const [targetSecretNameErrorText, setTargetSecretNameErrorText] = useState("");
const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState("");
// const [path, setPath] = useState('');
// const [pathErrorText, setPathErrorText] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [shouldTag, setShouldTag] = useState(false);
useEffect(() => {
if (workspace) {
@@ -109,7 +123,17 @@ export default function AWSSecretManagerCreateIntegrationPage() {
app: targetSecretName.trim(),
sourceEnvironment: selectedSourceEnvironment,
region: selectedAWSRegion,
secretPath
secretPath,
metadata: {
...(shouldTag
? {
secretAWSTag: {
key: tagKey,
value: tagValue
}
}
: {})
}
});
setIsLoading(false);
@@ -156,56 +180,114 @@ export default function AWSSecretManagerCreateIntegrationPage() {
</Link>
</div>
</CardTitle>
<FormControl label="Project Environment" className="px-6">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
<Tabs defaultValue={TabSections.Connection} className="px-6">
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Connection}>Connection</Tab>
<Tab value={TabSections.Options}>Options</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Connection}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<FormControl label="Project Environment">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="AWS SM Secret Name"
errorText={targetSecretNameErrorText}
isError={targetSecretNameErrorText !== "" ?? false}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Secrets Path" className="px-6">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region" className="px-6">
<Select
value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)}
className="w-full border border-mineshaft-500"
>
{awsRegions.map((awsRegion) => (
<SelectItem value={awsRegion.slug} key={`flyio-environment-${awsRegion.slug}`}>
{awsRegion.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl
label="AWS SM Secret Name"
errorText={targetSecretNameErrorText}
isError={targetSecretNameErrorText !== "" ?? false}
className="px-6"
>
<Input
placeholder={`${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}`}
value={targetSecretName}
onChange={(e) => setTargetSecretName(e.target.value)}
/>
</FormControl>
<Input
placeholder={`${workspace.name
.toLowerCase()
.replace(/ /g, "-")}/${selectedSourceEnvironment}`}
value={targetSecretName}
onChange={(e) => setTargetSecretName(e.target.value)}
/>
</FormControl>
</motion.div>
</TabPanel>
<TabPanel value={TabSections.Options}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: -30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<div className="mt-2 ml-1">
<Switch
id="tag-aws"
onCheckedChange={() => setShouldTag(!shouldTag)}
isChecked={shouldTag}
>
Tag in AWS Secrets Manager
</Switch>
</div>
{shouldTag && (
<div className="mt-4">
<FormControl
label="Tag Key"
>
<Input
placeholder="managed-by"
value={tagKey}
onChange={(e) => setTagKey(e.target.value)}
/>
</FormControl>
<FormControl
label="Tag Value"
>
<Input
placeholder="managed-by"
value={tagValue}
onChange={(e) => setTagValue(e.target.value)}
/>
</FormControl>
</div>
)}
</motion.div>
</TabPanel>
</Tabs>
<Button
onClick={handleButtonClick}
color="mineshaft"

View File

@@ -5,4 +5,8 @@ export type Metadata = {
labelName: string;
labelValue: string;
}
secretAWSTag?: {
key: string;
value: string;
}
}