-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1714 from akhilmhdh/dynamic-secret/cassandra
Dynamic secret cassandra
- Loading branch information
Showing
21 changed files
with
3,046 additions
and
89 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
backend/src/ee/services/dynamic-secret/providers/cassandra.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import cassandra from "cassandra-driver"; | ||
import handlebars from "handlebars"; | ||
import { customAlphabet } from "nanoid"; | ||
import { z } from "zod"; | ||
|
||
import { BadRequestError } from "@app/lib/errors"; | ||
import { alphaNumericNanoId } from "@app/lib/nanoid"; | ||
|
||
import { DynamicSecretCassandraSchema, TDynamicProviderFns } from "./models"; | ||
|
||
const generatePassword = (size = 48) => { | ||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#"; | ||
return customAlphabet(charset, 48)(size); | ||
}; | ||
|
||
const generateUsername = () => { | ||
return alphaNumericNanoId(32); | ||
}; | ||
|
||
export const CassandraProvider = (): TDynamicProviderFns => { | ||
const validateProviderInputs = async (inputs: unknown) => { | ||
const providerInputs = await DynamicSecretCassandraSchema.parseAsync(inputs); | ||
if (providerInputs.host === "localhost" || providerInputs.host === "127.0.0.1") { | ||
throw new BadRequestError({ message: "Invalid db host" }); | ||
} | ||
|
||
return providerInputs; | ||
}; | ||
|
||
const getClient = async (providerInputs: z.infer<typeof DynamicSecretCassandraSchema>) => { | ||
const sslOptions = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined; | ||
const client = new cassandra.Client({ | ||
sslOptions, | ||
protocolOptions: { | ||
port: providerInputs.port | ||
}, | ||
credentials: { | ||
username: providerInputs.username, | ||
password: providerInputs.password | ||
}, | ||
keyspace: providerInputs.keyspace, | ||
localDataCenter: providerInputs?.localDataCenter, | ||
contactPoints: providerInputs.host.split(",").filter(Boolean) | ||
}); | ||
return client; | ||
}; | ||
|
||
const validateConnection = async (inputs: unknown) => { | ||
const providerInputs = await validateProviderInputs(inputs); | ||
const client = await getClient(providerInputs); | ||
|
||
const isConnected = await client.execute("SELECT * FROM system_schema.keyspaces").then(() => true); | ||
await client.shutdown(); | ||
return isConnected; | ||
}; | ||
|
||
const create = async (inputs: unknown, expireAt: number) => { | ||
const providerInputs = await validateProviderInputs(inputs); | ||
const client = await getClient(providerInputs); | ||
|
||
const username = generateUsername(); | ||
const password = generatePassword(); | ||
const { keyspace } = providerInputs; | ||
const expiration = new Date(expireAt).toISOString(); | ||
|
||
const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({ | ||
username, | ||
password, | ||
expiration, | ||
keyspace | ||
}); | ||
|
||
const queries = creationStatement.toString().split(";").filter(Boolean); | ||
for (const query of queries) { | ||
// eslint-disable-next-line | ||
await client.execute(query); | ||
} | ||
await client.shutdown(); | ||
|
||
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; | ||
}; | ||
|
||
const revoke = async (inputs: unknown, entityId: string) => { | ||
const providerInputs = await validateProviderInputs(inputs); | ||
const client = await getClient(providerInputs); | ||
|
||
const username = entityId; | ||
const { keyspace } = providerInputs; | ||
|
||
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username, keyspace }); | ||
const queries = revokeStatement.toString().split(";").filter(Boolean); | ||
for (const query of queries) { | ||
// eslint-disable-next-line | ||
await client.execute(query); | ||
} | ||
await client.shutdown(); | ||
return { entityId: username }; | ||
}; | ||
|
||
const renew = async (inputs: unknown, entityId: string, expireAt: number) => { | ||
const providerInputs = await validateProviderInputs(inputs); | ||
const client = await getClient(providerInputs); | ||
|
||
const username = entityId; | ||
const expiration = new Date(expireAt).toISOString(); | ||
const { keyspace } = providerInputs; | ||
|
||
const renewStatement = handlebars.compile(providerInputs.revocationStatement)({ username, keyspace, expiration }); | ||
const queries = renewStatement.toString().split(";").filter(Boolean); | ||
for (const query of queries) { | ||
// eslint-disable-next-line | ||
await client.execute(query); | ||
} | ||
await client.shutdown(); | ||
return { entityId: username }; | ||
}; | ||
|
||
return { | ||
validateProviderInputs, | ||
validateConnection, | ||
create, | ||
revoke, | ||
renew | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
import { CassandraProvider } from "./cassandra"; | ||
import { DynamicSecretProviders } from "./models"; | ||
import { SqlDatabaseProvider } from "./sql-database"; | ||
|
||
export const buildDynamicSecretProviders = () => ({ | ||
[DynamicSecretProviders.SqlDatabase]: SqlDatabaseProvider() | ||
[DynamicSecretProviders.SqlDatabase]: SqlDatabaseProvider(), | ||
[DynamicSecretProviders.Cassandra]: CassandraProvider() | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.