1
0
mirror of https://github.com/Infisical/infisical.git synced 2025-03-29 22:02:57 +00:00

Compare commits

..

37 Commits

Author SHA1 Message Date
e143a31e79 Merge pull request from JunedKhan101/main
docs:fixed broken link
2024-04-08 17:45:51 -07:00
f6cc20b08b remove link from docs 2024-04-08 12:00:11 -07:00
90e125454e remove docs for e2ee 2024-04-08 11:58:48 -07:00
fbdf3dc9ce Merge pull request from akhilmhdh/doc/integration-api-guide
docs: added guide to setup integration with api
2024-04-08 11:56:19 -07:00
f333c905d9 revise generic integration docs 2024-04-08 11:55:38 -07:00
71e60df39a Merge pull request from agilesyndrome/fix_universalAuth_operatorinstall
fix: Run make kubectl-install
2024-04-08 10:18:00 -07:00
8b4d050d05 updated original value in replace script 2024-04-08 10:15:09 -07:00
3b4bb591a3 set default for NEXT_PUBLIC_SAML_ORG_SLUG 2024-04-08 09:25:37 -07:00
54f1a4416b add default value 2024-04-08 09:06:42 -07:00
47e3f1b510 Merge pull request from Infisical/saml-auto-redirect
add automatic SAML redirect
2024-04-08 08:22:04 -07:00
5810b76027 docs:fixed broken link 2024-04-08 16:55:11 +05:30
246e6c64d1 Merge pull request from JunedKhan101/main
removed extra whitespace from error message
2024-04-07 16:05:31 -07:00
4e836c5dca removed extra whitespace from error message 2024-04-07 17:41:39 +05:30
63a289c3be add saml org clug to standalone 2024-04-06 11:58:50 -07:00
0a52bbd55d add render once to use effect 2024-04-06 11:40:13 -07:00
593bdf74b8 patch notice 2024-04-06 10:57:08 -07:00
1f3742e619 update april_2024_db_update_closed 2024-04-06 10:39:21 -07:00
d6e5ac2133 maintenance postponed 2024-04-06 10:01:13 -07:00
fea48518a3 removed new tag from identities 2024-04-05 19:01:54 -07:00
94d509eb01 fixed search bar with folders 2024-04-05 18:37:12 -07:00
055fd34c33 added baked env var 2024-04-05 17:25:25 -07:00
74fefa9879 add automatic SAML redirect 2024-04-05 14:39:31 -07:00
ff2c8d017f add automatic SAML redirect 2024-04-05 14:29:50 -07:00
ba1f8f4564 Merge pull request from Infisical/fix/delete-role-error
Fix: Error handling when deleting roles that are assigned to identities or users
2024-04-05 11:15:25 -07:00
e26df005c2 Fix: Typo 2024-04-05 11:11:32 -07:00
aca9b47f82 Fix: Typo 2024-04-05 11:11:26 -07:00
a16ce8899b Fix: Check for identities and project users who has the selected role before deleting 2024-04-05 11:11:15 -07:00
b61511d100 Update index.ts 2024-04-05 11:10:54 -07:00
a945bdfc4c update docs style 2024-04-05 10:07:42 -07:00
f6d7ec52c2 fix: Run make kubectl-install 2024-04-05 08:10:38 -04:00
3f6999b2e3 Merge pull request from Infisical/rate-limit
Add new rate limits for API
2024-04-04 19:53:31 -07:00
9128461409 Merge pull request from Infisical/daniel/delete-duplicate-org-memberships-migration
Feat: Delete duplicate memberships migration
2024-04-04 19:19:39 -07:00
893235c40f Update 20240405000045_org-memberships-unique-constraint.ts 2024-04-04 18:43:32 -07:00
e0f655ae30 Merge pull request from Infisical/fix/duplicate-org-memberships
Fix: Duplicate organization memberships
2024-04-04 17:10:55 -07:00
93aeca3a38 Fix: Add unique constraint on orgId and userId 2024-04-04 17:04:23 -07:00
1edebdf8a5 Fix: Improve create migration script 2024-04-04 17:04:06 -07:00
40bb9668fe docs: added guide to setup integration with api 2024-04-03 01:12:30 +05:30
26 changed files with 362 additions and 1074 deletions
Dockerfile.standalone-infisical
backend
docs
api-reference/overview/examples
documentation/platform
mint.json
self-hosting/configuration
style.css
frontend
Dockerfile
scripts
src
pages/org/[id]/overview
views
Login/components/InitialStep
Org/MembersPage
MembersPage.tsx
components/OrgRoleTabSection
Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList
SecretMainPage
SecretMainPage.tsx
components/FolderListView
k8-operator

@ -1,6 +1,7 @@
ARG POSTHOG_HOST=https://app.posthog.com
ARG POSTHOG_API_KEY=posthog-api-key
ARG INTERCOM_ID=intercom-id
ARG SAML_ORG_SLUG=saml-org-slug-default
FROM node:20-alpine AS base
@ -35,6 +36,8 @@ ARG INTERCOM_ID
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
ARG INFISICAL_PLATFORM_VERSION
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
ARG SAML_ORG_SLUG
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
# Build
RUN npm run build
@ -100,6 +103,9 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
ARG INTERCOM_ID=intercom-id
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
ARG SAML_ORG_SLUG
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
WORKDIR /

@ -7,10 +7,10 @@ const prompt = promptSync({ sigint: true });
const migrationName = prompt("Enter name for migration: ");
// Remove spaces from migration name and replace with hyphens
const formattedMigrationName = migrationName.replace(/\s+/g, "-");
execSync(
`npx knex migrate:make --knexfile ${path.join(
__dirname,
"../src/db/knexfile.ts"
)} -x ts ${migrationName}`,
`npx knex migrate:make --knexfile ${path.join(__dirname, "../src/db/knexfile.ts")} -x ts ${formattedMigrationName}`,
{ stdio: "inherit" }
);

@ -0,0 +1,111 @@
import { Knex } from "knex";
import { z } from "zod";
import { TableName, TOrgMemberships } from "../schemas";
const validateOrgMembership = (membershipToValidate: TOrgMemberships, firstMembership: TOrgMemberships) => {
const firstOrgId = firstMembership.orgId;
const firstUserId = firstMembership.userId;
if (membershipToValidate.id === firstMembership.id) {
return;
}
if (membershipToValidate.inviteEmail !== firstMembership.inviteEmail) {
throw new Error(`Invite emails are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.orgId !== firstMembership.orgId) {
throw new Error(`OrgIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.role !== firstMembership.role) {
throw new Error(`Roles are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.roleId !== firstMembership.roleId) {
throw new Error(`RoleIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.status !== firstMembership.status) {
throw new Error(`Statuses are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
if (membershipToValidate.userId !== firstMembership.userId) {
throw new Error(`UserIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
}
};
export async function up(knex: Knex): Promise<void> {
const RowSchema = z.object({
userId: z.string(),
orgId: z.string(),
cnt: z.string()
});
// Transactional find and delete duplicate rows
await knex.transaction(async (tx) => {
const duplicateRows = await tx(TableName.OrgMembership)
.select("userId", "orgId") // Select the userId and orgId so we can group by them
.count("* as cnt") // Count the number of rows for each userId and orgId, so we can make sure there are more than 1 row (a duplicate)
.groupBy("userId", "orgId")
.havingRaw("count(*) > ?", [1]); // Using havingRaw for direct SQL expressions
// Parse the rows to ensure they are in the correct format, and for type safety
const parsedRows = RowSchema.array().parse(duplicateRows);
// For each of the duplicate rows, loop through and find the actual memberships to delete
for (const row of parsedRows) {
const count = Number(row.cnt);
// An extra check to ensure that the count is actually a number, and the number is greater than 2
if (typeof count !== "number" || count < 2) {
// eslint-disable-next-line no-continue
continue;
}
// Find all the organization memberships that have the same userId and orgId
// eslint-disable-next-line no-await-in-loop
const rowsToDelete = await tx(TableName.OrgMembership).where({
userId: row.userId,
orgId: row.orgId
});
// Ensure that all the rows have exactly the same value, except id, createdAt, updatedAt
for (const rowToDelete of rowsToDelete) {
validateOrgMembership(rowToDelete, rowsToDelete[0]);
}
// Find the row with the latest createdAt, which we will keep
let lowestCreatedAt: number | null = null;
let latestCreatedRow: TOrgMemberships | null = null;
for (const rowToDelete of rowsToDelete) {
if (lowestCreatedAt === null || rowToDelete.createdAt.getTime() < lowestCreatedAt) {
lowestCreatedAt = rowToDelete.createdAt.getTime();
latestCreatedRow = rowToDelete;
}
}
if (!latestCreatedRow) {
throw new Error("Failed to find last created membership");
}
// Filter out the latest row from the rows to delete
const membershipIdsToDelete = rowsToDelete.map((r) => r.id).filter((id) => id !== latestCreatedRow!.id);
// eslint-disable-next-line no-await-in-loop
const numberOfRowsDeleted = await tx(TableName.OrgMembership).whereIn("id", membershipIdsToDelete).delete();
// eslint-disable-next-line no-console
console.log(
`Deleted ${numberOfRowsDeleted} duplicate organization memberships for ${row.userId} and ${row.orgId}`
);
}
});
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
table.unique(["userId", "orgId"]);
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
table.dropUnique(["userId", "orgId"]);
});
}

@ -411,7 +411,12 @@ export const registerRoutes = async (
folderDAL
});
const projectRoleService = projectRoleServiceFactory({ permissionService, projectRoleDAL });
const projectRoleService = projectRoleServiceFactory({
permissionService,
projectRoleDAL,
projectUserMembershipRoleDAL,
identityProjectMembershipRoleDAL
});
const snapshotService = secretSnapshotServiceFactory({
permissionService,

@ -153,7 +153,7 @@ export const authLoginServiceFactory = ({
username: email
});
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
throw new Error("Failed to find user");
throw new Error("Failed to find user");
}
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
validateProviderAuthToken(providerAuthToken as string, email);

@ -192,7 +192,7 @@ export const authPaswordServiceFactory = ({
}: TCreateBackupPrivateKeyDTO) => {
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
throw new Error("Failed to find user");
throw new Error("Failed to find user");
}
if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey) throw new Error("failed to create backup key");
@ -239,7 +239,7 @@ export const authPaswordServiceFactory = ({
const getBackupPrivateKeyOfUser = async (userId: string) => {
const user = await userDAL.findUserEncKeyByUserId(userId);
if (!user || (user && !user.isAccepted)) {
throw new Error("Failed to find user");
throw new Error("Failed to find user");
}
const backupKey = await authDAL.getBackupPrivateKeyByUserId(userId);
if (!backupKey) throw new Error("Failed to find user backup key");

@ -14,16 +14,25 @@ import {
import { BadRequestError } from "@app/lib/errors";
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal";
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
import { TProjectRoleDALFactory } from "./project-role-dal";
type TProjectRoleServiceFactoryDep = {
projectRoleDAL: TProjectRoleDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getUserProjectPermission">;
identityProjectMembershipRoleDAL: TIdentityProjectMembershipRoleDALFactory;
projectUserMembershipRoleDAL: TProjectUserMembershipRoleDALFactory;
};
export type TProjectRoleServiceFactory = ReturnType<typeof projectRoleServiceFactory>;
export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }: TProjectRoleServiceFactoryDep) => {
export const projectRoleServiceFactory = ({
projectRoleDAL,
permissionService,
identityProjectMembershipRoleDAL,
projectUserMembershipRoleDAL
}: TProjectRoleServiceFactoryDep) => {
const createRole = async (
actor: ActorType,
actorId: string,
@ -96,8 +105,25 @@ export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }:
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role);
const identityRole = await identityProjectMembershipRoleDAL.findOne({ customRoleId: roleId });
const projectUserRole = await projectUserMembershipRoleDAL.findOne({ customRoleId: roleId });
if (identityRole) {
throw new BadRequestError({
message: "The role is assigned to one or more identities. Make sure to unassign them before deleting the role.",
name: "Delete role"
});
}
if (projectUserRole) {
throw new BadRequestError({
message: "The role is assigned to one or more users. Make sure to unassign them before deleting the role.",
name: "Delete role"
});
}
const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId });
if (!deletedRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
if (!deletedRole) throw new BadRequestError({ message: "Role not found", name: "Delete role" });
return deletedRole;
};

@ -1,180 +0,0 @@
---
title: "E2EE Disabled"
---
Using Infisical's API to read/write secrets with E2EE disabled allows you to create, update, and retrieve secrets
in plaintext. Effectively, this means each such secret operation only requires 1 HTTP call.
<AccordionGroup>
<Accordion title="Retrieve secrets">
Retrieve all secrets for an Infisical project and environment.
<Tabs>
<Tab title="cURL">
```bash
curl --location --request GET 'https://app.infisical.com/api/v3/secrets/raw?environment=environment&workspaceId=workspaceId' \
--header 'Authorization: Bearer serviceToken'
```
</Tab>
</Tabs>
####
<Info>
When using a [service token](../../../documentation/platform/token) with access to a single environment and path, you don't need to provide request parameters because the server will automatically scope the request to the defined environment/secrets path of the service token used.
For all other cases, request parameters are required.
</Info>
####
<ParamField query="workspaceId" type="string" required>
The ID of the workspace
</ParamField>
<ParamField query="environment" type="string" required>
The environment slug
</ParamField>
<ParamField query="secretPath" type="string" default="/" optional>
Path to secrets in workspace
</ParamField>
</Accordion>
<Accordion title="Create secret">
Create a secret in Infisical.
<Tabs>
<Tab title="cURL">
```bash
curl --location --request POST 'https://app.infisical.com/api/v3/secrets/raw/secretName' \
--header 'Authorization: Bearer serviceToken' \
--header 'Content-Type: application/json' \
--data-raw '{
"workspaceId": "workspaceId",
"environment": "environment",
"type": "shared",
"secretValue": "secretValue",
"secretPath": "/"
}'
```
</Tab>
</Tabs>
<ParamField path="secretName" type="string" required>
Name of secret to create
</ParamField>
<ParamField body="workspaceId" type="string" required>
The ID of the workspace
</ParamField>
<ParamField body="environment" type="string" required>
The environment slug
</ParamField>
<ParamField body="secretValue" type="string" required>
Value of secret
</ParamField>
<ParamField body="secretComment" type="string" optional>
Comment of secret
</ParamField>
<ParamField body="secretPath" type="string" default="/" optional>
Path to secret in workspace
</ParamField>
<ParamField query="type" type="string" optional default="shared">
The type of the secret. Valid options are “shared” or “personal”
</ParamField>
</Accordion>
<Accordion title="Retrieve secret">
Retrieve a secret from Infisical.
<Tabs>
<Tab title="cURL">
```bash
curl --location --request GET 'https://app.infisical.com/api/v3/secrets/raw/secretName?workspaceId=workspaceId&environment=environment' \
--header 'Authorization: Bearer serviceToken'
```
</Tab>
</Tabs>
<ParamField path="secretName" type="string" required>
Name of secret to retrieve
</ParamField>
<ParamField query="workspaceId" type="string" required>
The ID of the workspace
</ParamField>
<ParamField query="environment" type="string" required>
The environment slug
</ParamField>
<ParamField query="secretPath" type="string" default="/" optional>
Path to secrets in workspace
</ParamField>
<ParamField query="type" type="string" optional default="personal">
The type of the secret. Valid options are “shared” or “personal”
</ParamField>
</Accordion>
<Accordion title="Update secret">
Update an existing secret in Infisical.
<Tabs>
<Tab title="cURL">
```bash
curl --location --request PATCH 'https://app.infisical.com/api/v3/secrets/raw/secretName' \
--header 'Authorization: Bearer serviceToken' \
--header 'Content-Type: application/json' \
--data-raw '{
"workspaceId": "workspaceId",
"environment": "environment",
"type": "shared",
"secretValue": "secretValue",
"secretPath": "/"
}'
```
</Tab>
</Tabs>
<ParamField path="secretName" type="string" required>
Name of secret to update
</ParamField>
<ParamField body="workspaceId" type="string" required>
The ID of the workspace
</ParamField>
<ParamField body="environment" type="string" required>
The environment slug
</ParamField>
<ParamField body="secretValue" type="string" required>
Value of secret
</ParamField>
<ParamField body="secretPath" type="string" default="/" optional>
Path to secret in workspace.
</ParamField>
<ParamField query="type" type="string" optional default="shared">
The type of the secret. Valid options are “shared” or “personal”
</ParamField>
</Accordion>
<Accordion title="Delete secret">
Delete a secret in Infisical.
<Tabs>
<Tab title="cURL">
```bash
curl --location --request DELETE 'https://app.infisical.com/api/v3/secrets/raw/secretName' \
--header 'Authorization: Bearer serviceToken' \
--header 'Content-Type: application/json' \
--data-raw '{
"workspaceId": "workspaceId",
"environment": "environment",
"type": "shared",
"secretPath": "/"
}'
```
</Tab>
</Tabs>
<ParamField path="secretName" type="string" required>
Name of secret to update
</ParamField>
<ParamField body="workspaceId" type="string" required>
The ID of the workspace
</ParamField>
<ParamField body="environment" type="string" required>
The environment slug
</ParamField>
<ParamField body="secretPath" type="string" default="/" optional>
Path to secret in workspace.
</ParamField>
<ParamField query="type" type="string" optional default="personal">
The type of the secret. Valid options are “shared” or “personal”
</ParamField>
</Accordion>
</AccordionGroup>

@ -1,862 +0,0 @@
---
title: "E2EE Enabled"
---
<Note>
E2EE enabled mode only works with [Service Tokens](/documentation/platform/token) and cannot be used with [Identities](/documentation/platform/identities/overview).
</Note>
Using Infisical's API to read/write secrets with E2EE enabled allows you to create, update, and retrieve secrets
but requires you to perform client-side encryption/decryption operations. For this reason, we recommend using one of the available
SDKs instead.
<AccordionGroup>
<Accordion title="Retrieve secrets">
<Tabs>
<Tab title="Javascript">
Retrieve all secrets for an Infisical project and environment.
```js
const crypto = require('crypto');
const axios = require('axios');
const BASE_URL = 'https://app.infisical.com';
const ALGORITHM = 'aes-256-gcm';
const decrypt = ({ ciphertext, iv, tag, secret}) => {
const decipher = crypto.createDecipheriv(
ALGORITHM,
secret,
Buffer.from(iv, 'base64')
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
cleartext += decipher.final('utf8');
return cleartext;
}
const getSecrets = async () => {
const serviceToken = 'your_service_token';
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
// 1. Get your Infisical Token data
const { data: serviceTokenData } = await axios.get(
`${BASE_URL}/api/v2/service-token`,
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
// 2. Get secrets for your project and environment
const { data } = await axios.get(
`${BASE_URL}/api/v3/secrets?${new URLSearchParams({
environment: serviceTokenData.environment,
workspaceId: serviceTokenData.workspace
})}`,
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
const encryptedSecrets = data.secrets;
// 3. Decrypt the (encrypted) project key with the key from your Infisical Token
const projectKey = decrypt({
ciphertext: serviceTokenData.encryptedKey,
iv: serviceTokenData.iv,
tag: serviceTokenData.tag,
secret: serviceTokenSecret
});
// 4. Decrypt the (encrypted) secrets
const secrets = encryptedSecrets.map((secret) => {
const secretKey = decrypt({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
secret: projectKey
});
const secretValue = decrypt({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
secret: projectKey
});
return ({
secretKey,
secretValue
});
});
console.log('secrets: ', secrets);
}
getSecrets();
```
</Tab>
<Tab title="Python">
```Python
import requests
import base64
from Cryptodome.Cipher import AES
BASE_URL = "http://app.infisical.com"
def decrypt(ciphertext, iv, tag, secret):
secret = bytes(secret, "utf-8")
iv = base64.standard_b64decode(iv)
tag = base64.standard_b64decode(tag)
ciphertext = base64.standard_b64decode(ciphertext)
cipher = AES.new(secret, AES.MODE_GCM, iv)
cipher.update(tag)
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
return cleartext
def get_secrets():
service_token = "your_service_token"
service_token_secret = service_token[service_token.rindex(".") + 1 :]
# 1. Get your Infisical Token data
service_token_data = requests.get(
f"{BASE_URL}/api/v2/service-token",
headers={"Authorization": f"Bearer {service_token}"},
).json()
# 2. Get secrets for your project and environment
data = requests.get(
f"{BASE_URL}/api/v3/secrets",
params={
"environment": service_token_data["environment"],
"workspaceId": service_token_data["workspace"],
},
headers={"Authorization": f"Bearer {service_token}"},
).json()
encrypted_secrets = data["secrets"]
# 3. Decrypt the (encrypted) project key with the key from your Infisical Token
project_key = decrypt(
ciphertext=service_token_data["encryptedKey"],
iv=service_token_data["iv"],
tag=service_token_data["tag"],
secret=service_token_secret,
)
# 4. Decrypt the (encrypted) secrets
secrets = []
for secret in encrypted_secrets:
secret_key = decrypt(
ciphertext=secret["secretKeyCiphertext"],
iv=secret["secretKeyIV"],
tag=secret["secretKeyTag"],
secret=project_key,
)
secret_value = decrypt(
ciphertext=secret["secretValueCiphertext"],
iv=secret["secretValueIV"],
tag=secret["secretValueTag"],
secret=project_key,
)
secrets.append(
{
"secret_key": secret_key,
"secret_value": secret_value,
}
)
print("secrets:", secrets)
get_secrets()
```
</Tab>
</Tabs>
</Accordion>
<Accordion title="Create secret">
<Tabs>
<Tab title="Javascript">
Create a secret in Infisical.
```js
const crypto = require('crypto');
const axios = require('axios');
const nacl = require('tweetnacl');
const BASE_URL = 'https://app.infisical.com';
const ALGORITHM = 'aes-256-gcm';
const BLOCK_SIZE_BYTES = 16;
const encrypt = ({ text, secret }) => {
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
let ciphertext = cipher.update(text, 'utf8', 'base64');
ciphertext += cipher.final('base64');
return {
ciphertext,
iv: iv.toString('base64'),
tag: cipher.getAuthTag().toString('base64')
};
}
const decrypt = ({ ciphertext, iv, tag, secret}) => {
const decipher = crypto.createDecipheriv(
ALGORITHM,
secret,
Buffer.from(iv, 'base64')
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
cleartext += decipher.final('utf8');
return cleartext;
}
const createSecrets = async () => {
const serviceToken = '';
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
const secretType = 'shared'; // 'shared' or 'personal'
const secretKey = 'some_key';
const secretValue = 'some_value';
const secretComment = 'some_comment';
// 1. Get your Infisical Token data
const { data: serviceTokenData } = await axios.get(
`${BASE_URL}/api/v2/service-token`,
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
// 2. Decrypt the (encrypted) project key with the key from your Infisical Token
const projectKey = decrypt({
ciphertext: serviceTokenData.encryptedKey,
iv: serviceTokenData.iv,
tag: serviceTokenData.tag,
secret: serviceTokenSecret
});
// 3. Encrypt your secret with the project key
const {
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
tag: secretKeyTag
} = encrypt({
text: secretKey,
secret: projectKey
});
const {
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag
} = encrypt({
text: secretValue,
secret: projectKey
});
const {
ciphertext: secretCommentCiphertext,
iv: secretCommentIV,
tag: secretCommentTag
} = encrypt({
text: secretComment,
secret: projectKey
});
// 4. Send (encrypted) secret to Infisical
await axios.post(
`${BASE_URL}/api/v3/secrets/${secretKey}`,
{
workspaceId: serviceTokenData.workspace,
environment: serviceTokenData.environment,
type: secretType,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
},
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
}
createSecrets();
```
</Tab>
<Tab title="Python">
```Python
import base64
import requests
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
BASE_URL = "https://app.infisical.com"
BLOCK_SIZE_BYTES = 16
def encrypt(text, secret):
iv = get_random_bytes(BLOCK_SIZE_BYTES)
secret = bytes(secret, "utf-8")
cipher = AES.new(secret, AES.MODE_GCM, iv)
ciphertext, tag = cipher.encrypt_and_digest(text.encode("utf-8"))
return {
"ciphertext": base64.standard_b64encode(ciphertext).decode("utf-8"),
"tag": base64.standard_b64encode(tag).decode("utf-8"),
"iv": base64.standard_b64encode(iv).decode("utf-8"),
}
def decrypt(ciphertext, iv, tag, secret):
secret = bytes(secret, "utf-8")
iv = base64.standard_b64decode(iv)
tag = base64.standard_b64decode(tag)
ciphertext = base64.standard_b64decode(ciphertext)
cipher = AES.new(secret, AES.MODE_GCM, iv)
cipher.update(tag)
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
return cleartext
def create_secrets():
service_token = "your_service_token"
service_token_secret = service_token[service_token.rindex(".") + 1 :]
secret_type = "shared" # "shared or "personal"
secret_key = "some_key"
secret_value = "some_value"
secret_comment = "some_comment"
# 1. Get your Infisical Token data
service_token_data = requests.get(
f"{BASE_URL}/api/v2/service-token",
headers={"Authorization": f"Bearer {service_token}"},
).json()
# 2. Decrypt the (encrypted) project key with the key from your Infisical Token
project_key = decrypt(
ciphertext=service_token_data["encryptedKey"],
iv=service_token_data["iv"],
tag=service_token_data["tag"],
secret=service_token_secret,
)
# 3. Encrypt your secret with the project key
encrypted_key_data = encrypt(text=secret_key, secret=project_key)
encrypted_value_data = encrypt(text=secret_value, secret=project_key)
encrypted_comment_data = encrypt(text=secret_comment, secret=project_key)
# 4. Send (encrypted) secret to Infisical
requests.post(
f"{BASE_URL}/api/v3/secrets/{secret_key}",
json={
"workspaceId": service_token_data["workspace"],
"environment": service_token_data["environment"],
"type": secret_type,
"secretKeyCiphertext": encrypted_key_data["ciphertext"],
"secretKeyIV": encrypted_key_data["iv"],
"secretKeyTag": encrypted_key_data["tag"],
"secretValueCiphertext": encrypted_value_data["ciphertext"],
"secretValueIV": encrypted_value_data["iv"],
"secretValueTag": encrypted_value_data["tag"],
"secretCommentCiphertext": encrypted_comment_data["ciphertext"],
"secretCommentIV": encrypted_comment_data["iv"],
"secretCommentTag": encrypted_comment_data["tag"]
},
headers={"Authorization": f"Bearer {service_token}"},
)
create_secrets()
```
</Tab>
</Tabs>
</Accordion>
<Accordion title="Retrieve secret">
<Tabs>
<Tab title="Javascript">
Retrieve a secret from Infisical.
```js
const crypto = require('crypto');
const axios = require('axios');
const BASE_URL = 'https://app.infisical.com';
const ALGORITHM = 'aes-256-gcm';
const decrypt = ({ ciphertext, iv, tag, secret}) => {
const decipher = crypto.createDecipheriv(
ALGORITHM,
secret,
Buffer.from(iv, 'base64')
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
cleartext += decipher.final('utf8');
return cleartext;
}
const getSecret = async () => {
const serviceToken = 'your_service_token';
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
const secretType = 'shared' // 'shared' or 'personal'
const secretKey = 'some_key';
// 1. Get your Infisical Token data
const { data: serviceTokenData } = await axios.get(
`${BASE_URL}/api/v2/service-token`,
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
// 2. Get the secret from your project and environment
const { data } = await axios.get(
`${BASE_URL}/api/v3/secrets/${secretKey}?${new URLSearchParams({
environment: serviceTokenData.environment,
workspaceId: serviceTokenData.workspace,
type: secretType // optional, defaults to 'shared'
})}`,
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
const encryptedSecret = data.secret;
// 3. Decrypt the (encrypted) project key with the key from your Infisical Token
const projectKey = decrypt({
ciphertext: serviceTokenData.encryptedKey,
iv: serviceTokenData.iv,
tag: serviceTokenData.tag,
secret: serviceTokenSecret
});
// 4. Decrypt the (encrypted) secret value
const secretValue = decrypt({
ciphertext: encryptedSecret.secretValueCiphertext,
iv: encryptedSecret.secretValueIV,
tag: encryptedSecret.secretValueTag,
secret: projectKey
});
console.log('secret: ', ({
secretKey,
secretValue
}));
}
getSecret();
```
</Tab>
<Tab title="Python">
```Python
import requests
import base64
from Cryptodome.Cipher import AES
BASE_URL = "http://app.infisical.com"
def decrypt(ciphertext, iv, tag, secret):
secret = bytes(secret, "utf-8")
iv = base64.standard_b64decode(iv)
tag = base64.standard_b64decode(tag)
ciphertext = base64.standard_b64decode(ciphertext)
cipher = AES.new(secret, AES.MODE_GCM, iv)
cipher.update(tag)
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
return cleartext
def get_secret():
service_token = "your_service_token"
service_token_secret = service_token[service_token.rindex(".") + 1 :]
secret_type = "shared" # "shared" or "personal"
secret_key = "some_key"
# 1. Get your Infisical Token data
service_token_data = requests.get(
f"{BASE_URL}/api/v2/service-token",
headers={"Authorization": f"Bearer {service_token}"},
).json()
# 2. Get secret from your project and environment
data = requests.get(
f"{BASE_URL}/api/v3/secrets/{secret_key}",
params={
"environment": service_token_data["environment"],
"workspaceId": service_token_data["workspace"],
"type": secret_type # optional, defaults to "shared"
},
headers={"Authorization": f"Bearer {service_token}"},
).json()
encrypted_secret = data["secret"]
# 3. Decrypt the (encrypted) project key with the key from your Infisical Token
project_key = decrypt(
ciphertext=service_token_data["encryptedKey"],
iv=service_token_data["iv"],
tag=service_token_data["tag"],
secret=service_token_secret,
)
# 4. Decrypt the (encrypted) secret value
secret_value = decrypt(
ciphertext=encrypted_secret["secretValueCiphertext"],
iv=encrypted_secret["secretValueIV"],
tag=encrypted_secret["secretValueTag"],
secret=project_key,
)
print("secret: ", {
"secret_key": secret_key,
"secret_value": secret_value
})
get_secret()
```
</Tab>
</Tabs>
</Accordion>
<Accordion title="Update secret">
<Tabs>
<Tab title="Javascript">
Update an existing secret in Infisical.
```js
const crypto = require('crypto');
const axios = require('axios');
const BASE_URL = 'https://app.infisical.com';
const ALGORITHM = 'aes-256-gcm';
const BLOCK_SIZE_BYTES = 16;
const encrypt = ({ text, secret }) => {
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
let ciphertext = cipher.update(text, 'utf8', 'base64');
ciphertext += cipher.final('base64');
return {
ciphertext,
iv: iv.toString('base64'),
tag: cipher.getAuthTag().toString('base64')
};
}
const decrypt = ({ ciphertext, iv, tag, secret}) => {
const decipher = crypto.createDecipheriv(
ALGORITHM,
secret,
Buffer.from(iv, 'base64')
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
cleartext += decipher.final('utf8');
return cleartext;
}
const updateSecrets = async () => {
const serviceToken = 'your_service_token';
const serviceTokenSecret = serviceToken.substring(serviceToken.lastIndexOf('.') + 1);
const secretType = 'shared' // 'shared' or 'personal'
const secretKey = 'some_key';
const secretValue = 'updated_value';
const secretComment = 'updated_comment';
// 1. Get your Infisical Token data
const { data: serviceTokenData } = await axios.get(
`${BASE_URL}/api/v2/service-token`,
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
// 2. Decrypt the (encrypted) project key with the key from your Infisical Token
const projectKey = decrypt({
ciphertext: serviceTokenData.encryptedKey,
iv: serviceTokenData.iv,
tag: serviceTokenData.tag,
secret: serviceTokenSecret
});
// 3. Encrypt your updated secret with the project key
const {
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
tag: secretKeyTag
} = encrypt({
text: secretKey,
secret: projectKey
});
const {
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag
} = encrypt({
text: secretValue,
secret: projectKey
});
const {
ciphertext: secretCommentCiphertext,
iv: secretCommentIV,
tag: secretCommentTag
} = encrypt({
text: secretComment,
secret: projectKey
});
// 4. Send (encrypted) updated secret to Infisical
await axios.patch(
`${BASE_URL}/api/v3/secrets/${secretKey}`,
{
workspaceId: serviceTokenData.workspace,
environment: serviceTokenData.environment,
type: secretType,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
},
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
}
updateSecrets();
```
</Tab>
<Tab title="Python">
```Python
import base64
import requests
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
BASE_URL = "https://app.infisical.com"
BLOCK_SIZE_BYTES = 16
def encrypt(text, secret):
iv = get_random_bytes(BLOCK_SIZE_BYTES)
secret = bytes(secret, "utf-8")
cipher = AES.new(secret, AES.MODE_GCM, iv)
ciphertext, tag = cipher.encrypt_and_digest(text.encode("utf-8"))
return {
"ciphertext": base64.standard_b64encode(ciphertext).decode("utf-8"),
"tag": base64.standard_b64encode(tag).decode("utf-8"),
"iv": base64.standard_b64encode(iv).decode("utf-8"),
}
def decrypt(ciphertext, iv, tag, secret):
secret = bytes(secret, "utf-8")
iv = base64.standard_b64decode(iv)
tag = base64.standard_b64decode(tag)
ciphertext = base64.standard_b64decode(ciphertext)
cipher = AES.new(secret, AES.MODE_GCM, iv)
cipher.update(tag)
cleartext = cipher.decrypt(ciphertext).decode("utf-8")
return cleartext
def update_secret():
service_token = "your_service_token"
service_token_secret = service_token[service_token.rindex(".") + 1 :]
secret_type = "shared" # "shared" or "personal"
secret_key = "some_key"
secret_value = "updated_value"
secret_comment = "updated_comment"
# 1. Get your Infisical Token data
service_token_data = requests.get(
f"{BASE_URL}/api/v2/service-token",
headers={"Authorization": f"Bearer {service_token}"},
).json()
# 2. Decrypt the (encrypted) project key with the key from your Infisical Token
project_key = decrypt(
ciphertext=service_token_data["encryptedKey"],
iv=service_token_data["iv"],
tag=service_token_data["tag"],
secret=service_token_secret,
)
# 3. Encrypt your updated secret with the project key
encrypted_key_data = encrypt(text=secret_key, secret=project_key)
encrypted_value_data = encrypt(text=secret_value, secret=project_key)
encrypted_comment_data = encrypt(text=secret_comment, secret=project_key)
# 4. Send (encrypted) updated secret to Infisical
requests.patch(
f"{BASE_URL}/api/v3/secrets/{secret_key}",
json={
"workspaceId": service_token_data["workspace"],
"environment": service_token_data["environment"],
"type": secret_type,
"secretKeyCiphertext": encrypted_key_data["ciphertext"],
"secretKeyIV": encrypted_key_data["iv"],
"secretKeyTag": encrypted_key_data["tag"],
"secretValueCiphertext": encrypted_value_data["ciphertext"],
"secretValueIV": encrypted_value_data["iv"],
"secretValueTag": encrypted_value_data["tag"],
"secretCommentCiphertext": encrypted_comment_data["ciphertext"],
"secretCommentIV": encrypted_comment_data["iv"],
"secretCommentTag": encrypted_comment_data["tag"]
},
headers={"Authorization": f"Bearer {service_token}"},
)
update_secret()
```
</Tab>
</Tabs>
</Accordion>
<Accordion title="Delete secret">
<Tabs>
<Tab title="Javascript">
Delete a secret in Infisical.
```js
const axios = require('axios');
const BASE_URL = 'https://app.infisical.com';
const deleteSecrets = async () => {
const serviceToken = 'your_service_token';
const secretType = 'shared' // 'shared' or 'personal'
const secretKey = 'some_key'
// 1. Get your Infisical Token data
const { data: serviceTokenData } = await axios.get(
`${BASE_URL}/api/v2/service-token`,
{
headers: {
Authorization: `Bearer ${serviceToken}`
}
}
);
// 2. Delete secret from Infisical
await axios.delete(
`${BASE_URL}/api/v3/secrets/${secretKey}`,
{
workspaceId: serviceTokenData.workspace,
environment: serviceTokenData.environment,
type: secretType
},
{
headers: {
Authorization: `Bearer ${serviceToken}`
},
}
);
};
deleteSecrets();
```
</Tab>
<Tab title="Python">
```Python
import requests
BASE_URL = "https://app.infisical.com"
def delete_secrets():
service_token = "<your_service_token>"
secret_type = "shared" # "shared" or "personal"
secret_key = "some_key"
# 1. Get your Infisical Token data
service_token_data = requests.get(
f"{BASE_URL}/api/v2/service-token",
headers={"Authorization": f"Bearer {service_token}"},
).json()
# 2. Delete secret from Infisical
requests.delete(
f"{BASE_URL}/api/v2/secrets/{secret_key}",
json={
"workspaceId": service_token_data["workspace"],
"environment": service_token_data["environment"],
"type": secret_type
},
headers={"Authorization": f"Bearer {service_token}"},
)
delete_secrets()
```
</Tab>
</Tabs>
<Info>
If using an `API_KEY` to authenticate with the Infisical API, then you should include it in the `X_API_KEY` header.
</Info>
</Accordion>
</AccordionGroup>

@ -0,0 +1,90 @@
---
title: "Configure native integrations programmatically"
description: "How to use Infisical API to sync secrets to external secret managers"
---
The Infisical API allows you to create programmatic integrations that connect with third-party secret managers to synchronize secrets from Infisical.
This guide will primarily demonstrate the process using AWS Secret Store Manager (AWS SSM), but the steps are generally applicable to other secret management integrations.
<Info>
For details on setting up AWS SSM synchronization and understanding its prerequisites, refer to the [AWS SSM integration setup documentation](../../../integrations/cloud/aws-secret-manager).
</Info>
<Steps>
<Step title="Authenticate with AWS SSM">
Authentication is required for all integrations. Use the [Integration Auth API](../../endpoints/integrations/create-auth) with the following parameters to authenticate.
<ParamField body="integration" type="string" initialValue="aws-secret-manager" required>
Set this parameter to **aws-secret-manager**.
</ParamField>
<ParamField body="workspaceId" type="string" required>
The Infisical project ID for the integration.
</ParamField>
<ParamField body="accessId" type="string" required>
The AWS IAM User Access ID.
</ParamField>
<ParamField body="accessToken" type="string" required>
The AWS IAM User Access Secret Key.
</ParamField>
```bash Request
curl --request POST \
--url https://app.infisical.com/api/v1/integration-auth/access-token \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '{
"workspaceId": "<workspaceid>",
"integration": "aws-secret-manager",
"accessId": "<aws iam user access id>",
"accessToken": "<aws iam user access secret key>"
}'
```
</Step>
<Step title="Configure the Synchronization Setup">
Once authentication between AWS SSM and Infisical is established, you can configure the synchronization behavior.
This involves specifying the source (environment and secret path in Infisical) and the destination in SSM to which the secrets will be synchronized.
Use the [integration API](../../endpoints/integrations/create) with the following parameters to configure the sync source and destination.
<ParamField body="integrationAuthId" type="string" required>
The ID of the integration authentication object used with AWS, obtained from the previous API response.
</ParamField>
<ParamField body="isActive" type="boolean">
Indicates whether the integration should be active or inactive.
</ParamField>
<ParamField body="app" type="string" required>
The secret name for saving in AWS SSM, which can be arbitrarily chosen.
</ParamField>
<ParamField body="region" type="string" required>
The AWS region where the SSM is located, e.g., `us-east-1`.
</ParamField>
<ParamField body="sourceEnvironment" type="string" required>
The Infisical environment slug from which secrets will be synchronized, e.g., `dev`.
</ParamField>
<ParamField body="secretPath" type="string" required>
The Infisical folder path from which secrets will be synchronized, e.g., `/some/path`. The root path is `/`.
</ParamField>
```bash Request
curl --request POST \
--url https://app.infisical.com/api/v1/integration \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '{
"integrationAuthId": "<integrationauthid>",
"sourceEnvironment": "<sourceenvironment>",
"secretPath": "<secret-path, default is '/' >",
"app": "<app>",
"region": "<aws-ssm-region>"
}'
```
</Step>
</Steps>
<Check>
Congratulations! You have successfully set up an integration to synchronize secrets from Infisical with AWS SSM.
For more information, [view the integration API reference](../../endpoints/integrations).
</Check>

@ -44,7 +44,7 @@ In the above screenshot, you can see that we are creating a token token with `re
of the `/common` path within the development environment of the project; the token expires in 6 months and can be used from any IP address.
<Note>
For a deeper understanding of service tokens, it is recommended to read [this guide](http://localhost:3000/internals/service-tokens).
For a deeper understanding of service tokens, it is recommended to read [this guide](https://infisical.com/docs/internals/service-tokens).
</Note>
**FAQ**

@ -388,8 +388,7 @@
"group": "Examples",
"pages": [
"api-reference/overview/examples/note",
"api-reference/overview/examples/e2ee-disabled",
"api-reference/overview/examples/e2ee-enabled"
"api-reference/overview/examples/integration"
]
}
]
@ -472,7 +471,7 @@
]
},
{
"group": "Secret tags",
"group": "Secret Tags",
"pages": [
"api-reference/endpoints/secret-tags/list",
"api-reference/endpoints/secret-tags/create",
@ -492,7 +491,7 @@
]
},
{
"group": "Secret imports",
"group": "Secret Imports",
"pages": [
"api-reference/endpoints/secret-imports/list",
"api-reference/endpoints/secret-imports/create",

@ -335,6 +335,10 @@ To login into Infisical with OAuth providers such as Google, configure the assoc
Requires enterprise license. Please contact team@infisical.com to get more information.
</Accordion>
<ParamField query="NEXT_PUBLIC_SAML_ORG_SLUG" type="string">
Configure SAML organization slug to automatically redirect all users of your Infisical instance to the identity provider.
</ParamField>

@ -63,6 +63,30 @@
border-color: #ebebeb;
}
#content-area .mt-8 .rounded-xl{
border-radius: 0;
}
#content-area .mt-8 .rounded-lg{
border-radius: 0;
}
#content-area .mt-6 .rounded-xl{
border-radius: 0;
}
#content-area .mt-6 .rounded-lg{
border-radius: 0;
}
#content-area .mt-6 .rounded-md{
border-radius: 0;
}
#content-area .mt-8 .rounded-md{
border-radius: 0;
}
#content-area div.my-4{
border-radius: 0;
border-width: 1px;
@ -78,6 +102,10 @@
border-radius: 0;
}
#content-area a {
border-radius: 0;
}
#content-area .not-prose {
border-radius: 0;
}

@ -52,6 +52,9 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
ARG INTERCOM_ID
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
ARG SAML_ORG_SLUG
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
ARG NEXT_INFISICAL_PLATFORM_VERSION
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION=$NEXT_INFISICAL_PLATFORM_VERSION

@ -4,6 +4,8 @@ scripts/replace-standalone-build-variable.sh "$BAKED_NEXT_PUBLIC_POSTHOG_API_KEY
scripts/replace-standalone-build-variable.sh "$BAKED_NEXT_PUBLIC_INTERCOM_ID" "$NEXT_PUBLIC_INTERCOM_ID"
scripts/replace-standalone-build-variable.sh "$BAKED_NEXT_PUBLIC_SAML_ORG_SLUG" "$NEXT_PUBLIC_SAML_ORG_SLUG"
if [ "$TELEMETRY_ENABLED" != "false" ]; then
echo "Telemetry is enabled"
scripts/set-standalone-build-telemetry.sh true

@ -4,6 +4,8 @@ scripts/replace-variable.sh "$BAKED_NEXT_PUBLIC_POSTHOG_API_KEY" "$NEXT_PUBLIC_P
scripts/replace-variable.sh "$BAKED_NEXT_PUBLIC_INTERCOM_ID" "$NEXT_PUBLIC_INTERCOM_ID"
scripts/replace-variable.sh "$BAKED_NEXT_SAML_ORG_SLUG" "$NEXT_PUBLIC_SAML_ORG_SLUG"
if [ "$TELEMETRY_ENABLED" != "false" ]; then
echo "Telemetry is enabled"
scripts/set-telemetry.sh true

@ -483,10 +483,10 @@ const OrganizationPage = withPermission(
const addUsersToProject = useAddUserToWsNonE2EE();
const { data: updateClosed } = useGetUserAction("april_2024_db_update_closed");
const { data: updateClosed } = useGetUserAction("april_13_2024_db_update_closed");
const registerUserAction = useRegisterUserAction();
const closeUpdate = async () => {
await registerUserAction.mutateAsync("april_2024_db_update_closed");
await registerUserAction.mutateAsync("april_13_2024_db_update_closed");
};
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
@ -594,6 +594,7 @@ const OrganizationPage = withPermission(
</div>
)}
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
{window.location.origin.includes("https://app.infisical.com") || window.location.origin.includes("http://localhost:8080") && (
<div
className={`${
!updateClosed ? "block" : "hidden"
@ -601,11 +602,11 @@ const OrganizationPage = withPermission(
>
<FontAwesomeIcon icon={faWarning} className="p-6 text-4xl text-primary" />
<div className="text-sm">
<span className="text-lg font-semibold">Scheduled maintenance on April 6th 2024 </span>{" "}
<span className="text-lg font-semibold">Scheduled maintenance on April 13th 2024 </span>{" "}
<br />
Infisical will undergo scheduled maintenance for approximately 1 hour on Saturday, April 6th, 11am EST. During these hours, read
Infisical will undergo scheduled maintenance for approximately 1 hour on Saturday, April 13th, 11am EST. During these hours, read
operations will continue to function normally but no resources will be editable.
No action is required on your end your applications can continue to fetch secrets.
No action is required on your end your applications will continue to fetch secrets.
<br />
</div>
<button
@ -616,7 +617,8 @@ const OrganizationPage = withPermission(
>
<FontAwesomeIcon icon={faXmark} />
</button>
</div>
</div>)}
<p className="mr-4 font-semibold text-white">Projects</p>
<div className="mt-6 flex w-full flex-row">
<Input

@ -1,4 +1,4 @@
import { FormEvent, useState } from "react";
import { FormEvent, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import Link from "next/link";
import { useRouter } from "next/router";
@ -32,6 +32,18 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
const { config } = useServerConfig();
const queryParams = new URLSearchParams(window.location.search);
useEffect(() => {
if (process.env.NEXT_PUBLIC_SAML_ORG_SLUG && process.env.NEXT_PUBLIC_SAML_ORG_SLUG !== "saml-org-slug-default") {
const callbackPort = queryParams.get("callback_port");
window.open(
`/api/v1/sso/redirect/saml2/organizations/${process.env.NEXT_PUBLIC_SAML_ORG_SLUG}${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.close();
}
}, [])
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
@ -244,7 +256,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
</Link>
</div>
) : (
<div />
<div className="mt-4" />
)}
<div className="mt-2 flex flex-row text-sm text-bunker-400">
<Link href="/verify-email">

@ -23,9 +23,6 @@ export const MembersPage = withPermission(
<Tab value={TabSections.Identities}>
<div className="flex items-center">
<p>Machine Identities</p>
<div className="ml-2 inline-block cursor-default rounded-md bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] text-sm text-yellow opacity-80 hover:opacity-100">
New
</div>
</div>
</Tab>
<Tab value={TabSections.Roles}>Organization Roles</Tab>

@ -31,7 +31,7 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => {
const [searchRoles, setSearchRoles] = useState("");
const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || "";
const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp(["deleteRole"] as const);
const { data: roles, isLoading: isRolesLoading } = useGetOrgRoles(orgId);
@ -49,7 +49,7 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => {
handlePopUpClose("deleteRole");
} catch (err) {
console.log(err);
createNotification({ type: "error", text: "Failed to create role" });
createNotification({ type: "error", text: "Failed to delete role" });
}
};

@ -29,7 +29,7 @@ type Props = {
export const ProjectRoleList = ({ onSelectRole }: Props) => {
const [searchRoles, setSearchRoles] = useState("");
const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp(["deleteRole"] as const);
const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?.id || "";
@ -50,7 +50,7 @@ export const ProjectRoleList = ({ onSelectRole }: Props) => {
handlePopUpClose("deleteRole");
} catch (err) {
console.log(err);
createNotification({ type: "error", text: "Failed to create role" });
createNotification({ type: "error", text: "Failed to delete role" });
}
};

@ -313,6 +313,7 @@ export const SecretMainPage = () => {
workspaceId={workspaceId}
secretPath={secretPath}
sortDir={sortDir}
searchTerm={filter.searchFilter}
/>
{canReadSecret && (
<DynamicSecretListView

@ -20,12 +20,14 @@ type Props = {
workspaceId: string;
secretPath?: string;
sortDir: SortDir;
searchTerm?: string;
};
export const FolderListView = ({
folders = [],
environment,
workspaceId,
searchTerm,
secretPath = "/",
sortDir = SortDir.ASC
}: Props) => {
@ -35,8 +37,6 @@ export const FolderListView = ({
] as const);
const router = useRouter();
const { mutateAsync: updateFolder } = useUpdateFolder();
const { mutateAsync: deleteFolder } = useDeleteFolder();
@ -100,6 +100,7 @@ export const FolderListView = ({
return (
<>
{folders
.filter(({ name }) => name.toUpperCase().includes(String(searchTerm?.toUpperCase())))
.sort((a, b) =>
sortDir === SortDir.ASC
? a.name.toLowerCase().localeCompare(b.name.toLowerCase())

@ -72,6 +72,12 @@ If you are editing the API definitions, generate the manifests such as CRs or CR
make manifests
```
Also, after editing the API definitions, update the kubectl-install folder:
```sh
make kubectl-install
```
**NOTE:** Run `make --help` for more information on all potential `make` targets
More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)

@ -96,12 +96,47 @@ spec:
- secretsScope
- serviceTokenSecretReference
type: object
universalAuth:
properties:
credentialsRef:
properties:
secretName:
description: The name of the Kubernetes Secret
type: string
secretNamespace:
description: The name space where the Kubernetes Secret is located
type: string
required:
- secretName
- secretNamespace
type: object
secretsScope:
properties:
envSlug:
type: string
projectSlug:
type: string
secretsPath:
type: string
required:
- envSlug
- projectSlug
- secretsPath
type: object
required:
- credentialsRef
- secretsScope
type: object
type: object
hostAPI:
description: Infisical host to pull secrets from
type: string
managedSecretReference:
properties:
creationPolicy:
default: Orphan
description: 'The Kubernetes Secret creation policy. Enum with values: ''Owner'', ''Orphan''. Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.'
type: string
secretName:
description: The name of the Kubernetes Secret
type: string