1
0
mirror of https://github.com/Infisical/infisical.git synced 2025-03-21 23:59:52 +00:00

Compare commits

..

52 Commits

Author SHA1 Message Date
31231cfcca Update developing.mdx 2024-01-08 23:30:10 +04:00
fef5369738 Merge pull request from Infisical/identity-apis
Update various identities items
2024-01-06 17:11:01 +01:00
c94b7d63f6 Update various identities items 2024-01-06 17:04:44 +01:00
485ddc5c50 Merge pull request from Infisical/patch-railway
Fix client-side railway integration issue
2024-01-06 16:14:16 +01:00
edd9c66e49 Remove commented print statements 2024-01-06 16:11:22 +01:00
0a3b85534b Fix client-side railway integration issue 2024-01-06 16:09:15 +01:00
ec2cc5162e Merge pull request from Infisical/daniel/sdk-contribution-guide
Contribution guide refactor & SDK contribution guide
2024-01-05 20:26:17 -05:00
7ce472957c Fixed quality 2024-01-06 04:04:09 +04:00
8529e0da3d Update developing.mdx 2024-01-06 03:41:31 +04:00
e5a5433f10 Update developing.mdx 2024-01-06 03:00:14 +04:00
ee6e518ff8 Update link to contribution guide 2024-01-06 02:58:26 +04:00
15a7222505 Update mint.json 2024-01-06 02:58:16 +04:00
25d482cc62 Create sdk-flow.png 2024-01-06 02:58:12 +04:00
785a2bec6a Added SDK guide 2024-01-06 02:58:08 +04:00
449466f326 Restructure 2024-01-06 02:58:02 +04:00
4131e9c3f1 Added getting started section 2024-01-06 02:57:53 +04:00
310595256f Restructured existing guide 2024-01-06 02:57:21 +04:00
1737880e58 Merge pull request from Infisical/snyk-fix-b96b562a611b0789d0a73c522a261f22
[Snyk] Security upgrade probot from 12.3.1 to 12.3.3
2024-01-05 11:20:43 -05:00
b72483f5f2 Merge pull request from Emiliaaah/fix-agent-secret-path
fix(cli): secret-path directive for agent
2024-01-05 10:39:39 -05:00
ee14bda706 Merge pull request from rlaisqls/error-message-typos
Fix error message typos
2024-01-05 18:18:20 +04:00
e56463d52b fix(cli): secret-path directive for agent 2024-01-05 15:05:57 +01:00
ebd3d7c7c4 Merge pull request from Infisical/fix-vercel-preview-env
Fix: Vercel integration preview environment client side error
2024-01-04 10:18:25 -05:00
9ecbfe201b Update create.tsx 2024-01-04 17:42:31 +04:00
ba2a03897f update secret import create notif 2024-01-04 01:55:34 -05:00
304f14c0ed update service token create notif 2024-01-04 01:52:03 -05:00
51e5c25e16 update imports/service token crud 2024-01-04 00:55:03 -05:00
0f6490b1e7 move cli to bin folder 2024-01-03 20:17:34 -05:00
f894e48fcb remove unused import 2024-01-02 13:55:01 -05:00
37cfa22619 add back macos build 2024-01-02 13:47:15 -05:00
94557344b7 wrap cli into a docker image 2024-01-02 13:43:55 -05:00
d5063018eb Added identities, universal auth, agent to changelog 2024-01-02 10:05:43 +01:00
51d68505d3 Merge pull request from Infisical/posthog-revamp
removed posthog cli export events
2023-12-29 15:18:59 -05:00
ade27ad072 Fix typos 2023-12-29 13:26:08 +09:00
683c512bce Merge pull request from Infisical/ui-improvements
ui and docs improvements
2023-12-25 14:33:47 -05:00
43ff28b5fb added terraform useragent 2023-12-24 17:13:29 -08:00
ce41855e84 added sdk useragent and channel 2023-12-24 16:58:48 -08:00
d24461b17c removed posthog cli export events 2023-12-24 15:49:18 -08:00
1797e56f9f fixed sdk guides 2023-12-24 13:30:59 -08:00
74f3ca5356 Merge pull request from Infisical/sdk/docs-update-2
Sdk/docs update 2
2023-12-24 21:57:52 +04:00
db27beaf0b Update overview.mdx 2023-12-24 21:54:57 +04:00
d6e55f51f2 Updated Python docs 2023-12-24 21:36:47 +04:00
e9b5996567 Updated node caching docs 2023-12-24 21:36:40 +04:00
094fe73917 Updated Java caching docs 2023-12-24 21:36:31 +04:00
dc3f85e92e Re-added an updated FAQ 2023-12-24 17:11:20 +04:00
c463256058 Updated Python docs 2023-12-24 17:11:08 +04:00
8df22302fd Updated Node docs 2023-12-24 17:11:03 +04:00
f37fa2bbf5 Updated Java docs 2023-12-24 17:10:54 +04:00
597c9d6f2a fix docs sdk errors 2023-12-23 17:17:10 -08:00
24d2eea930 ui and docs improvements 2023-12-23 16:06:00 -08:00
382cb910af tps 2023-12-23 17:31:34 -05:00
6725475575 Merge pull request from Infisical/sdk/docs-update
SDK documentation update
2023-12-23 09:30:35 -08:00
c7b2489d0b fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-PROBOT-6129524
2023-12-17 14:48:29 +00:00
57 changed files with 892 additions and 215 deletions
.goreleaser.yamlREADME.md
backend
cli
docs
frontend/src
components/signup
hooks/api
auditLogs
integrationAuth
layouts/AppLayout
pages
integrations
org/[id]/overview
views
Org/MembersPage/components
OrgIdentityTab/components/IdentitySection
OrgMembersTab/components/OrgMembersSection
Project/MembersPage/components
IdentityTab/components/IdentitySection
MemberListTab
ServiceTokenTab/components/ServiceTokenSection
SecretApprovalPage
SecretMainPage/components
SecretOverviewPage
SecretRotationPage
admin/SignUpPage

@ -108,7 +108,7 @@ brews:
zsh_completion.install "completions/infisical.zsh" => "_infisical"
fish_completion.install "completions/infisical.fish"
man1.install "manpages/infisical.1.gz"
- name: 'infisical@{{.Version}}'
- name: "infisical@{{.Version}}"
tap:
owner: Infisical
name: homebrew-get-cli
@ -186,12 +186,14 @@ aurs:
# man pages
install -Dm644 "./manpages/infisical.1.gz" "${pkgdir}/usr/share/man/man1/infisical.1.gz"
# dockers:
# - dockerfile: cli/docker/Dockerfile
# goos: linux
# goarch: amd64
# ids:
# - infisical
# image_templates:
# - "infisical/cli:{{ .Version }}"
# - "infisical/cli:latest"
dockers:
- dockerfile: docker/alpine
goos: linux
goarch: amd64
ids:
- all-other-builds
image_templates:
- "infisical/cli:{{ .Version }}"
- "infisical/cli:{{ .Major }}.{{ .Minor }}"
- "infisical/cli:{{ .Major }}"
- "infisical/cli:latest"

@ -129,7 +129,7 @@ Note that this security address should be used only for undisclosed vulnerabilit
## Contributing
Whether it's big or small, we love contributions. Check out our guide to see how to [get started](https://infisical.com/docs/contributing/overview).
Whether it's big or small, we love contributions. Check out our guide to see how to [get started](https://infisical.com/docs/contributing/getting-started).
Not sure where to get started? You can:

@ -60,7 +60,7 @@
"pino": "^8.16.1",
"pino-http": "^8.5.1",
"posthog-node": "^2.6.0",
"probot": "^12.3.1",
"probot": "^12.3.3",
"query-string": "^7.1.3",
"rate-limit-mongo": "^2.3.2",
"rimraf": "^3.0.2",
@ -5991,9 +5991,9 @@
}
},
"node_modules/@octokit/webhooks": {
"version": "9.26.0",
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.0.tgz",
"integrity": "sha512-foZlsgrTDwAmD5j2Czn6ji10lbWjGDVsUxTIydjG9KTkAWKJrFapXJgO5SbGxRwfPd3OJdhK3nA2YPqVhxLXqA==",
"version": "9.26.3",
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.3.tgz",
"integrity": "sha512-DLGk+gzeVq5oK89Bo601txYmyrelMQ7Fi5EnjHE0Xs8CWicy2xkmnJMKptKJrBJpstqbd/9oeDFi/Zj2pudBDQ==",
"dependencies": {
"@octokit/request-error": "^2.0.2",
"@octokit/webhooks-methods": "^2.0.0",
@ -16306,9 +16306,9 @@
}
},
"node_modules/probot": {
"version": "12.3.1",
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.1.tgz",
"integrity": "sha512-ECSgycmAC0ILEK6cOa+x3QPufP5JybsuohOFCYr3glQU5SkbmypZJE/Sfio9mxAFHK5LCXveIDsfZCxf6ck4JA==",
"version": "12.3.3",
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.3.tgz",
"integrity": "sha512-cdtKd+xISzi8sw6++BYBXleRknCA6hqUMoHj/sJqQBrjbNxQLhfeFCq9O2d0Z4eShsy5YFRR3MWwDKJ9uAE0CA==",
"dependencies": {
"@octokit/core": "^3.2.4",
"@octokit/plugin-enterprise-compatibility": "^1.2.8",
@ -16317,7 +16317,7 @@
"@octokit/plugin-retry": "^3.0.6",
"@octokit/plugin-throttling": "^3.3.4",
"@octokit/types": "^8.0.0",
"@octokit/webhooks": "^9.8.4",
"@octokit/webhooks": "^9.26.3",
"@probot/get-private-key": "^1.1.0",
"@probot/octokit-plugin-config": "^1.0.0",
"@probot/pino": "^2.2.0",
@ -23392,9 +23392,9 @@
}
},
"@octokit/webhooks": {
"version": "9.26.0",
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.0.tgz",
"integrity": "sha512-foZlsgrTDwAmD5j2Czn6ji10lbWjGDVsUxTIydjG9KTkAWKJrFapXJgO5SbGxRwfPd3OJdhK3nA2YPqVhxLXqA==",
"version": "9.26.3",
"resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.3.tgz",
"integrity": "sha512-DLGk+gzeVq5oK89Bo601txYmyrelMQ7Fi5EnjHE0Xs8CWicy2xkmnJMKptKJrBJpstqbd/9oeDFi/Zj2pudBDQ==",
"requires": {
"@octokit/request-error": "^2.0.2",
"@octokit/webhooks-methods": "^2.0.0",
@ -31039,9 +31039,9 @@
}
},
"probot": {
"version": "12.3.1",
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.1.tgz",
"integrity": "sha512-ECSgycmAC0ILEK6cOa+x3QPufP5JybsuohOFCYr3glQU5SkbmypZJE/Sfio9mxAFHK5LCXveIDsfZCxf6ck4JA==",
"version": "12.3.3",
"resolved": "https://registry.npmjs.org/probot/-/probot-12.3.3.tgz",
"integrity": "sha512-cdtKd+xISzi8sw6++BYBXleRknCA6hqUMoHj/sJqQBrjbNxQLhfeFCq9O2d0Z4eShsy5YFRR3MWwDKJ9uAE0CA==",
"requires": {
"@octokit/core": "^3.2.4",
"@octokit/plugin-enterprise-compatibility": "^1.2.8",
@ -31050,7 +31050,7 @@
"@octokit/plugin-retry": "^3.0.6",
"@octokit/plugin-throttling": "^3.3.4",
"@octokit/types": "^8.0.0",
"@octokit/webhooks": "^9.8.4",
"@octokit/webhooks": "^9.26.3",
"@probot/get-private-key": "^1.1.0",
"@probot/octokit-plugin-config": "^1.0.0",
"@probot/pino": "^2.2.0",

@ -51,7 +51,7 @@
"pino": "^8.16.1",
"pino-http": "^8.5.1",
"posthog-node": "^2.6.0",
"probot": "^12.3.1",
"probot": "^12.3.3",
"query-string": "^7.1.3",
"rate-limit-mongo": "^2.3.2",
"rimraf": "^3.0.2",

@ -4962,7 +4962,8 @@
},
"security": [
{
"apiKeyAuth": []
"apiKeyAuth": [],
"bearerAuth": []
}
]
}

@ -111,11 +111,17 @@ export const createSecretImp = async (req: Request, res: Response) => {
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory })
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment: secretImport.environment, secretPath: secretImport.secretPath })
);
}
const folders = await Folder.findOne({
@ -323,7 +329,7 @@ export const updateSecretImport = async (req: Request, res: Response) => {
authData: req.authData,
workspaceId: importSecDoc.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, {
@ -331,6 +337,13 @@ export const updateSecretImport = async (req: Request, res: Response) => {
secretPath
})
);
secretImports.forEach(({ environment, secretPath }) => {
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
);
})
}
const orderBefore = importSecDoc.imports;
@ -453,7 +466,7 @@ export const deleteSecretImport = async (req: Request, res: Response) => {
authData: req.authData,
workspaceId: importSecDoc.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
subject(ProjectPermissionSub.Secrets, {
@ -620,7 +633,7 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => {
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId)
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, {
@ -677,7 +690,7 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => {
authData: req.authData,
workspaceId: importSecDoc.workspace
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, {

@ -1,9 +1,13 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import {
IdentityMembershipOrg,
Membership,
IWorkspace,
Identity,
IdentityMembership,
IdentityMembershipOrg,
Membership,
MembershipOrg,
User,
Workspace
} from "../../models";
import { Role } from "../../ee/models";
@ -298,7 +302,8 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
#swagger.description = 'Return projects in organization that user is part of'
#swagger.security = [{
"apiKeyAuth": []
"apiKeyAuth": [],
"bearerAuth": []
}]
#swagger.parameters['organizationId'] = {
@ -326,6 +331,7 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
}
}
*/
const {
params: { organizationId }
} = await validateRequest(reqValidator.GetOrgWorkspacesv2, req);
@ -351,13 +357,27 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
).map((w) => w._id.toString())
);
const workspaces = (
await Membership.find({
user: req.user._id
}).populate("workspace")
)
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
.map((m) => m.workspace);
let workspaces: IWorkspace[] = [];
if (req.authData.authPayload instanceof Identity) {
workspaces = (
await IdentityMembership.find({
identity: req.authData.authPayload._id
}).populate<{ workspace: IWorkspace }>("workspace")
)
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
.map((m) => m.workspace);
}
if (req.authData.authPayload instanceof User) {
workspaces = (
await Membership.find({
user: req.authData.authPayload._id
}).populate<{ workspace: IWorkspace }>("workspace")
)
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
.map((m) => m.workspace);
}
return res.status(200).send({
workspaces

@ -13,7 +13,7 @@ import {
ProjectPermissionSub,
getAuthDataProjectPermissions
} from "../../ee/services/ProjectRoleService";
import { ForbiddenError } from "@casl/ability";
import { ForbiddenError, subject } from "@casl/ability";
import { Types } from "mongoose";
/**
@ -86,6 +86,14 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
ProjectPermissionSub.ServiceTokens
);
scopes.forEach(({ environment, secretPath }) => {
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment, secretPath: secretPath })
);
})
const secret = crypto.randomBytes(16).toString("hex");
const secretHash = await bcrypt.hash(secret, await getSaltRounds());

@ -8,7 +8,10 @@ export enum UserAgentType {
WEB = "web",
CLI = "cli",
K8_OPERATOR = "k8-operator",
OTHER = "other"
TERRAFORM = "terraform",
OTHER = "other",
PYTHON_SDK = "InfisicalPythonSDK",
NODE_SDK = "InfisicalNodeSDK"
}
export enum EventType {

@ -10,7 +10,7 @@ export const apiLimiter = rateLimit({
// errorHandler: console.error.bind(null, 'rate-limit-mongo')
// }),
windowMs: 60 * 1000,
max: 350,
max: 480,
standardHeaders: true,
legacyHeaders: false,
skip: (request) => {
@ -30,7 +30,7 @@ const authLimit = rateLimit({
// collectionName: "expressRateRecords-authLimit",
// }),
windowMs: 60 * 1000,
max: 100,
max: 300,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req, res) => {
@ -46,8 +46,8 @@ export const passwordLimiter = rateLimit({
// errorHandler: console.error.bind(null, 'rate-limit-mongo'),
// collectionName: "expressRateRecords-passwordLimiter",
// }),
windowMs: 60 * 60 * 1000,
max: 10,
windowMs: 60 * 1000,
max: 300,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req, res) => {

@ -7,8 +7,14 @@ export const getUserAgentType = function (userAgent: string | undefined) {
return UserAgentType.CLI;
} else if (userAgent == UserAgentType.K8_OPERATOR) {
return UserAgentType.K8_OPERATOR;
} else if (userAgent == UserAgentType.TERRAFORM) {
return UserAgentType.TERRAFORM;
} else if (userAgent.toLowerCase().includes("mozilla")) {
return UserAgentType.WEB;
} else if (userAgent.includes(UserAgentType.NODE_SDK)) {
return UserAgentType.NODE_SDK;
} else if (userAgent.includes(UserAgentType.PYTHON_SDK)) {
return UserAgentType.PYTHON_SDK;
} else {
return UserAgentType.OTHER;
}

@ -1,4 +0,0 @@
FROM alpine
RUN apk add --no-cache tini
COPY infisical /bin/infisical
ENTRYPOINT ["/sbin/tini", "--", "/bin/infisical"]

9
cli/docker/alpine Normal file

@ -0,0 +1,9 @@
FROM alpine
RUN apk add --no-cache tini
## Upgrade OpenSSL libraries to mitigate known vulnerabilities as the current Alpine image has not been patched yet.
RUN apk update && apk upgrade --no-cache libcrypto3 libssl3
COPY infisical /bin/infisical
ENTRYPOINT ["/sbin/tini", "--", "/bin/infisical"]

@ -474,6 +474,7 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques
SetBody(request).
SetQueryParam("workspaceId", request.WorkspaceId).
SetQueryParam("environment", request.Environment).
SetQueryParam("secretPath", request.SecretPath).
SetQueryParam("include_imports", "false").
Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL))

@ -11,7 +11,6 @@ import (
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
@ -102,7 +101,7 @@ var exportCmd = &cobra.Command{
fmt.Print(output)
Telemetry.CaptureEvent("cli-command:export", posthog.NewProperties().Set("secretsCount", len(secrets)).Set("version", util.CLI_VERSION))
// Telemetry.CaptureEvent("cli-command:export", posthog.NewProperties().Set("secretsCount", len(secrets)).Set("version", util.CLI_VERSION))
},
}

@ -168,7 +168,7 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin
getSecretsRequest.SecretPath = secretsPath
}
rawSecrets, err := api.CallGetRawSecretsV3(httpClient, api.GetRawSecretsV3Request{WorkspaceId: workspaceId, SecretPath: environmentName, Environment: environmentName})
rawSecrets, err := api.CallGetRawSecretsV3(httpClient, api.GetRawSecretsV3Request{WorkspaceId: workspaceId, SecretPath: secretsPath, Environment: environmentName})
if err != nil {
return nil, err
}

@ -1,10 +1,4 @@
---
title: "Get Projects"
openapi: "GET /api/v2/organizations/{organizationId}/workspaces"
---
<Warning>
This endpoint will be deprecated in the near future in Q1/Q2 2024.
We recommend switching to using [identities](/documentation/platform/identities/overview).
</Warning>
---

@ -6,6 +6,9 @@ The changelog below reflects new product developments and updates on a monthly b
## December 2023
- Released [(machine) identities](https://infisical.com/docs/documentation/platform/identities/overview) and [universal auth](https://infisical.com/docs/documentation/platform/identities/universal-auth) features.
- Created new cross-language SDKs for [Python](https://infisical.com/docs/sdks/languages/python), [Node](https://infisical.com/docs/sdks/languages/node), and [Java](https://infisical.com/docs/sdks/languages/java).
- Released first version of the [Infisical Agent](https://infisical.com/docs/infisical-agent/overview)
- Added ability to [manage folders via CLI](https://infisical.com/docs/cli/commands/secrets).
## November 2023

@ -6,7 +6,7 @@ description: "Frequently Asked Questions about contributing to Infisical"
Frequently asked questions about contributing to Infisical can be found on this page.
If you can't find the answer you are looking for, please create an issue on our GitHub repository or join our Slack channel for additional support.
<Accordion title="Error building backend (Alpine Linux CDN temporary error)">
<Accordion title="Error building Infisical platform backend (Alpine Linux CDN temporary error)">
The Alpine Linux CDN may be unavailable/down in your region infrequently (eg. there is an unplanned outage). One possible fix is to add a retry mechanism and a fallback mirrors array to the Dockerfile. You can also use this as an opportunity to pin the Alpine Linux version for Docker to use in case there are issues with the latest version. Ensure to use https for the mirrors.
#### Make the following changes to the backend Dockerfile

@ -1,14 +1,30 @@
---
title: "Overview"
description: "We welcome any contributions to Infisical, big or small."
description: "Contributing to the Infisical ecosystem."
---
To set a strong foundation, this section outlines how we, the community and members of Infisical,
should approach the development and contribution process.
## Code-bases
Infisical has two major code-bases. One for the platform code, and one for SDKs. The contribution process has some key differences between the two, so we've split the documentation into two sections:
- The [Infisical Platform](https://github.com/Infisical/infisical), the Infisical platform itself.
- The [Infisical SDK](https://github.com/Infisical/sdk), the official Infisical client SDKs.
<CardGroup cols={2}>
<Card title="Infisical Platform" href="/contributing/platform/developing" icon="layer-group" color="#A1B659">
The Infisical platform is the core of the Infisical ecosystem.
</Card>
<Card href="/contributing/sdk/developing" title="Infisical SDK" icon="code" color="#A1B659">
The SDKs are the official Infisical client libraries, used by developers to easily interact with the Infisical platform.
</Card>
</CardGroup>
## Community
We are building an inclusive community, and this means adhering to the [Code of Conduct](/contributing/code-of-conduct).
We are building an inclusive community, and this means adhering to the [Code of Conduct](/contributing/getting-started/code-of-conduct).
## Bugs and issues
@ -29,8 +45,10 @@ If you're ever in doubt about whether or not a proposed feature aligns with Infi
## Writing and submitting code
Anyone can contribute code to Infisical. To get started, check out the [local development guide](/contributing/developing), make your changes, and submit a pull request to the main repository
adhering to the [pull request guide](/contributing/pull-requests).
Anyone can contribute code to Infisical. To get started, check out the local development guides for each language.
- Local development guide for Platform is [here](/contributing/platform/developing).
- Local development guide for SDK is [here](/contributing/sdk/developing).
## Licensing
@ -38,3 +56,4 @@ adhering to the [pull request guide](/contributing/pull-requests).
Most of Infisical's code is under the MIT license, though some paid feature restrictions are covered by a proprietary license.
Any third party components incorporated into our code are licensed under the original license provided by the applicable component owner.

@ -19,7 +19,7 @@ You should follow the automatically-generated PR template to fill in the PR desc
Give a functional overview of how your feature works, including how the user can use the feature. Then share any technical details in an overview of how the PR works.
As of `06-01-2023`, all PRs created after this date are required to attach a video of you performing the described functionality.
As of `06-01-2023`, all PRs created after this date are required to attach a video of you performing the described functionality.
### Bug Fix PRs
@ -34,6 +34,8 @@ Once your PR is reviewed, one or two relevant members of the Infisical team shou
- Vlad: Frontend, Web UI
- Tony: Backend, SDKs, Security
- Maidul: Backend, CI/CD, CLI, Kubernetes Operator
- Daniel: Frontend, UI/UX, Backend, SDKs
The team member(s) will start by enabling baseline checks to ensure that there are no leaked secrets, new dependencies are clear, and the frontend/backend services start up. Afterward, they will review your PR thoroughly by testing the code and leave any feedback or work in with you to revise the PR up to standard.

@ -1,6 +1,6 @@
---
title: 'Local development'
description: 'This guide will help you set up and run Infisical in local development.'
description: 'This guide will help you set up and run the Infisical platform in local development.'
---
## Fork and clone the repo

@ -0,0 +1,408 @@
---
title: "Local development"
description: "This guide will help you contribute to the Infisical SDK."
---
## Fork and clone the repo
[Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the [repository](https://github.com/Infisical/sdk) to your own GitHub account and then [clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) it to your local device.
Once, you've done that, create a new branch:
```console
git checkout -b MY_BRANCH_NAME
```
## Set up environment variables
Start by creating a .env file at the root of the Infisical directory then copy the contents of the file below into the .env file.
<Accordion title=".env file content">
```env
# This is required for running tests locally.
# Rename this file to ".env" and fill in the values below.
# Please make sure that the machine identity has access to the project you are testing in.
# https://infisical.com/docs/documentation/platform/identities/universal-auth
INFISICAL_UNIVERSAL_CLIENT_ID=MACHINE_IDENTITY_CLIENT_ID
INFISICAL_UNIVERSAL_CLIENT_SECRET=MACHINE_IDENTITY_CLIENT_SECRET
# The ID of the Infisical project where we will create the test secrets.
# NOTE: The project must have a dev environment. (This is created by default when you create a project.)
INFISICAL_PROJECT_ID=INFISICAL_TEST_PROJECT_ID
# The Infisical site URL. If you are testing with a local Infisical instance, then this should be set to "http://localhost:8080".
INFISICAL_SITE_URL=https://app.infisical.com
````
</Accordion>
<Warning>
The above values are required for running tests locally. Before opening a pull request, make sure to run `cargo test` to ensure that all tests pass.
</Warning>
## Guidelines
### Predictable and consistent
When adding new functionality (such as new functions), it's very important that the functionality is added to _all_ the SDK's. This is to ensure that the SDK's are predictable and consistent across all languages. If you are adding new functionality, please make sure to add it to all the SDK's.
### Handling errors
Error handling is very important when writing SDK's. We want to make sure that the SDK's are easy to use, and that the user gets a good understanding of what went wrong when something fails. When adding new functionality, please make sure to add proper error handling. [Read more about error handling here](#error-handling).
### Tests
If you add new functionality or modify existing functionality, please write tests thats properly cover the new functionality. You can run tests locally by running `cargo test` from the root directory. You must always run tests before opening a pull request.
### Code style
Please follow the default rust styling guide when writing code for the base SDK. [Read more about rust code style here](https://doc.rust-lang.org/nightly/style-guide/#the-default-rust-style).
## Prerequisites for contributing
### Understanding the terms
In the guide we use some terms that might be unfamiliar to you. Here's a quick explanation of the terms we use:
- **Base SDK**: The base SDK is the SDK that all other SDK's are built on top of. The base SDK is written in Rust, and is responsible for executing commands and parsing the input and output to and from JSON.
- **Commands**: Commands are what's being sent from the target language to the command handler. The command handler uses the command to execute the corresponding function in the base SDK. Commands are in reality just a JSON string that tells the command handler what function to execute, and what input to use.
- **Command handler**: The command handler is the part of the base SDK that takes care of executing commands. It also takes care of parsing the input and output to and from JSON.
- **Target language**: The target language refers to the actual SDK code. For example, the [Node.js SDK](https://www.npmjs.com/package/@infisical/sdk) is a "target language", and so is the [Python SDK](https://pypi.org/project/infisical-python/).
### Understanding the execution flow
After the target language SDK is initiated, it uses language-specific bindings to interact with the base SDK.
These bindings are instantiated, setting up the interface for command execution. A client within the command handler is created, which issues commands to the base SDK.
When a command is executed, it is first validated. If valid, the command handler locates the corresponding command to perform. If the command executes successfully, the command handler returns the output to the target language SDK, where it is parsed and returned to the user.
If the command handler fails to validate the input, an error will be returned to the target language SDK.
<Frame caption="Execution flow diagram for the SDK from the target language to the base SDK. The execution flow is the same for all target languages.">
<img height="640" width="520" src="/images/sdk-flow.png" />
</Frame>
### Rust knowledge
Contributing to the SDK requires intermediate to advanced knowledge of Rust concepts such as lifetimes, traits, generics, and async/await _(futures)_, and more.
### Rust setup
The base SDK is written in rust. Therefore you must have rustc and cargo installed. You can install rustc and cargo by following the instructions [here](https://www.rust-lang.org/tools/install).
You shouldn't have to use the rust cross compilation toolchain, as all compilation is done through a collection of Github Actions. However. If you need to test cross compilation, please do so with Github Actions.
### Tests
If you add new functionality or modify existing functionality, please write tests thats properly cover the new functionality. You can run tests locally by running `cargo test` from the root directory.
### Language-specific crates
The language-specific crates should ideally never have to be modified, as they are simply a wrapper for the `infisical-json` crate, which executes "commands" from the base SDK. If you need to create a new target-language specific crate, please try to create native bindings for the target language. Some languages don't have direct support for native bindings (Java as an example). In those cases we can use the C bindings (`crates/infisical-c`) in the target language.
## Generate types
Having almost seemless type safety from the base SDK to the target language is critical, as writing types for each language has a lot of drawbacks such as duplicated code, and lots of overhead trying to keep the types up-to-date and in sync across a large collection of languages. Therefore we decided to use [QuickType](https://quicktype.io/) and [Serde](https://serde.rs/) to help us generate types for each language. In our Rust base SDK (`crates/infisical`), we define all the inputs/outputs.
If you are interested in reading about QuickType works under the hood, you can [read more here](http://blog.quicktype.io/under-the-hood/).
This is an example of a type defined in Rust (both input and output). For this to become a generated type, you'll need to add it to our schema generator. More on that further down.
```rust
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
// Input:
pub struct CreateSecretOptions {
pub environment: String, // environment
pub secret_comment: Option<String>, // secretComment
pub path: Option<String>, // secretPath
pub secret_value: String, // secretValue
pub skip_multiline_encoding: Option<bool>, // skipMultilineEncoding
pub r#type: Option<String>, // shared / personal
pub project_id: String, // workspaceId
pub secret_name: String, // secretName (PASSED AS PARAMETER IN REQUEST)
}
// Output:
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CreateSecretResponse {
pub secret: Secret, // "Secret" is defined elsewhere.
}
````
### Adding input types to the schema generator
You will _only_ have to define outputs in our schema generator, then QuickType will take care of the rest behind the scenes. You can find the Rust crate that takes care of type generation here: `crates/sdk-schemas/src/main.rs`.
Simply add the output _(also called response)_, to the `write_schema_for_response!` macro. This will let QuickType know that it should generate types for the given structs. The main function will look something like this:
```rust
fn main() -> Result<()> {
// Input types for new Client
write_schema_for!(infisical_json::client::ClientSettings);
// Input types for Client::run_command
write_schema_for!(infisical_json::command::Command);
// Output types for Client::run_command
// Only add structs which are direct results of SDK commands.
write_schema_for_response! {
infisical::manager::secrets::GetSecretResponse,
infisical::manager::secrets::ListSecretsResponse,
infisical::manager::secrets::UpdateSecretResponse,
infisical::manager::secrets::DeleteSecretResponse,
infisical::manager::secrets::CreateSecretResponse, // <-- This is the output from the above example!
infisical::auth::AccessTokenSuccessResponse
};
Ok(())
}
```
### Generating the types for the target language
Once you've added the output to the schema generator, you can generate the types for the target language by running the following command from the root directory:
```console
$ npm install
$ npm run schemas
```
<Warning>If you change any of the structs defined in the base SDK, you will need to run this script to re-generate the types.</Warning>
This command will run the `schemas.ts` file found in the `support/scripts` folder. If you are adding a new language, it's important that you add the language to the code.
This is an example of how how we generate types for Node.js:
```ts
const ts = await quicktype({
inputData,
lang: "typescript",
rendererOptions: {}
});
await ensureDir("./languages/node/src/infisical_client");
writeToFile("./languages/node/src/infisical_client/schemas.ts", ts.lines);
```
## Building bindings
We've tried to streamline the building process as much as possible. So you shouldn't have to worry much about building bindings, as it should just be a few commands.
### Node.js
Building bindings for Node.js is very straight foward. The command below will generate NAPI bindings for Node.js, and move the bindings to the correct folder. We use [NAPI-RS](https://napi.rs/) to generate the bindings.
```console
$ cd languages/node
$ npm run build
```
### Python
To generate and use python bindings you will need to run the following commands.
The Python SDK is located inside the crates folder. This is a limitation of the maturin tool, forcing us to structure the project in this way.
```console
$ pip install -U pip maturin
$ cd crates/infisical-py
$ python3 -m venv .venv
$ source .venv/bin/activate
$ maturin develop
```
<Warning>
After running the commands above, it's very important that you rename the generated .so file to `infisical_py.so`. After renaming it you also need to move it into the root of the `crates/infisical-py` folder.
</Warning>
### Java
Java uses the C bindings to interact with the base SDK. To build and use the C bindings in Java, please follow the instructions below.
```console
$ cd crates/infisical-c
$ cargo build --release
$ cd ../../languages/java
```
<Warning>
After generating the C bindings, the generated .so or .dll has been created in the `/target` directory at the root of the project.
You have to manually move the generated file into the `languages/java/src/main/resources` directory.
</Warning>
## Error handling
### Error handling in the base SDK
The base SDK should never panic. If an error occurs, we should return a `Result` with an error message. We have a custom Result type defined in the `error.rs` file in the base SDK.
All our errors are defined in an enum called `Error`. The `Error` enum is defined in the `error.rs` file in the base SDK. The `Error` enum is used in the `Result` type, which is used as the return type for all functions in the base SDK.
```rust
#[derive(Debug, Error)]
pub enum Error {
// Secret not found
#[error("Secret with name '{}' not found.", .secret_name)]
SecretNotFound { secret_name: String },
// .. other errors
// Errors that are not specific to the base SDK.
#[error(transparent)]
Reqwest(#[from] reqwest::Error),
#[error(transparent)]
Serde(#[from] serde_json::Error),
#[error(transparent)]
Io(#[from] std::io::Error),
}
```
### Returning an error
You can find many examples of how we return errors in the SDK code. A relevant example is for creating secrets, which can be found in `crates/infisical/src/api/secrets/create_secret.rs`. When the error happened due to a request error to our API, we have an API error handler. This prevents duplicate code and keeps error handling consistent across the SDK. You can find the api error handler in the `error.rs` file.
### Error handling in the target language SDK's.
All data sent to the target language SDK has the same format. The format is an object with 3 fields: `success (boolean)`, `data (could be anything or nothing)`, and `errorMessage (string or null)`.
The `success` field is used to determine if the request was successful or not. The `data` field is used to return data from the SDK. The `errorMessage` field is used to return an error message if the request was not successful.
This means that if the success if false or if the error message is not null, something went wrong and we should throw an error on the target-language level, with the error message.
## Command handler
### What is the command handler
The command handler (the `infisical-json` crate), takes care of executing commands sent from the target language. It also takes care of parsing the input and output to and from JSON. The command handler is the only part of the base SDK that should be aware of JSON. The rest of the base SDK should be completely unaware of JSON, and only work with the Rust structs defined in the base SDK.
The command handler exposes a function called `run_command`, which is what we use in the target language to execute commands. The function takes a json string as input, and returns a json string as output. We use helper functions generated by QuickType to convert the input and output to and from JSON.
### Creating new SDK methods
Creating new commands is necessary when adding new methods to the SDK's. Defining a new command is a 3-step process in most cases.
#### 1. Define the input and output structs
Earlier in this guide, we defined the input and output structs for the `CreateSecret` command. We will use that as an example here as well.
#### 2. Creating the method in the base SDK
The first step is to create the method in the base SDK. This step will be different depending on what method you are adding. In this example we're going to assume you're adding a function for creating a new secret.
After you created the function for creating the secret, you'll need need to add it to the ClientSecrets implementation. We do it this way to keep the code organized and easy to read. The ClientSecrets struct is located in the `crates/infisical/src/manager/secrets.rs` file.
```rust
pub struct ClientSecrets<'a> {
pub(crate) client: &'a mut crate::Client,
}
impl<'a> ClientSecrets<'a> {
pub async fn create(&mut self, input: &CreateSecretOptions) -> Result<CreateSecretResponse> {
create_secret(self.client, input).await // <-- This is the function you created!
}
}
impl<'a> Client {
pub fn secrets(&'a mut self) -> ClientSecrets<'a> {
ClientSecrets { client: self }
}
}
```
#### 3. Define a new command
We define new commands in the `crates/infisical-json/src/command.rs` file. The `Command` enum is what we use to define new commands.
In the codesnippet below we define a new command called `CreateSecret`. The `CreateSecret` command takes a `CreateSecretOptions` struct as input. We don't have to define the output, because QuickType's converter helps us with figuring out the return type for each command.
````rust
```rust
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum Command {
GetSecret(GetSecretOptions),
ListSecrets(ListSecretsOptions),
CreateSecret(CreateSecretOptions), // <-- The new command!
UpdateSecret(UpdateSecretOptions),
DeleteSecret(DeleteSecretOptions),
}
````
#### 4. Add the command to the command handler
After defining the command, we need to add it to the command handler itself. This takes place in the `crates/infisical-json/src/client.rs` file. The `run_command` function is what we use to execute commands.
In the Client implementation we try to parse the JSON string into a `Command` enum. If the parsing is successful, we match the command and execute the corresponding function.
```rust
match cmd {
Command::GetSecret(req) => self.0.secrets().get(&req).await.into_string(),
Command::ListSecrets(req) => self.0.secrets().list(&req).await.into_string(),
Command::UpdateSecret(req) => self.0.secrets().update(&req).await.into_string(),
Command::DeleteSecret(req) => self.0.secrets().delete(&req).await.into_string(),
// This is the new command:
Command::CreateSecret(req) => self.0.secrets().create(&req).await.into_string(),
}
```
#### 5. Implementing the new command in the target language SDK's
We did it! We've now added a new command to the base SDK. The last step is to implement the new command in the target language SDK's. The process is a little different from language to language, but in this example we're going to assume that we're adding a new command to the Node.js SDK.
First you'll need to generate the new type schemas, we added a new command, input struct, and output struct. [Read more about generating types here](#generating-the-types-for-the-target-language).
Secondly you need to build the new node bindings so we can use the new functionality in the Node.js SDK. You can do this by running the following command from the `languages/node` directory:
```console
$ npm install
$ npm run build
```
The build command will execute a build script in the `infisical-napi` crate, and move the generated bindings to the appropriate folder.
After building the new bindings, you can access the new functionality in the Node.js SDK source.
```ts
// 'binding' is a js file that makes it easier to access the methods in the bindings. (it's auto generated when running npm run build)
import * as rust from "../../binding";
// We can import the newly generated types from the schemas.ts file. (Generated with QuickType!)
import type { CreateSecretOptions, CreateSecretResponse } from "./schemas";
// This is the QuickType converter that we use to create commands with! It takes care of all JSON parsing and serialization.
import { Convert, ClientSettings } from "./schemas";
export class InfisicalClient {
#client: rust.Client;
constructor(settings: ClientSettings) {
const settingsJson = settings == null ? null : Convert.clientSettingsToJson(settings);
this.#client = new rust.InfisicalClient(settingsJson);
}
// ... getSecret
// ... listSecrets
// ... updateSecret
// ... deleteSecret
async createSecret(options: CreateSecretOptions): Promise<CreateSecretResponse["secret"]> {
// The runCommand will return a JSON string, which we can parse into a CreateSecretResponse.
const command = await this.#client.runCommand(
Convert.commandToJson({
createSecret: options
})
);
const response = Convert.toResponseForCreateSecretResponse(command); // <-- This is the QuickType converter in action!
// If the response is not successful or the data is null, we throw an error.
if (!response.success || response.data == null) {
throw new Error(response.errorMessage ?? "Something went wrong");
}
// To make it easier to work with the response, we return the secret directly.
return response.data.secret;
}
}
```
And that's it! We've now added a new command to the base SDK, and implemented it in the Node.js SDK. The process is very similar for all other languages, but the code will look a little different.
## Conclusion
The SDK has a lot of moving parts, and it can be a little overwhelming at first. But once you get the hang of it, it's actually quite simple. If you have any questions, feel free to reach out to us on [Slack](https://infisical.com/slack), or [open an issue](https://github.com/Infisical/sdk/issues) on GitHub.

@ -13,7 +13,8 @@ Prerequisites:
Follow the instructions for your language use the SDK for it:
- [Node SDK](https://github.com/Infisical/infisical-node)
- [Python SDK](https://github.com/Infisical/infisical-python)
- [Node SDK](https://infisical.com/docs/sdks/languages/node)
- [Python SDK](https://infisical.com/docs/sdks/languages/python)
- [Java SDK](https://infisical.com/docs/sdks/languages/java)
Missing a language? [Throw in a request](https://github.com/Infisical/infisical/issues).

@ -240,12 +240,6 @@ At this stage, you know how to use the Infisical-Vercel integration to sync prod
Check out the [security guide](/security/overview).
</Accordion>
<Accordion title="Is there way to retain end-to-end encryption for syncing production secrets to Vercel?">
Yes. You can also use the Infisical [Node SDK](https://github.com/Infisical/infisical-node) to fetch secrets back to your Next.js app
in both development and production.
Depending on how you use it, however, it may require certain pages to be server-side rendered.
</Accordion>
</AccordionGroup>
See also:

@ -4,8 +4,7 @@ description: "Programmatically interact with Infisical"
---
<Note>
Currently, identities can only be used to make authenticated requests to the Infisical API and do not work with any clients such as [Node SDK](https://github.com/Infisical/infisical-node)
, [Python SDK](https://github.com/Infisical/infisical-python), CLI, K8s operator, Terraform Provider, etc.
Currently, identities can only be used to make authenticated requests to the Infisical API and SDKs. They do not work with clients such as CLI, K8s Operator, Terraform Provider, etc.
We will be releasing compatibility with it across clients in the coming quarter.
</Note>

@ -12,8 +12,8 @@ This means that updating the value of a base secret propagates directly to other
Currently, the secret referencing feature is only supported by the
[Infisical CLI](/cli/overview) and [native integrations](/integrations/overview).
We intend to add support for it to the [Node SDK](https://github.com/Infisical/infisical-node)
and [Python SDK](https://github.com/Infisical/infisical-python) this quarter.
We intend to add support for it to the [Node SDK](https://infisical.com/docs/sdks/languages/node),
[Python SDK](https://infisical.com/docs/sdks/languages/python), and [Java SDK](https://infisical.com/docs/sdks/languages/java) this quarter.
</Note>
![secret referencing](../../images/platform/secret-references-imports/secret-reference.png)

BIN
docs/images/sdk-flow.png Normal file

Binary file not shown.

After

(image error) Size: 881 KiB

@ -25,6 +25,6 @@ The Web UI is the browser-based portal that connects to the Infisical API.
Clients are any application or infrastructure that connecting to the Infisical API using one of the below methods:
- Public API: Making API requests directly to the Infisical API.
- Client SDK: A platform-specific library with method abstractions for working with secrets. Currently, there are two official SDKs: [Node SDK](https://github.com/Infisical/infisical-node) and [Python SDK](https://github.com/Infisical/infisical-python).
- Client SDK: A platform-specific library with method abstractions for working with secrets. Currently, there are three official SDKs: [Node SDK](https://infisical.com/docs/sdks/languages/node), [Python SDK](https://infisical.com/docs/sdks/languages/python), and [Java SDK](https://infisical.com/docs/sdks/languages/java).
- CLI: A terminal-based interface for interacting with the Infisical API.
- Kubernetes Operator: This operator retrieves secrets from Infisical and securely store

@ -448,13 +448,30 @@
"pages": ["changelog/overview"]
},
{
"group": "Contributing",
"group": "",
"pages": [
"contributing/overview",
"contributing/code-of-conduct",
"contributing/developing",
"contributing/pull-requests",
"contributing/faq"
{
"group": "Getting Started",
"pages": [
"contributing/getting-started/overview",
"contributing/getting-started/code-of-conduct",
"contributing/getting-started/pull-requests",
"contributing/getting-started/faq"
]
},
{
"group": "Contributing to platform",
"pages": [
"contributing/platform/developing"
]
},
{
"group": "Contributing to SDK",
"pages": [
"contributing/sdk/developing"
]
}
]
}
],

@ -19,6 +19,7 @@ public class Example {
ClientSettings settings = new ClientSettings();
settings.setClientID("MACHINE_IDENTITY_CLIENT_ID");
settings.setClientSecret("MACHINE_IDENTITY_CLIENT_SECRET");
settings.setCacheTTL(Long.valueOf(300)); // 300 seconds, 5 minutes
InfisicalClient client = new InfisicalClient(settings);
@ -88,6 +89,11 @@ public class App {
An access token obtained from the machine identity login endpoint.
</ParamField>
<ParamField query="setCacheTTL()" type="number" default="300" optional>
Time-to-live (in seconds) for refreshing cached secrets.
If manually set to 0, caching will be disabled, this is not recommended.
</ParamField>
<ParamField query="setSiteURL()" type="string" default="https://app.infisical.com" optional>
Your self-hosted absolute site URL including the protocol (e.g. `https://app.infisical.com`)
</ParamField>
@ -95,6 +101,10 @@ public class App {
</ParamField>
### Caching
To reduce the number of API requests, the SDK temporarily stores secrets it retrieves. By default, a secret remains cached for 5 minutes after it's first fetched. Each time it's fetched again, this 5-minute timer resets. You can adjust this caching duration by setting the "cacheTTL" option when creating the client.
## Working with Secrets
### client.listSecrets(options)
@ -127,7 +137,11 @@ Retrieve all secrets within the Infisical project and environment that client is
The path from where secrets should be fetched from.
</ParamField>
<ParamField query="setIncludeImports" type="string" default="https://app.infisical.com" optional>
<ParamField query="setAttachToProcessEnv()" type="boolean" default="false" optional>
Whether or not to set the fetched secrets to the process environment. If true, you can access the secrets like so `System.getenv("SECRET_NAME")`.
</ParamField>
<ParamField query="setIncludeImports()" type="boolean" default="false" optional>
Whether or not to include imported secrets from the current path. Read about [secret import](/documentation/platform/secret-reference)
</ParamField>
</Expandable>

@ -104,6 +104,11 @@ Import the SDK and create a client instance with your [Machine Identity](/docume
An access token obtained from the machine identity login endpoint.
</ParamField>
<ParamField query="cacheTtl" type="number" default="300" optional>
Time-to-live (in seconds) for refreshing cached secrets.
If manually set to 0, caching will be disabled, this is not recommended.
</ParamField>
<ParamField query="siteUrl" type="string" default="https://app.infisical.com" optional>
Your self-hosted absolute site URL including the protocol (e.g. `https://app.infisical.com`)
</ParamField>
@ -113,6 +118,11 @@ Import the SDK and create a client instance with your [Machine Identity](/docume
</Expandable>
</ParamField>
### Caching
To reduce the number of API requests, the SDK temporarily stores secrets it retrieves. By default, a secret remains cached for 5 minutes after it's first fetched. Each time it's fetched again, this 5-minute timer resets. You can adjust this caching duration by setting the "cacheTtl" option when creating the client.
## Working with Secrets
### client.listSecrets(options)
@ -143,7 +153,11 @@ Retrieve all secrets within the Infisical project and environment that client is
The path from where secrets should be fetched from.
</ParamField>
<ParamField query="includeImports" type="string" default="https://app.infisical.com" optional>
<ParamField query="attachToProcessEnv" type="boolean" default="false" optional>
Whether or not to set the fetched secrets to the process environment. If true, you can access the secrets like so `process.env["SECRET_NAME"]`.
</ParamField>
<ParamField query="includeImports" type="false" default="boolean" optional>
Whether or not to include imported secrets from the current path. Read about [secret import](/documentation/platform/secret-reference)
</ParamField>
</Expandable>

@ -62,24 +62,40 @@ client = InfisicalClient(ClientSettings(
### Parameters
<ParamField query="client_id" type="string" optional>
Your Infisical Client ID.
</ParamField>
<ParamField query="client_secret" type="string" optional>
Your Infisical Client Secret.
</ParamField>
<ParamField query="access_token" type="string" optional>
If you want to directly pass an access token obtained from the authentication endpoints, you can do so.
</ParamField>
<ParamField
query="site_url"
type="string"
default="https://app.infisical.com"
optional
>
Your self-hosted absolute site URL including the protocol (e.g.
`https://app.infisical.com`)
</ParamField>
<ParamField query="options" type="object">
<Expandable title="properties">
<ParamField query="client_id" type="string" optional>
Your Infisical Client ID.
</ParamField>
<ParamField query="client_secret" type="string" optional>
Your Infisical Client Secret.
</ParamField>
<ParamField query="access_token" type="string" optional>
If you want to directly pass an access token obtained from the authentication endpoints, you can do so.
</ParamField>
<ParamField query="cache_ttl" type="number" default="300" optional>
Time-to-live (in seconds) for refreshing cached secrets.
If manually set to 0, caching will be disabled, this is not recommended.
</ParamField>
<ParamField
query="site_url"
type="string"
default="https://app.infisical.com"
optional
>
Your self-hosted absolute site URL including the protocol (e.g.
`https://app.infisical.com`)
</ParamField>
</Expandable>
</ParamField>
### Caching
To reduce the number of API requests, the SDK temporarily stores secrets it retrieves. By default, a secret remains cached for 5 minutes after it's first fetched. Each time it's fetched again, this 5-minute timer resets. You can adjust this caching duration by setting the "cache_ttl" option when creating the client.
## Working with Secrets
@ -109,7 +125,11 @@ Retrieve all secrets within the Infisical project and environment that client is
The path from where secrets should be fetched from.
</ParamField>
<ParamField query="include_imports" type="string" default="https://app.infisical.com" optional>
<ParamField query="attach_to_process_env" type="boolean" default="false" optional>
Whether or not to set the fetched secrets to the process environment. If true, you can access the secrets like so `process.env["SECRET_NAME"]`.
</ParamField>
<ParamField query="include_imports" type="boolean" default="false" optional>
Whether or not to include imported secrets from the current path. Read about [secret import](/documentation/platform/secret-reference)
</ParamField>
</Expandable>
@ -148,7 +168,7 @@ By default, `getSecret()` fetches and returns a shared secret. If not found, it
<ParamField query="type" type="string" optional>
The type of the secret. Valid options are "shared" or "personal". If not specified, the default value is "personal".
</ParamField>
<ParamField query="include_imports" type="string" default="https://app.infisical.com" optional>
<ParamField query="include_imports" type="boolean" default="false" optional>
Whether or not to include imported secrets from the current path. Read about [secret import](/documentation/platform/secret-reference)
</ParamField>
</Expandable>

@ -31,3 +31,23 @@ From local development to production, Infisical SDKs provide the easiest way for
Manage secrets for your PHP application on demand
</Card>
</CardGroup>
## FAQ
<AccordionGroup>
<Accordion title="Isn't it inefficient if my app makes a request every time it needs a secret?">
The client SDK caches every secret and implements a 5-minute waiting period before re-requesting it. The waiting period can be controlled by
setting the `cacheTTL` parameter at the time of initializing the client.
Note: The exact parameter name may differ depending on the language.
</Accordion>
<Accordion title="Can I attach the environment variables to my process environment?">
Yes you can! The client SDK provides a method to attach the secrets to your process environment. When using the `listSecrets()` method, you
can pass a `attachToProcessEnv` parameter, which tells the SDK to attach all the found secrets to your process environment.
Note: The exact parameter name may differ depending on the language.
</Accordion>
<Accordion title="What if a request for a secret fails?">
The SDK caches every secret and falls back to the cached value if a request fails. If no cached value is found, and the request fails, then the SDK throws an error.
</Accordion>
</AccordionGroup>

@ -3133,6 +3133,7 @@ paths:
description: Projects of organization
security:
- apiKeyAuth: []
bearerAuth: []
/api/v2/organizations/:
post:
description: ''

@ -49,7 +49,7 @@ export default function DonwloadBackupPDFStep({
console.log(err);
createNotification({
type: "error",
text: "Faield to generate backup key"
text: "Failed to generate backup key"
});
} finally {
setIsLoading.off();

@ -50,5 +50,8 @@ export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
[UserAgentType.WEB]: "Web",
[UserAgentType.CLI]: "CLI",
[UserAgentType.K8_OPERATOR]: "K8s operator",
[UserAgentType.TERRAFORM]: "Terraform",
[UserAgentType.NODE_SDK]: "InfisicalNodeSDK",
[UserAgentType.PYTHON_SDK]: "InfisicalPythonSDK",
[UserAgentType.OTHER]: "Other",
};

@ -8,6 +8,9 @@ export enum UserAgentType {
WEB = "web",
CLI = "cli",
K8_OPERATOR = "k8-operator",
TERRAFORM = "terraform",
NODE_SDK = "node-sdk",
PYTHON_SDK = "python-sdk",
OTHER = "other"
}

@ -297,6 +297,7 @@ const fetchIntegrationAuthRailwayEnvironments = async ({
integrationAuthId: string;
appId: string;
}) => {
if (appId === "none") return [];
const {
data: { environments }
} = await apiRequest.get<{ environments: Environment[] }>(
@ -318,6 +319,7 @@ const fetchIntegrationAuthRailwayServices = async ({
integrationAuthId: string;
appId: string;
}) => {
if (appId === "none") return [];
const {
data: { services }
} = await apiRequest.get<{ services: Service[] }>(

@ -716,6 +716,12 @@ export const AppLayout = ({ children }: LayoutProps) => {
</a>
</DropdownMenuItem>
))}
{infisicalPlatformVersion && (
<div className="cursor-default mb-2 mt-2 w-full pl-5 duration-200 hover:text-mineshaft-200 text-sm">
<FontAwesomeIcon icon={faInfo} className="mr-4 px-[0.1rem]" />
Version: {infisicalPlatformVersion}
</div>
)}
</DropdownMenuContent>
</DropdownMenu>
{subscription &&
@ -745,12 +751,6 @@ export const AppLayout = ({ children }: LayoutProps) => {
</div>
</button>
)}
{infisicalPlatformVersion && (
<div className="mb-2 w-full pl-5 duration-200 hover:text-mineshaft-200">
<FontAwesomeIcon icon={faInfo} className="mr-4 px-[0.1rem]" />
Version: {infisicalPlatformVersion}
</div>
)}
</div>
</nav>
</aside>

@ -76,10 +76,15 @@ export default function RailwayCreateIntegrationPage() {
}
}, [targetEnvironments]);
const filteredServices = targetServices?.concat({
name: "",
serviceId: ""
});
useEffect(() => {
if (targetServices) {
if (targetServices.length > 0) {
setTargetServiceId(targetServices[0].serviceId);
} else {
setTargetServiceId("none");
}
}
}, [targetServices]);
const handleButtonClick = async () => {
try {
@ -125,7 +130,7 @@ export default function RailwayCreateIntegrationPage() {
selectedSourceEnvironment &&
integrationAuthApps &&
targetEnvironments &&
filteredServices ? (
targetServices ? (
<div className="flex h-full w-full items-center justify-center">
<Card className="max-w-md rounded-md p-8">
<CardTitle className="text-center">Railway Integration</CardTitle>
@ -180,6 +185,7 @@ export default function RailwayCreateIntegrationPage() {
value={targetEnvironmentId}
onValueChange={(val) => setTargetEnvironmentId(val)}
className="w-full border border-mineshaft-500"
isDisabled={targetEnvironments.length === 0}
>
{targetEnvironments.length > 0 ? (
targetEnvironments.map((targetEnvironment) => (
@ -202,15 +208,22 @@ export default function RailwayCreateIntegrationPage() {
value={targetServiceId}
onValueChange={(val) => setTargetServiceId(val)}
className="w-full border border-mineshaft-500"
isDisabled={targetServices.length === 0}
>
{filteredServices.map((targetService) => (
<SelectItem
value={targetService.serviceId as string}
key={`target-service-${targetService.serviceId as string}`}
>
{targetService.name}
{targetServices.length > 0 ? (
targetServices.map((targetService) => (
<SelectItem
value={targetService.serviceId as string}
key={`target-service-${targetService.serviceId as string}`}
>
{targetService.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-service-none">
No services found
</SelectItem>
))}
)}
</Select>
</FormControl>
<Button

@ -56,7 +56,7 @@ export default function VercelCreateIntegrationPage() {
appId: targetAppId
});
const filteredBranches = branches?.filter((branchName) => branchName !== "main").concat("");
const filteredBranches = branches?.filter((branchName) => branchName !== "main").concat();
useEffect(() => {
if (workspace) {
@ -125,7 +125,7 @@ export default function VercelCreateIntegrationPage() {
subTitle="Select which environment or folder in Infisical you want to sync to Vercel's environment variables."
>
<div className="flex flex-row items-center">
<div className="inline flex items-center">
<div className="flex items-center">
<Image
src="/images/integrations/Vercel.png"
height={30}

@ -67,8 +67,16 @@ const features = [
{
_id: 0,
name: "Kubernetes Operator",
link: "https://infisical.com/docs/documentation/getting-started/kubernetes",
description:
"Pull secrets into your Kubernetes containers and automatically redeploy upon secret changes."
},
{
_id: 1,
name: "Infisical Agent",
link: "https://infisical.com/docs/infisical-agent/overview",
description:
"Inject secrets into your apps without modifying any application logic."
}
];
@ -691,11 +699,44 @@ const OrganizationPage = withPermission(
</div>
)}
</div>
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-6 text-3xl">
<p className="mr-4 font-semibold text-white">Explore Infisical</p>
<div className="mt-4 grid grid-cols-3 w-full gap-4">
{features.map((feature) => (
<div
key={feature._id}
className="flex h-44 w-full flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">{feature.name}</div>
<div className="mb-4 mt-2 text-[15px] font-light text-mineshaft-300">
{feature.description}
</div>
<div className="flex w-full items-center">
<div className="text-[15px] font-light text-mineshaft-300">
Setup time: 20 min
</div>
<a
target="_blank"
rel="noopener noreferrer"
className="group ml-auto w-max cursor-default rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200"
href={feature.link}
>
Learn more{" "}
<FontAwesomeIcon
icon={faArrowRight}
className="pl-1.5 pr-0.5 duration-200 group-hover:pl-2 group-hover:pr-0"
/>
</a>
</div>
</div>
))}
</div>
</div>
{!(
new Date().getTime() - new Date(user?.createdAt).getTime() <
30 * 24 * 60 * 60 * 1000
) && (
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
<div className="mb-4 flex flex-col items-start justify-start px-6 pb-6 pb-0 text-3xl">
<p className="mr-4 mb-4 font-semibold text-white">Onboarding Guide</p>
<div className="mb-3 grid w-full grid-cols-1 gap-3 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
<LearningItemSquare
@ -784,42 +825,6 @@ const OrganizationPage = withPermission(
)}
</div>
)}
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-6 text-3xl">
<p className="mr-4 font-semibold text-white">Explore More</p>
<div
className="mt-4 grid w-full grid-flow-dense gap-4"
style={{ gridTemplateColumns: "repeat(auto-fill, minmax(256px, 4fr))" }}
>
{features.map((feature) => (
<div
key={feature._id}
className="flex h-44 w-96 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">{feature.name}</div>
<div className="mb-4 mt-2 text-[15px] font-light text-mineshaft-300">
{feature.description}
</div>
<div className="flex w-full items-center">
<div className="text-[15px] font-light text-mineshaft-300">
Setup time: 20 min
</div>
<a
target="_blank"
rel="noopener noreferrer"
className="group ml-auto w-max cursor-default rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200"
href="https://infisical.com/docs/documentation/getting-started/kubernetes"
>
Learn more{" "}
<FontAwesomeIcon
icon={faArrowRight}
className="pl-1.5 pr-0.5 duration-200 group-hover:pl-2 group-hover:pr-0"
/>
</a>
</div>
</div>
))}
</div>
</div>
<Modal
isOpen={popUp.addNewWs.isOpen}
onOpenChange={(isModalOpen) => {

@ -1,4 +1,5 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import Link from "next/link";
import { faArrowUpRightFromSquare, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
@ -65,17 +66,24 @@ export const IdentitySection = withPermission(
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8">
<div className="flex justify-between mb-4">
<p className="text-xl font-semibold text-mineshaft-100">
Identities
</p>
<div className="flex justify-end w-full pr-4">
<Link href="https://infisical.com/docs/documentation/platform/identities/overview">
<a target="_blank" rel="noopener noreferrer" className="rounded-md px-4 py-2 w-max text-mineshaft-200 hover:text-white bg-mineshaft-600 border border-mineshaft-500 hover:bg-primary/10 hover:border-primary/40 duration-200 cursor-pointer">
Documentation <FontAwesomeIcon icon={faArrowUpRightFromSquare} className="text-xs mb-[0.06rem] ml-1"/>
</a>
</Link>
</div>
<OrgPermissionCan
I={OrgPermissionActions.Create}
a={OrgPermissionSubjects.Identity}
>
{(isAllowed) => (
<Button
colorSchema="secondary"
colorSchema="primary"
type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handlePopUpOpen("identity")}

@ -98,6 +98,7 @@ export const IdentityTable = ({
<THead>
<Tr>
<Th>Name</Th>
<Th>ID</Th>
<Th>Role</Th>
<Th>Auth Method</Th>
<Th className="w-5" />
@ -120,6 +121,7 @@ export const IdentityTable = ({
return (
<Tr className="h-10" key={`identity-${_id}`}>
<Td>{name}</Td>
<Td>{_id}</Td>
<Td>
<OrgPermissionCan
I={OrgPermissionActions.Edit}

@ -89,7 +89,7 @@ export const OrgMembersSection = () => {
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8">
<div className="flex justify-between mb-4">
<p className="text-xl font-semibold text-mineshaft-100">
Members
</p>
@ -99,7 +99,7 @@ export const OrgMembersSection = () => {
>
{(isAllowed) => (
<Button
colorSchema="secondary"
colorSchema="primary"
type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handleAddMemberModal()}

@ -191,9 +191,9 @@ export const IdentityModal = ({
</form>
) : (
<div className="flex flex-col space-y-4">
<div>All identities in your organization are already added.</div>
<div className="text-sm">All identities in your organization have already been added to this project.</div>
<Link href={`/org/${currentWorkspace?.organization}/members`}>
<Button variant="outline_bg">Create a new/another identities</Button>
<Button variant="outline_bg">Create a new identity</Button>
</Link>
</div>
)}

@ -1,4 +1,5 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import Link from "next/link";
import { faArrowUpRightFromSquare, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
@ -61,17 +62,24 @@ export const IdentitySection = withProjectPermission(
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8">
<div className="flex justify-between items-center mb-4">
<p className="text-xl font-semibold text-mineshaft-100">
Identities
</p>
<div className="flex justify-end w-full pr-4">
<Link href="https://infisical.com/docs/documentation/platform/identities/overview">
<span className="rounded-md px-4 py-2 w-max text-mineshaft-200 hover:text-white bg-mineshaft-600 border border-mineshaft-500 hover:bg-primary/10 hover:border-primary/40 duration-200 cursor-pointer">
Documentation <FontAwesomeIcon icon={faArrowUpRightFromSquare} className="text-xs mb-[0.06rem] ml-1"/>
</span>
</Link>
</div>
<ProjectPermissionCan
I={ProjectPermissionActions.Create}
a={ProjectPermissionSub.Identity}
>
{(isAllowed) => (
<Button
colorSchema="secondary"
colorSchema="primary"
type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handlePopUpOpen("identity")}

@ -254,7 +254,7 @@ export const MemberListTab = () => {
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between mb-8">
<div className="flex justify-between items-center mb-4">
<p className="text-xl font-semibold text-mineshaft-100">
Members
</p>
@ -264,7 +264,7 @@ export const MemberListTab = () => {
>
{(isAllowed) => (
<Button
colorSchema="secondary"
colorSchema="primary"
type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handlePopUpOpen("addMember")}

@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
import { faCheck, faCopy, faPlus, faTrashCan } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup";
import { AxiosError } from "axios";
import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
@ -160,10 +161,18 @@ export const AddServiceTokenModal = ({ popUp, handlePopUpToggle }: Props) => {
});
} catch (err) {
console.error(err);
createNotification({
text: "Failed to create a service token",
type: "error"
});
const axiosError = err as AxiosError
if (axiosError?.response?.status === 401) {
createNotification({
text: "You do not have access to the selected environment/path",
type: "error"
});
} else {
createNotification({
text: "Failed to create a service token",
type: "error"
});
}
}
};

@ -1,3 +1,7 @@
import Link from "next/link";
import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { useWorkspace } from "@app/context";
@ -15,8 +19,18 @@ export const SecretApprovalPage = () => {
return (
<div className="container mx-auto bg-bunker-800 text-white w-full h-full max-w-7xl px-6">
<div className="my-6">
<p className="text-3xl font-semibold text-gray-200">Secret Approvals</p>
<div className="py-6 flex justify-between items-center">
<div className="flex flex-col w-full">
<h2 className="text-3xl font-semibold text-gray-200">Secret Approval Workflows</h2>
<p className="text-bunker-300">Create approval policies for any modifications to secrets in sensitive environments and folders.</p>
</div>
<div className="flex justify-center w-max">
<Link href="https://infisical.com/docs/documentation/platform/pr-workflows">
<span className="rounded-md px-4 py-2 w-max text-mineshaft-200 hover:text-white bg-mineshaft-600 border border-mineshaft-500 hover:bg-primary/10 hover:border-primary/40 duration-200 cursor-pointer">
Documentation <FontAwesomeIcon icon={faArrowUpRightFromSquare} className="text-xs mb-[0.06rem] ml-1"/>
</span>
</Link>
</div>
</div>
<Tabs defaultValue={TabSection.ApprovalRequests}>
<TabList>

@ -1,5 +1,6 @@
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { AxiosError } from "axios";
import { z } from "zod";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
@ -78,12 +79,20 @@ export const CreateSecretImportForm = ({
type: "success",
text: "Successfully linked"
});
} catch (error) {
console.log(error);
createNotification({
type: "error",
text: "Failed to link secrets"
});
} catch (err) {
console.error(err);
const axiosError = err as AxiosError
if (axiosError?.response?.status === 401) {
createNotification({
text: "You do not have access to the selected environment/path",
type: "error"
});
} else {
createNotification({
type: "error",
text: "Failed to link secrets"
});
}
}
};

@ -108,11 +108,11 @@ export const FolderListView = ({
key={id}
className="flex group border-b border-mineshaft-600 hover:bg-mineshaft-700 cursor-pointer"
>
<div className="w-12 px-4 py-2 text-yellow-700 flex items-center">
<div className="w-11 px-5 py-3 text-yellow-700 flex items-center">
<FontAwesomeIcon icon={faFolder} />
</div>
<div
className="flex-grow px-4 py-2 flex items-center"
className="flex-grow px-4 py-3 flex items-center"
role="button"
tabIndex={0}
onKeyDown={(evt) => {
@ -122,7 +122,7 @@ export const FolderListView = ({
>
{name}
</div>
<div className="px-3 py-2 flex items-center space-x-4 border-l border-mineshaft-600">
<div className="px-3 py-3 flex items-center space-x-4 border-l border-mineshaft-600">
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}

@ -270,22 +270,39 @@ export const SecretOverviewPage = () => {
<p className="text-md text-bunker-300">
Inject your secrets using
<a
className="mx-1 text-primary/80 hover:text-primary"
className="ml-1 text-mineshaft-300 underline underline-offset-4 decoration-primary-800 hover:decoration-primary-600 hover:text-mineshaft-100 duration-200"
href="https://infisical.com/docs/cli/overview"
target="_blank"
rel="noopener noreferrer"
>
Infisical CLI
</a>
or
</a>,
<a
className="mx-1 text-primary/80 hover:text-primary"
className="ml-1 text-mineshaft-300 underline underline-offset-4 decoration-primary-800 hover:decoration-primary-600 hover:text-mineshaft-100 duration-200"
href="https://infisical.com/docs/documentation/getting-started/api"
target="_blank"
rel="noopener noreferrer"
>
Infisical API
</a>
,
<a
className="ml-1 text-mineshaft-300 underline underline-offset-4 decoration-primary-800 hover:decoration-primary-600 hover:text-mineshaft-100 duration-200"
href="https://infisical.com/docs/sdks/overview"
target="_blank"
rel="noopener noreferrer"
>
Infisical SDKs
</a>
, and
<a
className="ml-1 text-mineshaft-300 underline underline-offset-4 decoration-primary-800 hover:decoration-primary-600 hover:text-mineshaft-100 duration-200"
href="https://infisical.com/docs/documentation/getting-started/introduction"
target="_blank"
rel="noopener noreferrer"
>
more
</a>.
</p>
</div>
<div className="mt-8 flex items-center justify-between">
@ -306,7 +323,7 @@ export const SecretOverviewPage = () => {
<THead>
<Tr className="sticky top-0 z-20 border-0">
<Th className="sticky left-0 z-20 min-w-[20rem] border-b-0 p-0">
<div className="flex items-center border-b border-r border-mineshaft-600 px-5 pt-4 pb-3.5">
<div className="flex items-center border-b border-r border-mineshaft-600 px-5 pt-3.5 pb-3">
Name
<IconButton
variant="plain"
@ -326,7 +343,7 @@ export const SecretOverviewPage = () => {
className="min-table-row min-w-[11rem] border-b-0 p-0 text-center"
key={`secret-overview-${name}-${index + 1}`}
>
<div className="flex items-center justify-center border-b border-mineshaft-600 px-5 pt-3.5 pb-3">
<div className="flex items-center justify-center border-b border-mineshaft-600 px-5 pt-3.5 pb-[0.83rem]">
<button
type="button"
className="text-sm font-medium duration-100 hover:text-mineshaft-100"
@ -382,7 +399,7 @@ export const SecretOverviewPage = () => {
</Td>
</Tr>
)}
{filteredFolderNames.map((folderName, index) => (
{!isTableLoading && filteredFolderNames.map((folderName, index) => (
<SecretOverviewFolderRow
folderName={folderName}
isFolderPresentInEnv={isFolderPresentInEnv}
@ -391,7 +408,7 @@ export const SecretOverviewPage = () => {
onClick={handleFolderClick}
/>
))}
{userAvailableEnvs?.length > 0 ? (
{!isTableLoading && (userAvailableEnvs?.length > 0 ? (
filteredSecretNames.map((key, index) => (
<SecretOverviewTableRow
secretPath={secretPath}
@ -407,7 +424,7 @@ export const SecretOverviewPage = () => {
))
) : (
<PermissionDeniedBanner />
)}
))}
</TBody>
<TFoot>
<Tr className="sticky bottom-0 z-10 border-0 bg-mineshaft-800">

@ -1,9 +1,12 @@
import { useTranslation } from "react-i18next";
import Link from "next/link";
import {
faArrowsSpin,
faArrowUpRightFromSquare,
faExclamationTriangle,
faFolder,
faInfoCircle,
faPlus,
faRotate,
faTrash
} from "@fortawesome/free-solid-svg-icons";
@ -180,12 +183,21 @@ export const SecretRotationPage = withProjectPermission(
return (
<div className="container mx-auto bg-bunker-800 text-white w-full h-full max-w-7xl px-6">
<div className="my-6">
<h2 className="text-3xl font-semibold text-gray-200">Secret Rotation</h2>
<p className="text-bunker-300">Auto rotate secrets for better security</p>
<div className="py-6 flex justify-between items-center">
<div className="flex flex-col w-full">
<h2 className="text-3xl font-semibold text-gray-200">Secret Rotation</h2>
<p className="text-bunker-300">Stop manually rotating secrets and automate credential rotation.</p>
</div>
<div className="flex justify-center w-max">
<Link href="https://infisical.com/docs/documentation/platform/secret-rotation/overview">
<span className="rounded-md px-4 py-2 w-max text-mineshaft-200 hover:text-white bg-mineshaft-600 border border-mineshaft-500 hover:bg-primary/10 hover:border-primary/40 duration-200 cursor-pointer">
Documentation <FontAwesomeIcon icon={faArrowUpRightFromSquare} className="text-xs mb-[0.06rem] ml-1"/>
</span>
</Link>
</div>
</div>
<div className="mb-6">
<div className="text-xl font-semibold text-gray-200 mb-2">Rotated Secrets</div>
<div className="text-xl font-semibold text-gray-200 mb-2 mt-6">Rotated Secrets</div>
<div className="flex flex-col space-y-2">
<TableContainer>
<Table>
@ -322,7 +334,7 @@ export const SecretRotationPage = withProjectPermission(
</TableContainer>
</div>
</div>
<div className="text-xl font-semibold text-gray-200 mb-2">Infisical Rotation Providers</div>
<div className="text-xl font-semibold text-gray-200 mb-2 mt-12">Infisical Rotation Providers</div>
<div className="grid grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4">
{isRotationProviderLoading &&
Array.from({ length: 12 }).map((_, index) => (
@ -331,7 +343,7 @@ export const SecretRotationPage = withProjectPermission(
{!isRotationProviderLoading &&
secretRotationProviders?.providers.map((provider) => (
<div
className="group relative cursor-pointer h-32 flex flex-row items-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
className="group relative cursor-pointer h-32 flex flex-row items-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4 hover:border-primary/40 hover:bg-primary/10"
key={`infisical-rotation-provider-${provider.name}`}
tabIndex={0}
role="button"
@ -349,13 +361,21 @@ export const SecretRotationPage = withProjectPermission(
<div className="ml-4 max-w-xs text-xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
{provider.title}
</div>
<div className="group-hover:opacity-100 transition-all opacity-0 absolute top-1 right-1">
<div className="group-hover:opacity-100 transition-all opacity-0 absolute top-1 right-1.5">
<Tooltip content={provider.description} sideOffset={10}>
<FontAwesomeIcon icon={faInfoCircle} className="text-bunker-300" />
<FontAwesomeIcon icon={faInfoCircle} className="text-primary" />
</Tooltip>
</div>
</div>
))}
<Link href="https://github.com/Infisical/infisical/issues">
<div className="group relative cursor-pointer h-32 flex flex-row items-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4 hover:border-primary/40 hover:bg-primary/10">
<FontAwesomeIcon icon={faPlus} className="text-gray-300 text-3xl pl-3 pr-2" />
<div className="ml-4 max-w-xs text-xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
Request or create your own template
</div>
</div>
</Link>
</div>
<CreateRotationForm
isOpen={popUp.createRotation.isOpen}

@ -89,7 +89,7 @@ export const SignUpPage = () => {
console.log(err);
createNotification({
type: "error",
text: "Faield to create admin"
text: "Failed to create admin"
});
}
};
@ -108,7 +108,7 @@ export const SignUpPage = () => {
console.log(err);
createNotification({
type: "error",
text: "Faield to generate backup"
text: "Failed to generate backup"
});
}
};