mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #2641 from Infisical/feat/ldap-static-dynamic-secret
feat: static ldap credentials
This commit is contained in:
@@ -7,7 +7,7 @@ import { z } from "zod";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { LdapSchema, TDynamicProviderFns } from "./models";
|
||||
import { LdapCredentialType, LdapSchema, TDynamicProviderFns } from "./models";
|
||||
|
||||
const generatePassword = () => {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#";
|
||||
@@ -193,29 +193,76 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const client = await getClient(providerInputs);
|
||||
|
||||
const username = generateUsername();
|
||||
const password = generatePassword();
|
||||
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif });
|
||||
if (providerInputs.credentialType === LdapCredentialType.Static) {
|
||||
const dnMatch = providerInputs.rotationLdif.match(/^dn:\s*(.+)/m);
|
||||
|
||||
try {
|
||||
const dnArray = await executeLdif(client, generatedLdif);
|
||||
if (dnMatch) {
|
||||
const username = dnMatch[1];
|
||||
const password = generatePassword();
|
||||
|
||||
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
|
||||
} catch (err) {
|
||||
if (providerInputs.rollbackLdif) {
|
||||
const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif });
|
||||
await executeLdif(client, rollbackLdif);
|
||||
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rotationLdif });
|
||||
|
||||
try {
|
||||
const dnArray = await executeLdif(client, generatedLdif);
|
||||
|
||||
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
|
||||
} catch (err) {
|
||||
throw new BadRequestError({ message: (err as Error).message });
|
||||
}
|
||||
} else {
|
||||
throw new BadRequestError({
|
||||
message: "Invalid rotation LDIF, missing DN."
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const username = generateUsername();
|
||||
const password = generatePassword();
|
||||
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif });
|
||||
|
||||
try {
|
||||
const dnArray = await executeLdif(client, generatedLdif);
|
||||
|
||||
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
|
||||
} catch (err) {
|
||||
if (providerInputs.rollbackLdif) {
|
||||
const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif });
|
||||
await executeLdif(client, rollbackLdif);
|
||||
}
|
||||
throw new BadRequestError({ message: (err as Error).message });
|
||||
}
|
||||
throw new BadRequestError({ message: (err as Error).message });
|
||||
}
|
||||
};
|
||||
|
||||
const revoke = async (inputs: unknown, entityId: string) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const connection = await getClient(providerInputs);
|
||||
const client = await getClient(providerInputs);
|
||||
|
||||
if (providerInputs.credentialType === LdapCredentialType.Static) {
|
||||
const dnMatch = providerInputs.rotationLdif.match(/^dn:\s*(.+)/m);
|
||||
|
||||
if (dnMatch) {
|
||||
const username = dnMatch[1];
|
||||
const password = generatePassword();
|
||||
|
||||
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rotationLdif });
|
||||
|
||||
try {
|
||||
const dnArray = await executeLdif(client, generatedLdif);
|
||||
|
||||
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
|
||||
} catch (err) {
|
||||
throw new BadRequestError({ message: (err as Error).message });
|
||||
}
|
||||
} else {
|
||||
throw new BadRequestError({
|
||||
message: "Invalid rotation LDIF, missing DN."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const revocationLdif = generateLDIF({ username: entityId, ldifTemplate: providerInputs.revocationLdif });
|
||||
|
||||
await executeLdif(connection, revocationLdif);
|
||||
await executeLdif(client, revocationLdif);
|
||||
|
||||
return { entityId };
|
||||
};
|
||||
|
||||
@@ -12,6 +12,11 @@ export enum ElasticSearchAuthTypes {
|
||||
ApiKey = "api-key"
|
||||
}
|
||||
|
||||
export enum LdapCredentialType {
|
||||
Dynamic = "dynamic",
|
||||
Static = "static"
|
||||
}
|
||||
|
||||
export const DynamicSecretRedisDBSchema = z.object({
|
||||
host: z.string().trim().toLowerCase(),
|
||||
port: z.number(),
|
||||
@@ -195,16 +200,26 @@ export const AzureEntraIDSchema = z.object({
|
||||
clientSecret: z.string().trim().min(1)
|
||||
});
|
||||
|
||||
export const LdapSchema = z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
|
||||
creationLdif: z.string().min(1),
|
||||
revocationLdif: z.string().min(1),
|
||||
rollbackLdif: z.string().optional()
|
||||
});
|
||||
export const LdapSchema = z.union([
|
||||
z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
credentialType: z.literal(LdapCredentialType.Dynamic).optional().default(LdapCredentialType.Dynamic),
|
||||
creationLdif: z.string().min(1),
|
||||
revocationLdif: z.string().min(1),
|
||||
rollbackLdif: z.string().optional()
|
||||
}),
|
||||
z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
credentialType: z.literal(LdapCredentialType.Static),
|
||||
rotationLdif: z.string().min(1)
|
||||
})
|
||||
]);
|
||||
|
||||
export enum DynamicSecretProviders {
|
||||
SqlDatabase = "sql-database",
|
||||
|
||||
@@ -10,143 +10,253 @@ The Infisical LDAP dynamic secret allows you to generate user credentials on dem
|
||||
1. Create a user with the necessary permissions to create users in your LDAP server.
|
||||
2. Ensure your LDAP server is reachable via Infisical instance.
|
||||
|
||||
## Set up Dynamic Secrets with LDAP
|
||||
## Create LDAP Credentials
|
||||
|
||||
<Steps>
|
||||
<Step title="Open Secret Overview Dashboard">
|
||||
Open the Secret Overview dashboard and select the environment in which you would like to add a dynamic secret.
|
||||
</Step>
|
||||
<Step title="Click on the 'Add Dynamic Secret' button">
|
||||

|
||||
</Step>
|
||||
<Step title="Select 'LDAP'">
|
||||

|
||||
</Step>
|
||||
<Tabs>
|
||||
<Tab title="Dynamic">
|
||||
<Steps>
|
||||
<Step title="Open Secret Overview Dashboard">
|
||||
Open the Secret Overview dashboard and select the environment in which you would like to add a dynamic secret.
|
||||
</Step>
|
||||
<Step title="Click on the 'Add Dynamic Secret' button">
|
||||

|
||||
</Step>
|
||||
<Step title="Select 'LDAP'">
|
||||

|
||||
</Step>
|
||||
|
||||
<Step title="Provide the inputs for dynamic secret parameters">
|
||||
<ParamField path="Secret Name" type="string" required>
|
||||
Name by which you want the secret to be referenced
|
||||
</ParamField>
|
||||
<Step title="Provide the inputs for dynamic secret parameters">
|
||||
<ParamField path="Secret Name" type="string" required>
|
||||
Name by which you want the secret to be referenced
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="Default TTL" type="string" required>
|
||||
Default time-to-live for a generated secret (it is possible to modify this value when a secret is generate)
|
||||
</ParamField>
|
||||
<ParamField path="Default TTL" type="string" required>
|
||||
Default time-to-live for a generated secret (it is possible to modify this value when a secret is generate)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="Max TTL" type="string" required>
|
||||
Maximum time-to-live for a generated secret.
|
||||
</ParamField>
|
||||
<ParamField path="Max TTL" type="string" required>
|
||||
Maximum time-to-live for a generated secret.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="URL" type="string" required>
|
||||
LDAP url to connect to. _(Example: ldap://your-ldap-ip:389 or ldaps://domain:636)_
|
||||
</ParamField>
|
||||
<ParamField path="URL" type="string" required>
|
||||
LDAP url to connect to. _(Example: ldap://your-ldap-ip:389 or ldaps://domain:636)_
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="BIND DN" type="string" required>
|
||||
DN to bind to. This should have permissions to create a new users.
|
||||
</ParamField>
|
||||
<ParamField path="BIND DN" type="string" required>
|
||||
DN to bind to. This should have permissions to create a new users.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="BIND Password" type="string" required>
|
||||
Password for the given DN.
|
||||
</ParamField>
|
||||
<ParamField path="BIND Password" type="string" required>
|
||||
Password for the given DN.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="CA" type="text">
|
||||
CA certificate to use for TLS in case of a secure connection.
|
||||
</ParamField>
|
||||
<ParamField path="CA" type="text">
|
||||
CA certificate to use for TLS in case of a secure connection.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="Creation LDIF" type="text" required>
|
||||
LDIF to run while creating a user in LDAP. This can include extra steps to assign the user to groups or set permissions.
|
||||
Here `{{Username}}`, `{{Password}}` and `{{EncodedPassword}}` are templatized variables for the username and password generated by the dynamic secret.
|
||||
<ParamField path="Credential Type" type="enum">
|
||||
The type of LDAP credential - select Dynamic.
|
||||
</ParamField>
|
||||
|
||||
`{{EncodedPassword}}` is the encoded password required for the `unicodePwd` field in Active Directory as described [here](https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/change-windows-active-directory-user-password).
|
||||
<ParamField path="Creation LDIF" type="text" required>
|
||||
LDIF to run while creating a user in LDAP. This can include extra steps to assign the user to groups or set permissions.
|
||||
Here `{{Username}}`, `{{Password}}` and `{{EncodedPassword}}` are templatized variables for the username and password generated by the dynamic secret.
|
||||
|
||||
**OpenLDAP** Example:
|
||||
```
|
||||
dn: uid={{Username}},dc=infisical,dc=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
cn: John Doe
|
||||
sn: Doe
|
||||
uid: jdoe
|
||||
mail: jdoe@infisical.com
|
||||
userPassword: {{Password}}
|
||||
```
|
||||
`{{EncodedPassword}}` is the encoded password required for the `unicodePwd` field in Active Directory as described [here](https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/change-windows-active-directory-user-password).
|
||||
|
||||
**Active Directory** Example:
|
||||
```
|
||||
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: user
|
||||
userPrincipalName: {{Username}}@infisical.com
|
||||
sAMAccountName: {{Username}}
|
||||
unicodePwd::{{EncodedPassword}}
|
||||
userAccountControl: 66048
|
||||
**OpenLDAP** Example:
|
||||
```
|
||||
dn: uid={{Username}},dc=infisical,dc=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
cn: John Doe
|
||||
sn: Doe
|
||||
uid: jdoe
|
||||
mail: jdoe@infisical.com
|
||||
userPassword: {{Password}}
|
||||
```
|
||||
|
||||
dn: CN=test-group,OU=Test Create,DC=infisical,DC=com
|
||||
changetype: modify
|
||||
add: member
|
||||
member: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
-
|
||||
```
|
||||
</ParamField>
|
||||
**Active Directory** Example:
|
||||
```
|
||||
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: user
|
||||
userPrincipalName: {{Username}}@infisical.com
|
||||
sAMAccountName: {{Username}}
|
||||
unicodePwd::{{EncodedPassword}}
|
||||
userAccountControl: 66048
|
||||
|
||||
<ParamField path="Revocation LDIF" type="text" required>
|
||||
LDIF to run while revoking a user in LDAP. This can include extra steps to remove the user from groups or set permissions.
|
||||
Here `{{Username}}` is a templatized variable for the username generated by the dynamic secret.
|
||||
dn: CN=test-group,OU=Test Create,DC=infisical,DC=com
|
||||
changetype: modify
|
||||
add: member
|
||||
member: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
-
|
||||
```
|
||||
</ParamField>
|
||||
|
||||
**OpenLDAP / Active Directory** Example:
|
||||
```
|
||||
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
changetype: delete
|
||||
```
|
||||
</ParamField>
|
||||
<ParamField path="Revocation LDIF" type="text" required>
|
||||
LDIF to run while revoking a user in LDAP. This can include extra steps to remove the user from groups or set permissions.
|
||||
Here `{{Username}}` is a templatized variable for the username generated by the dynamic secret.
|
||||
|
||||
<ParamField path="Rollback LDIF" type="text">
|
||||
LDIF to run incase Creation LDIF fails midway.
|
||||
**OpenLDAP / Active Directory** Example:
|
||||
```
|
||||
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
changetype: delete
|
||||
```
|
||||
</ParamField>
|
||||
|
||||
For the creation example shown above, if the user is created successfully but not added to a group, this LDIF can be used to remove the user.
|
||||
Here `{{Username}}`, `{{Password}}` and `{{EncodedPassword}}` are templatized variables for the username generated by the dynamic secret.
|
||||
<ParamField path="Rollback LDIF" type="text">
|
||||
LDIF to run incase Creation LDIF fails midway.
|
||||
|
||||
**OpenLDAP / Active Directory** Example:
|
||||
```
|
||||
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
changetype: delete
|
||||
```
|
||||
</ParamField>
|
||||
For the creation example shown above, if the user is created successfully but not added to a group, this LDIF can be used to remove the user.
|
||||
Here `{{Username}}`, `{{Password}}` and `{{EncodedPassword}}` are templatized variables for the username generated by the dynamic secret.
|
||||
|
||||
</Step>
|
||||
**OpenLDAP / Active Directory** Example:
|
||||
```
|
||||
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||
changetype: delete
|
||||
```
|
||||
</ParamField>
|
||||
</Step>
|
||||
|
||||
<Step title="Click `Submit`">
|
||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||
</Step>
|
||||
<Step title="Generate dynamic secrets">
|
||||
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
|
||||
To do this, simply click on the 'Generate' button which appears when hovering over the dynamic secret item.
|
||||
Alternatively, you can initiate the creation of a new lease by selecting 'New Lease' from the dynamic secret lease list section.
|
||||
<Step title="Click `Submit`">
|
||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||
</Step>
|
||||
<Step title="Generate dynamic secrets">
|
||||
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
|
||||
To do this, simply click on the 'Generate' button which appears when hovering over the dynamic secret item.
|
||||
Alternatively, you can initiate the creation of a new lease by selecting 'New Lease' from the dynamic secret lease list section.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for.
|
||||
When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for.
|
||||
|
||||

|
||||

|
||||
|
||||
<Tip>
|
||||
Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret.
|
||||
</Tip>
|
||||
<Tip>
|
||||
Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret.
|
||||
</Tip>
|
||||
|
||||
|
||||
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you with an array of DN's altered depending on the Creation LDIF.
|
||||
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you with an array of DN's altered depending on the Creation LDIF.
|
||||
|
||||

|
||||

|
||||
</Step>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="Static">
|
||||
<Steps>
|
||||
<Step title="Open Secret Overview Dashboard">
|
||||
Open the Secret Overview dashboard and select the environment in which you would like to add a dynamic secret.
|
||||
</Step>
|
||||
<Step title="Click on the 'Add Dynamic Secret' button">
|
||||

|
||||
</Step>
|
||||
<Step title="Select 'LDAP'">
|
||||

|
||||
</Step>
|
||||
|
||||
<Step title="Provide the inputs for dynamic secret parameters">
|
||||
<ParamField path="Secret Name" type="string" required>
|
||||
Name by which you want the secret to be referenced
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="Default TTL" type="string" required>
|
||||
Default time-to-live for a generated secret (it is possible to modify this value when a secret is generate)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="Max TTL" type="string" required>
|
||||
Maximum time-to-live for a generated secret.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="URL" type="string" required>
|
||||
LDAP url to connect to. _(Example: ldap://your-ldap-ip:389 or ldaps://domain:636)_
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="BIND DN" type="string" required>
|
||||
DN to bind to. This should have permissions to create a new users.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="BIND Password" type="string" required>
|
||||
Password for the given DN.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="CA" type="text">
|
||||
CA certificate to use for TLS in case of a secure connection.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="Credential Type" type="enum">
|
||||
The type of LDAP credential - select Static.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="Rotation LDIF" type="text" required>
|
||||
LDIF to run for rotating the credentals of an LDAP user. This can include extra LDAP steps based on your needs.
|
||||
Here `{{Password}}` and `{{EncodedPassword}}` are templatized variables for the password generated by the dynamic secret.
|
||||
|
||||
Note that the `-` characters and the empty lines found at the end of the examples are necessary based on the LDIF format.
|
||||
|
||||
**OpenLDAP** Example:
|
||||
```
|
||||
dn: cn=sheencaps capadngan,ou=people,dc=acme,dc=com
|
||||
changetype: modify
|
||||
replace: userPassword
|
||||
password: {{Password}}
|
||||
-
|
||||
|
||||
```
|
||||
|
||||
**Active Directory** Example:
|
||||
```
|
||||
dn: cn=sheencaps capadngan,ou=people,dc=acme,dc=com
|
||||
changetype: modify
|
||||
replace: unicodePwd
|
||||
unicodePwd::{{EncodedPassword}}
|
||||
-
|
||||
|
||||
```
|
||||
`{{EncodedPassword}}` is the encoded password required for the `unicodePwd` field in Active Directory as described [here](https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/change-windows-active-directory-user-password).
|
||||
|
||||
</ParamField>
|
||||
</Step>
|
||||
|
||||
<Step title="Click `Submit`">
|
||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||
</Step>
|
||||
<Step title="Generate dynamic secrets">
|
||||
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
|
||||
To do this, simply click on the 'Generate' button which appears when hovering over the dynamic secret item.
|
||||
Alternatively, you can initiate the creation of a new lease by selecting 'New Lease' from the dynamic secret lease list section.
|
||||
|
||||

|
||||

|
||||
|
||||
When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for.
|
||||
|
||||

|
||||
|
||||
<Tip>
|
||||
Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret.
|
||||
</Tip>
|
||||
|
||||
|
||||
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you with an array of DN's altered depending on the Creation LDIF.
|
||||
|
||||

|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Active Directory Integration
|
||||
|
||||
|
||||
@@ -199,9 +199,11 @@ export type TDynamicSecretProvider =
|
||||
binddn: string;
|
||||
bindpass: string;
|
||||
ca?: string | undefined;
|
||||
creationLdif: string;
|
||||
revocationLdif: string;
|
||||
credentialType: string;
|
||||
creationLdif?: string;
|
||||
revocationLdif?: string;
|
||||
rollbackLdif?: string;
|
||||
rotationLdif?: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
|
||||
@@ -8,21 +8,47 @@ import { z } from "zod";
|
||||
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, TextArea } from "@app/components/v2";
|
||||
import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
enum CredentialType {
|
||||
Dynamic = "dynamic",
|
||||
Static = "static"
|
||||
}
|
||||
|
||||
creationLdif: z.string().min(1),
|
||||
revocationLdif: z.string().min(1),
|
||||
rollbackLdif: z.string().optional()
|
||||
}),
|
||||
const credentialTypes = [
|
||||
{
|
||||
label: "Dynamic",
|
||||
value: CredentialType.Dynamic
|
||||
},
|
||||
{
|
||||
label: "Static",
|
||||
value: CredentialType.Static
|
||||
}
|
||||
] as const;
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.discriminatedUnion("credentialType", [
|
||||
z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
credentialType: z.literal(CredentialType.Dynamic),
|
||||
creationLdif: z.string().min(1),
|
||||
revocationLdif: z.string().min(1),
|
||||
rollbackLdif: z.string().optional()
|
||||
}),
|
||||
z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
credentialType: z.literal(CredentialType.Static),
|
||||
rotationLdif: z.string().min(1)
|
||||
})
|
||||
]),
|
||||
|
||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||
const valMs = ms(val);
|
||||
@@ -67,6 +93,8 @@ export const LdapInputForm = ({
|
||||
const {
|
||||
control,
|
||||
formState: { isSubmitting },
|
||||
setValue,
|
||||
watch,
|
||||
handleSubmit
|
||||
} = useForm<TForm>({
|
||||
resolver: zodResolver(formSchema),
|
||||
@@ -78,11 +106,14 @@ export const LdapInputForm = ({
|
||||
ca: "",
|
||||
creationLdif: "",
|
||||
revocationLdif: "",
|
||||
rollbackLdif: ""
|
||||
rollbackLdif: "",
|
||||
credentialType: CredentialType.Dynamic
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const selectedCredentialType = watch("provider.credentialType");
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
@@ -240,45 +271,106 @@ export const LdapInputForm = ({
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="provider.creationLdif"
|
||||
name="provider.credentialType"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Creation LDIF"
|
||||
label="Credential Type"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
className="w-full"
|
||||
>
|
||||
<TextArea {...field} />
|
||||
<Select
|
||||
defaultValue={field.value}
|
||||
{...field}
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
const ldifFields = [
|
||||
"provider.creationLdif",
|
||||
"provider.revocationLdif",
|
||||
"provider.rollbackLdif",
|
||||
"provider.rotationLdif"
|
||||
] as const;
|
||||
|
||||
ldifFields.forEach((f) => {
|
||||
setValue(f, "");
|
||||
});
|
||||
|
||||
field.onChange(e);
|
||||
}}
|
||||
>
|
||||
{credentialTypes.map((credentialType) => (
|
||||
<SelectItem
|
||||
value={credentialType.value}
|
||||
key={`credential-type-${credentialType.value}`}
|
||||
>
|
||||
{credentialType.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="provider.revocationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Revocation LDIF"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{selectedCredentialType === CredentialType.Dynamic && (
|
||||
<>
|
||||
<Controller
|
||||
control={control}
|
||||
name="provider.creationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Creation LDIF"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="provider.rollbackLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Rollback LDIF"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="provider.revocationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Revocation LDIF"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="provider.rollbackLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Rollback LDIF"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{selectedCredentialType === CredentialType.Static && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="provider.rotationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Rotation LDIF"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,22 +5,47 @@ import { z } from "zod";
|
||||
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, TextArea } from "@app/components/v2";
|
||||
import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2";
|
||||
import { useUpdateDynamicSecret } from "@app/hooks/api";
|
||||
import { TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
||||
|
||||
enum CredentialType {
|
||||
Dynamic = "dynamic",
|
||||
Static = "static"
|
||||
}
|
||||
|
||||
const credentialTypes = [
|
||||
{
|
||||
label: "Dynamic",
|
||||
value: CredentialType.Dynamic
|
||||
},
|
||||
{
|
||||
label: "Static",
|
||||
value: CredentialType.Static
|
||||
}
|
||||
] as const;
|
||||
|
||||
const formSchema = z.object({
|
||||
inputs: z
|
||||
.object({
|
||||
inputs: z.discriminatedUnion("credentialType", [
|
||||
z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
credentialType: z.literal(CredentialType.Dynamic),
|
||||
creationLdif: z.string().min(1),
|
||||
revocationLdif: z.string().min(1),
|
||||
rollbackLdif: z.string().optional()
|
||||
}),
|
||||
z.object({
|
||||
url: z.string().trim().min(1),
|
||||
binddn: z.string().trim().min(1),
|
||||
bindpass: z.string().trim().min(1),
|
||||
ca: z.string().optional(),
|
||||
credentialType: z.literal(CredentialType.Static),
|
||||
rotationLdif: z.string().min(1)
|
||||
})
|
||||
.partial(),
|
||||
]),
|
||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||
const valMs = ms(val);
|
||||
if (valMs < 60 * 1000)
|
||||
@@ -67,7 +92,9 @@ export const EditDynamicSecretLdapForm = ({
|
||||
const {
|
||||
control,
|
||||
formState: { isSubmitting },
|
||||
handleSubmit
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue
|
||||
} = useForm<TForm>({
|
||||
resolver: zodResolver(formSchema),
|
||||
values: {
|
||||
@@ -81,6 +108,7 @@ export const EditDynamicSecretLdapForm = ({
|
||||
});
|
||||
|
||||
const updateDynamicSecret = useUpdateDynamicSecret();
|
||||
const selectedCredentialType = watch("inputs.credentialType");
|
||||
|
||||
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
|
||||
// wait till previous request is finished
|
||||
@@ -204,43 +232,103 @@ export const EditDynamicSecretLdapForm = ({
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="inputs.creationLdif"
|
||||
name="inputs.credentialType"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Creation LDIF"
|
||||
isError={Boolean(error)}
|
||||
label="Credential Type"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
className="w-full"
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="inputs.revocationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Revocation LDIF"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="inputs.rollbackLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Rollback LDIF"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
<Select
|
||||
defaultValue={field.value}
|
||||
{...field}
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
const ldifFields = [
|
||||
"inputs.creationLdif",
|
||||
"inputs.revocationLdif",
|
||||
"inputs.rollbackLdif",
|
||||
"inputs.rotationLdif"
|
||||
] as const;
|
||||
|
||||
ldifFields.forEach((f) => {
|
||||
setValue(f, "");
|
||||
});
|
||||
|
||||
field.onChange(e);
|
||||
}}
|
||||
>
|
||||
{credentialTypes.map((credentialType) => (
|
||||
<SelectItem
|
||||
value={credentialType.value}
|
||||
key={`credential-type-${credentialType.value}`}
|
||||
>
|
||||
{credentialType.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{selectedCredentialType === CredentialType.Dynamic && (
|
||||
<>
|
||||
<Controller
|
||||
control={control}
|
||||
name="inputs.creationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Creation LDIF"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="inputs.revocationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Revocation LDIF"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="inputs.rollbackLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Rollback LDIF"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{selectedCredentialType === CredentialType.Static && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="inputs.rotationLdif"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Rotation LDIF"
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 flex items-center space-x-4">
|
||||
<Button type="submit" isLoading={isSubmitting}>
|
||||
|
||||
Reference in New Issue
Block a user