mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 21:28:18 +00:00
Requested changes
This commit is contained in:
@@ -1,11 +1,131 @@
|
|||||||
|
import {
|
||||||
|
CreateUserCommand,
|
||||||
|
CreateUserGroupCommand,
|
||||||
|
DeleteUserCommand,
|
||||||
|
DescribeReplicationGroupsCommand,
|
||||||
|
DescribeUserGroupsCommand,
|
||||||
|
ElastiCache,
|
||||||
|
ModifyReplicationGroupCommand,
|
||||||
|
ModifyUserGroupCommand
|
||||||
|
} from "@aws-sdk/client-elasticache";
|
||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { CreateElastiCacheUserSchema, DeleteElasticCacheUserSchema, ElastiCacheUserManager } from "@app/lib/aws";
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
|
||||||
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
|
const CreateElastiCacheUserSchema = z.object({
|
||||||
|
UserId: z.string().trim().min(1),
|
||||||
|
UserName: z.string().trim().min(1),
|
||||||
|
Engine: z.string().default("redis"),
|
||||||
|
Passwords: z.array(z.string().trim().min(1)).min(1).max(1), // Minimum password length is 16 characters, required by AWS.
|
||||||
|
AccessString: z.string().trim().min(1) // Example: "on ~* +@all"
|
||||||
|
});
|
||||||
|
|
||||||
|
const DeleteElasticCacheUserSchema = z.object({
|
||||||
|
UserId: z.string().trim().min(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
type TElastiCacheRedisUser = { userId: string; password: string };
|
||||||
|
type TBasicAWSCredentials = { accessKeyId: string; secretAccessKey: string };
|
||||||
|
|
||||||
|
type TCreateElastiCacheUserInput = z.infer<typeof CreateElastiCacheUserSchema>;
|
||||||
|
type TDeleteElastiCacheUserInput = z.infer<typeof DeleteElasticCacheUserSchema>;
|
||||||
|
|
||||||
|
const ElastiCacheUserManager = (credentials: TBasicAWSCredentials, region: string) => {
|
||||||
|
const elastiCache = new ElastiCache({
|
||||||
|
region,
|
||||||
|
credentials
|
||||||
|
});
|
||||||
|
const infisicalGroup = "infisical-managed-group-elasticache";
|
||||||
|
|
||||||
|
const ensureInfisicalGroupExists = async (clusterName: string) => {
|
||||||
|
const replicationGroups = await elastiCache.send(new DescribeUserGroupsCommand());
|
||||||
|
|
||||||
|
const existingGroup = replicationGroups.UserGroups?.find((group) => group.UserGroupId === infisicalGroup);
|
||||||
|
|
||||||
|
let newlyCreatedGroup = false;
|
||||||
|
if (!existingGroup) {
|
||||||
|
const createGroupCommand = new CreateUserGroupCommand({
|
||||||
|
UserGroupId: infisicalGroup,
|
||||||
|
UserIds: ["default"],
|
||||||
|
Engine: "redis"
|
||||||
|
});
|
||||||
|
|
||||||
|
await elastiCache.send(createGroupCommand);
|
||||||
|
newlyCreatedGroup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingGroup || newlyCreatedGroup) {
|
||||||
|
const replicationGroup = (
|
||||||
|
await elastiCache.send(
|
||||||
|
new DescribeReplicationGroupsCommand({
|
||||||
|
ReplicationGroupId: clusterName
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).ReplicationGroups?.[0];
|
||||||
|
|
||||||
|
if (!replicationGroup?.UserGroupIds?.includes(infisicalGroup)) {
|
||||||
|
// If the replication group doesn't have the infisical user group, we need to associate it
|
||||||
|
const modifyGroupCommand = new ModifyReplicationGroupCommand({
|
||||||
|
UserGroupIdsToAdd: [infisicalGroup],
|
||||||
|
UserGroupIdsToRemove: [],
|
||||||
|
ApplyImmediately: true,
|
||||||
|
ReplicationGroupId: clusterName
|
||||||
|
});
|
||||||
|
await elastiCache.send(modifyGroupCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addUserToInfisicalGroup = async (userId: string) => {
|
||||||
|
// figure out if the default user is already in the group, if it is, then we shouldn't add it again
|
||||||
|
|
||||||
|
const addUserToGroupCommand = new ModifyUserGroupCommand({
|
||||||
|
UserGroupId: infisicalGroup,
|
||||||
|
UserIdsToAdd: [userId],
|
||||||
|
UserIdsToRemove: []
|
||||||
|
});
|
||||||
|
|
||||||
|
await elastiCache.send(addUserToGroupCommand);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createUser = async (creationInput: TCreateElastiCacheUserInput, clusterName: string) => {
|
||||||
|
await ensureInfisicalGroupExists(clusterName);
|
||||||
|
|
||||||
|
await elastiCache.send(new CreateUserCommand(creationInput)); // First create the user
|
||||||
|
await addUserToInfisicalGroup(creationInput.UserId); // Then add the user to the group. We know the group is already a part of the cluster because of ensureInfisicalGroupExists()
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId: creationInput.UserId,
|
||||||
|
password: creationInput.Passwords[0]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteUser = async (
|
||||||
|
deletionInput: TDeleteElastiCacheUserInput
|
||||||
|
): Promise<Pick<TElastiCacheRedisUser, "userId">> => {
|
||||||
|
await elastiCache.send(new DeleteUserCommand(deletionInput));
|
||||||
|
return { userId: deletionInput.UserId };
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyCredentials = async (clusterName: string) => {
|
||||||
|
await elastiCache.send(
|
||||||
|
new DescribeReplicationGroupsCommand({
|
||||||
|
ReplicationGroupId: clusterName
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
createUser,
|
||||||
|
deleteUser,
|
||||||
|
verifyCredentials
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const generatePassword = () => {
|
const generatePassword = () => {
|
||||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#";
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#";
|
||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
@@ -20,12 +140,13 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = DynamicSecretAwsElastiCacheSchema.parse(inputs);
|
const providerInputs = DynamicSecretAwsElastiCacheSchema.parse(inputs);
|
||||||
|
|
||||||
JSON.parse(providerInputs.creationStatement);
|
// We need to ensure the that the creation & revocation statements are valid and can be used to create and revoke users.
|
||||||
JSON.parse(providerInputs.revocationStatement);
|
// We can't return the parsed statements here because we need to use the handlebars template to generate the username and password, before we can use the parsed statements.
|
||||||
|
CreateElastiCacheUserSchema.parse(JSON.parse(providerInputs.creationStatement));
|
||||||
|
DeleteElasticCacheUserSchema.parse(JSON.parse(providerInputs.revocationStatement));
|
||||||
|
|
||||||
return providerInputs;
|
return providerInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateConnection = async (inputs: unknown) => {
|
const validateConnection = async (inputs: unknown) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
await ElastiCacheUserManager(
|
await ElastiCacheUserManager(
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
import {
|
|
||||||
CreateUserCommand,
|
|
||||||
CreateUserGroupCommand,
|
|
||||||
DeleteUserCommand,
|
|
||||||
DescribeReplicationGroupsCommand,
|
|
||||||
DescribeUserGroupsCommand,
|
|
||||||
ElastiCache,
|
|
||||||
ModifyReplicationGroupCommand,
|
|
||||||
ModifyUserGroupCommand
|
|
||||||
} from "@aws-sdk/client-elasticache";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
type TElastiCacheRedisUser = {
|
|
||||||
userId: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TBasicAWSCredentials = { accessKeyId: string; secretAccessKey: string };
|
|
||||||
|
|
||||||
export const CreateElastiCacheUserSchema = z.object({
|
|
||||||
UserId: z.string().trim().min(1),
|
|
||||||
UserName: z.string().trim().min(1),
|
|
||||||
Engine: z.string().default("redis"),
|
|
||||||
Passwords: z.array(z.string().trim().min(1)).min(1).max(1), // Minimum password length is 16 characters, required by AWS.
|
|
||||||
AccessString: z.string().trim().min(1) // Example: "on ~* +@all"
|
|
||||||
});
|
|
||||||
|
|
||||||
export const DeleteElasticCacheUserSchema = z.object({
|
|
||||||
UserId: z.string().trim().min(1)
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TCreateElastiCacheUserInput = z.infer<typeof CreateElastiCacheUserSchema>;
|
|
||||||
export type TDeleteElastiCacheUserInput = z.infer<typeof DeleteElasticCacheUserSchema>;
|
|
||||||
|
|
||||||
export const ElastiCacheUserManager = (credentials: TBasicAWSCredentials, region: string) => {
|
|
||||||
const elastiCache = new ElastiCache({
|
|
||||||
region,
|
|
||||||
credentials
|
|
||||||
});
|
|
||||||
const infisicalGroup = "infisical-managed-group-elasticache";
|
|
||||||
|
|
||||||
const ensureInfisicalGroupExists = async (clusterName: string) => {
|
|
||||||
const replicationGroups = await elastiCache.send(new DescribeUserGroupsCommand());
|
|
||||||
|
|
||||||
const existingGroup = replicationGroups.UserGroups?.find((group) => group.UserGroupId === infisicalGroup);
|
|
||||||
|
|
||||||
let newlyCreatedGroup = false;
|
|
||||||
if (!existingGroup) {
|
|
||||||
const createGroupCommand = new CreateUserGroupCommand({
|
|
||||||
UserGroupId: infisicalGroup,
|
|
||||||
UserIds: ["default"],
|
|
||||||
Engine: "redis"
|
|
||||||
});
|
|
||||||
|
|
||||||
await elastiCache.send(createGroupCommand);
|
|
||||||
newlyCreatedGroup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingGroup || newlyCreatedGroup) {
|
|
||||||
const replicationGroup = (
|
|
||||||
await elastiCache.send(
|
|
||||||
new DescribeReplicationGroupsCommand({
|
|
||||||
ReplicationGroupId: clusterName
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).ReplicationGroups?.[0];
|
|
||||||
|
|
||||||
if (!replicationGroup?.UserGroupIds?.includes(infisicalGroup)) {
|
|
||||||
// If the replication group doesn't have the infisical user group, we need to associate it
|
|
||||||
const modifyGroupCommand = new ModifyReplicationGroupCommand({
|
|
||||||
UserGroupIdsToAdd: [infisicalGroup],
|
|
||||||
UserGroupIdsToRemove: [],
|
|
||||||
ApplyImmediately: true,
|
|
||||||
ReplicationGroupId: clusterName
|
|
||||||
});
|
|
||||||
await elastiCache.send(modifyGroupCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const addUserToInfisicalGroup = async (userId: string) => {
|
|
||||||
// figure out if the default user is already in the group, if it is, then we shouldn't add it again
|
|
||||||
|
|
||||||
const addUserToGroupCommand = new ModifyUserGroupCommand({
|
|
||||||
UserGroupId: infisicalGroup,
|
|
||||||
UserIdsToAdd: [userId],
|
|
||||||
UserIdsToRemove: []
|
|
||||||
});
|
|
||||||
|
|
||||||
await elastiCache.send(addUserToGroupCommand);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createUser = async (creationInput: TCreateElastiCacheUserInput, clusterName: string) => {
|
|
||||||
await ensureInfisicalGroupExists(clusterName);
|
|
||||||
|
|
||||||
await elastiCache.send(new CreateUserCommand(creationInput)); // First create the user
|
|
||||||
await addUserToInfisicalGroup(creationInput.UserId); // Then add the user to the group. We know the group is already a part of the cluster because of ensureInfisicalGroupExists()
|
|
||||||
|
|
||||||
return {
|
|
||||||
userId: creationInput.UserId,
|
|
||||||
password: creationInput.Passwords[0]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteUser = async (
|
|
||||||
deletionInput: TDeleteElastiCacheUserInput
|
|
||||||
): Promise<Pick<TElastiCacheRedisUser, "userId">> => {
|
|
||||||
await elastiCache.send(new DeleteUserCommand(deletionInput));
|
|
||||||
return { userId: deletionInput.UserId };
|
|
||||||
};
|
|
||||||
|
|
||||||
const verifyCredentials = async (clusterName: string) => {
|
|
||||||
await elastiCache.send(
|
|
||||||
new DescribeReplicationGroupsCommand({
|
|
||||||
ReplicationGroupId: clusterName
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
createUser,
|
|
||||||
deleteUser,
|
|
||||||
verifyCredentials
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./elasticache";
|
|
||||||
Reference in New Issue
Block a user