Compare commits
40 Commits
non-zero-m
...
patch-5
Author | SHA1 | Date | |
---|---|---|---|
567309e848 | |||
f264340903 | |||
51b788cc5b | |||
8e0f424249 | |||
f3767d3963 | |||
51cbfdbc46 | |||
f5a580eb72 | |||
460ebf3296 | |||
7f7f11c970 | |||
f799e224a0 | |||
8a87277fe6 | |||
32805c726a | |||
6c4a6d31e4 | |||
e7b89b645f | |||
b60cf2eb07 | |||
cf5a79995f | |||
c51f09fd3a | |||
f9444c5205 | |||
7dd0943b2d | |||
31a9f032b3 | |||
9c55d1906d | |||
ff54a20ace | |||
8bf7eba07b | |||
bb75ea550a | |||
344f7276d2 | |||
c375662411 | |||
cc4ad1df4b | |||
c92c0f7288 | |||
fbe0cf006f | |||
d2f959558e | |||
e50c89e326 | |||
6cda14328b | |||
b551ee50e7 | |||
93aeacc6b6 | |||
f940f8b79d | |||
72ac2c04b8 | |||
bb3d591f21 | |||
763ce1b206 | |||
5f29562fad | |||
544d37bbc4 |
1279
backend/spec.json
@ -3,28 +3,28 @@ import { Types } from "mongoose";
|
||||
import jwt from "jsonwebtoken";
|
||||
import crypto from "crypto";
|
||||
import bcrypt from "bcrypt";
|
||||
import {
|
||||
IIdentity,
|
||||
IIdentityTrustedIp,
|
||||
IIdentityUniversalAuthClientSecret,
|
||||
Identity,
|
||||
IdentityAccessToken,
|
||||
IdentityAuthMethod,
|
||||
IdentityMembershipOrg,
|
||||
IdentityUniversalAuth,
|
||||
IdentityUniversalAuthClientSecret,
|
||||
import {
|
||||
IIdentity,
|
||||
IIdentityTrustedIp,
|
||||
IIdentityUniversalAuthClientSecret,
|
||||
Identity,
|
||||
IdentityAccessToken,
|
||||
IdentityAuthMethod,
|
||||
IdentityMembershipOrg,
|
||||
IdentityUniversalAuth,
|
||||
IdentityUniversalAuthClientSecret,
|
||||
} from "../../models";
|
||||
import { createToken } from "../../helpers/auth";
|
||||
import { AuthTokenType } from "../../variables";
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
ResourceNotFoundError,
|
||||
UnauthorizedRequestError
|
||||
import {
|
||||
BadRequestError,
|
||||
ForbiddenRequestError,
|
||||
ResourceNotFoundError,
|
||||
UnauthorizedRequestError
|
||||
} from "../../utils/errors";
|
||||
import {
|
||||
getAuthSecret,
|
||||
getSaltRounds
|
||||
getAuthSecret,
|
||||
getSaltRounds
|
||||
} from "../../config";
|
||||
import { ActorType, EventType, IRole } from "../../ee/models";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
@ -32,12 +32,12 @@ import * as reqValidator from "../../validation/auth";
|
||||
import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr } from "../../utils/ip";
|
||||
import { getUserAgentType } from "../../utils/posthog";
|
||||
import { EEAuditLogService, EELicenseService } from "../../ee/services";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions,
|
||||
getOrgRolePermissions,
|
||||
isAtLeastAsPrivilegedOrg
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
getAuthDataOrgPermissions,
|
||||
getOrgRolePermissions,
|
||||
isAtLeastAsPrivilegedOrg
|
||||
} from "../../ee/services/RoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
@ -53,13 +53,59 @@ const packageUniversalAuthClientSecretData = (identityUniversalAuthClientSecret:
|
||||
createdAt: identityUniversalAuthClientSecret.createdAt,
|
||||
updatedAt: identityUniversalAuthClientSecret.updatedAt
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Renews an access token by its TTL
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const renewAccessToken = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Renew access token'
|
||||
#swagger.description = 'Renew access token'
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accessToken": {
|
||||
"type": "string",
|
||||
"description": "Access token to renew",
|
||||
"example": "..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accessToken": {
|
||||
"type": "string",
|
||||
"description": "(Same) Access token after successful renewal"
|
||||
},
|
||||
"expiresIn": {
|
||||
"type": "number",
|
||||
"description": "TTL of access token in seconds"
|
||||
},
|
||||
"tokenType": {
|
||||
"type": "string",
|
||||
"description": "Type of access token (e.g. Bearer)"
|
||||
}
|
||||
},
|
||||
"description": "Access token and its details"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
body: {
|
||||
accessToken
|
||||
@ -86,9 +132,6 @@ export const renewAccessToken = async (req: Request, res: Response) => {
|
||||
createdAt: accessTokenCreatedAt
|
||||
} = identityAccessToken;
|
||||
|
||||
if (accessTokenTTL === accessTokenMaxTTL) throw UnauthorizedRequestError({
|
||||
message: "Failed to renew non-renewable access token"
|
||||
});
|
||||
|
||||
// ttl check
|
||||
if (accessTokenTTL > 0) {
|
||||
@ -141,6 +184,7 @@ export const renewAccessToken = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
accessToken,
|
||||
expiresIn: identityAccessToken.accessTokenTTL,
|
||||
accessTokenMaxTTL: identityAccessToken.accessTokenMaxTTL,
|
||||
tokenType: "Bearer"
|
||||
});
|
||||
}
|
||||
@ -152,6 +196,57 @@ export const renewAccessToken = async (req: Request, res: Response) => {
|
||||
* @param res
|
||||
*/
|
||||
export const loginIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Login with Universal Auth'
|
||||
#swagger.description = 'Login with Universal Auth'
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clientId": {
|
||||
"type": "string",
|
||||
"description": "Client ID for identity to login with Universal Auth",
|
||||
"example": "..."
|
||||
},
|
||||
"clientSecret": {
|
||||
"type": "string",
|
||||
"description": "Client Secret for identity to login with Universal Auth",
|
||||
"example": "..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accessToken": {
|
||||
"type": "string",
|
||||
"description": "Access token issued after successful login"
|
||||
},
|
||||
"expiresIn": {
|
||||
"type": "number",
|
||||
"description": "TTL of access token in seconds"
|
||||
},
|
||||
"tokenType": {
|
||||
"type": "string",
|
||||
"description": "Type of access token (e.g. Bearer)"
|
||||
}
|
||||
},
|
||||
"description": "Access token and its details"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
body: {
|
||||
clientId,
|
||||
@ -162,7 +257,7 @@ export const loginIdentityUniversalAuth = async (req: Request, res: Response) =>
|
||||
const identityUniversalAuth = await IdentityUniversalAuth.findOne({
|
||||
clientId
|
||||
}).populate<{ identity: IIdentity }>("identity");
|
||||
|
||||
|
||||
if (!identityUniversalAuth) throw UnauthorizedRequestError();
|
||||
|
||||
checkIPAgainstBlocklist({
|
||||
@ -237,16 +332,16 @@ export const loginIdentityUniversalAuth = async (req: Request, res: Response) =>
|
||||
|
||||
// increment usage count by 1
|
||||
await IdentityUniversalAuthClientSecret
|
||||
.findByIdAndUpdate(
|
||||
validatedClientSecretDatum._id,
|
||||
{
|
||||
clientSecretLastUsedAt: new Date(),
|
||||
$inc: { clientSecretNumUses: 1 }
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
.findByIdAndUpdate(
|
||||
validatedClientSecretDatum._id,
|
||||
{
|
||||
clientSecretLastUsedAt: new Date(),
|
||||
$inc: { clientSecretNumUses: 1 }
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
const identityAccessToken = await new IdentityAccessToken({
|
||||
identity: identityUniversalAuth.identity,
|
||||
@ -300,11 +395,110 @@ export const loginIdentityUniversalAuth = async (req: Request, res: Response) =>
|
||||
return res.status(200).send({
|
||||
accessToken,
|
||||
expiresIn: identityUniversalAuth.accessTokenTTL,
|
||||
tokenType: "Bearer"
|
||||
accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL,
|
||||
tokenType: "Bearer",
|
||||
});
|
||||
}
|
||||
|
||||
export const addIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
/**
|
||||
* Attach identity universal auth method onto identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const attachIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Attach Universal Auth configuration onto identity'
|
||||
#swagger.description = 'Attach Universal Auth configuration onto identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity to attach Universal Auth onto",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clientSecretTrustedIps": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
"properties": {
|
||||
"ipAddress": {
|
||||
type: "string",
|
||||
description: "IP address to trust",
|
||||
default: "0.0.0.0/0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.",
|
||||
"example": "...",
|
||||
"default": [{ ipAddress: "0.0.0.0/0" }]
|
||||
},
|
||||
"accessTokenTTL": {
|
||||
"type": "number",
|
||||
"description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.",
|
||||
"example": "...",
|
||||
"default": 100
|
||||
},
|
||||
"accessTokenMaxTTL": {
|
||||
"type": "number",
|
||||
"description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.",
|
||||
"example": "...",
|
||||
"default": 2592000
|
||||
},
|
||||
"accessTokenNumUsesLimit": {
|
||||
"type": "number",
|
||||
"description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.",
|
||||
"example": "...",
|
||||
"default": 0
|
||||
},
|
||||
"accessTokenTrustedIps": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
"properties": {
|
||||
"ipAddress": {
|
||||
type: "string",
|
||||
description: "IP address to trust",
|
||||
default: "0.0.0.0/0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.",
|
||||
"example": "...",
|
||||
"default": [{ ipAddress: "0.0.0.0/0" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityUniversalAuth": {
|
||||
$ref: '#/definitions/IdentityUniversalAuth'
|
||||
}
|
||||
},
|
||||
"description": "Details of attached Universal Auth"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId },
|
||||
body: {
|
||||
@ -328,7 +522,7 @@ export const addIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
if (!identityMembershipOrg) throw ResourceNotFoundError({
|
||||
message: `Failed to find identity with id ${identityId}`
|
||||
});
|
||||
|
||||
|
||||
if (identityMembershipOrg.identity?.authMethod) throw BadRequestError({
|
||||
message: "Failed to add universal auth to already-configured identity"
|
||||
});
|
||||
@ -377,7 +571,7 @@ export const addIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
|
||||
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||
});
|
||||
|
||||
|
||||
const identityUniversalAuth = await new IdentityUniversalAuth({
|
||||
identity: identityMembershipOrg.identity._id,
|
||||
clientId: crypto.randomUUID(),
|
||||
@ -387,7 +581,7 @@ export const addIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps: reformattedAccessTokenTrustedIps,
|
||||
}).save();
|
||||
|
||||
|
||||
await Identity.findByIdAndUpdate(
|
||||
identityMembershipOrg.identity._id,
|
||||
{
|
||||
@ -415,7 +609,98 @@ export const addIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update identity universal auth method on identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const updateIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update Universal Auth configuration on identity'
|
||||
#swagger.description = 'Update Universal Auth configuration on identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity to update Universal Auth on",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clientSecretTrustedIps": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
"properties": {
|
||||
"ipAddress": {
|
||||
type: "string",
|
||||
description: "IP address to trust"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.",
|
||||
"example": "...",
|
||||
},
|
||||
"accessTokenTTL": {
|
||||
"type": "number",
|
||||
"description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.",
|
||||
"example": "...",
|
||||
},
|
||||
"accessTokenMaxTTL": {
|
||||
"type": "number",
|
||||
"description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.",
|
||||
"example": "...",
|
||||
},
|
||||
"accessTokenNumUsesLimit": {
|
||||
"type": "number",
|
||||
"description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.",
|
||||
"example": "...",
|
||||
},
|
||||
"accessTokenTrustedIps": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
"properties": {
|
||||
"ipAddress": {
|
||||
type: "string",
|
||||
description: "IP address to trust"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.",
|
||||
"example": "...",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityUniversalAuth": {
|
||||
$ref: '#/definitions/IdentityUniversalAuth'
|
||||
}
|
||||
},
|
||||
"description": "Details of updated Universal Auth"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId },
|
||||
body: {
|
||||
@ -439,7 +724,7 @@ export const updateIdentityUniversalAuth = async (req: Request, res: Response) =
|
||||
if (!identityMembershipOrg) throw ResourceNotFoundError({
|
||||
message: `Failed to find identity with id ${identityId}`
|
||||
});
|
||||
|
||||
|
||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) throw BadRequestError({
|
||||
message: "Failed to add universal auth to already-configured identity"
|
||||
});
|
||||
@ -490,7 +775,7 @@ export const updateIdentityUniversalAuth = async (req: Request, res: Response) =
|
||||
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const identityUniversalAuth = await IdentityUniversalAuth.findOneAndUpdate(
|
||||
{
|
||||
identity: identityMembershipOrg.identity._id,
|
||||
@ -527,11 +812,47 @@ export const updateIdentityUniversalAuth = async (req: Request, res: Response) =
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return identity universal auth method on identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Retrieve Universal Auth configuration on identity'
|
||||
#swagger.description = 'Retrieve Universal Auth configuration on identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity to retrieve Universal Auth on",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityUniversalAuth": {
|
||||
$ref: '#/definitions/IdentityUniversalAuth'
|
||||
}
|
||||
},
|
||||
"description": "Details of retrieved Universal Auth"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId }
|
||||
} = await validateRequest(reqValidator.GetUniversalAuthForIdentityV1, req);
|
||||
|
||||
|
||||
const identityMembershipOrg = await IdentityMembershipOrg
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId)
|
||||
@ -558,7 +879,7 @@ export const getIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) throw BadRequestError({
|
||||
message: "The identity does not have universal auth configured"
|
||||
});
|
||||
|
||||
|
||||
const identityUniversalAuth = await IdentityUniversalAuth.findOne({
|
||||
identity: identityMembershipOrg.identity._id,
|
||||
});
|
||||
@ -578,7 +899,77 @@ export const getIdentityUniversalAuth = async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create client secret for identity universal auth method on identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const createUniversalAuthClientSecret = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Create Universal Auth Client Secret for identity'
|
||||
#swagger.description = 'Create Universal Auth Client Secret for identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity to create Universal Auth Client Secret for",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "A description for the Client Secret to create.",
|
||||
"example": "..."
|
||||
},
|
||||
"ttl": {
|
||||
"type": "number",
|
||||
"description": "The time-to-live for the Client Secret to create. By default, the TTL will be set to 0 which implies that the Client Secret will never expire; a value of 0 implies an infinite lifetime.",
|
||||
"example": "...",
|
||||
"default": 0
|
||||
},
|
||||
"numUsesLimit": {
|
||||
"type": "number",
|
||||
"description": "The maximum number of times that the Client Secret can be used together with the Client ID to get back an access token; a value of 0 implies infinite number of uses.",
|
||||
"example": "...",
|
||||
"default": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clientSecret": {
|
||||
"type": "string",
|
||||
"description": "The created Client Secret"
|
||||
},
|
||||
"clientSecretData": {
|
||||
$ref: '#/definitions/IdentityUniversalAuthClientSecretData'
|
||||
}
|
||||
},
|
||||
"description": "Details of the created Client Secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId },
|
||||
body: {
|
||||
@ -625,11 +1016,11 @@ export const createUniversalAuthClientSecret = async (req: Request, res: Respons
|
||||
|
||||
const clientSecret = crypto.randomBytes(32).toString("hex");
|
||||
const clientSecretHash = await bcrypt.hash(clientSecret, await getSaltRounds());
|
||||
|
||||
|
||||
const identityUniversalAuth = await IdentityUniversalAuth.findOne({
|
||||
identity: identityMembershipOrg.identity._id
|
||||
});
|
||||
|
||||
|
||||
if (!identityUniversalAuth) throw ResourceNotFoundError();
|
||||
|
||||
const identityUniversalAuthClientSecret = await new IdentityUniversalAuthClientSecret({
|
||||
@ -661,11 +1052,50 @@ export const createUniversalAuthClientSecret = async (req: Request, res: Respons
|
||||
});
|
||||
}
|
||||
|
||||
export const getUniversalAuthClientSecrets = async (req: Request, res: Response) => {
|
||||
/**
|
||||
* Return list of client secret details for identity universal auth method on identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getUniversalAuthClientSecretsDetails = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'List Universal Auth Client Secrets for identity'
|
||||
#swagger.description = 'List Universal Auth Client Secrets for identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity for which to get Client Secrets for",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clientSecretData": {
|
||||
type: "array",
|
||||
items: {
|
||||
$ref: '#/definitions/IdentityUniversalAuthClientSecretData'
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Details of the Client Secrets"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId }
|
||||
} = await validateRequest(reqValidator.GetUniversalAuthClientSecretsV1, req);
|
||||
|
||||
|
||||
const identityMembershipOrg = await IdentityMembershipOrg.findOne({
|
||||
identity: new Types.ObjectId(identityId)
|
||||
}).populate<{
|
||||
@ -721,11 +1151,54 @@ export const getUniversalAuthClientSecrets = async (req: Request, res: Response)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke client secret for identity universal auth method on identity with id [identityId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const revokeUniversalAuthClientSecret = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Revoke Universal Auth Client Secret for identity'
|
||||
#swagger.description = 'Revoke Universal Auth Client Secret for identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity under which Client Secret was issued for",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.parameters['clientSecretId'] = {
|
||||
"description": "ID of Client Secret to revoke",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clientSecretData": {
|
||||
$ref: '#/definitions/IdentityUniversalAuthClientSecretData'
|
||||
}
|
||||
},
|
||||
"description": "Details of the revoked Client Secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId, clientSecretId }
|
||||
} = await validateRequest(reqValidator.RevokeUniversalAuthClientSecretV1, req);
|
||||
|
||||
|
||||
const identityMembershipOrg = await IdentityMembershipOrg
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId)
|
||||
@ -773,7 +1246,7 @@ export const revokeUniversalAuthClientSecret = async (req: Request, res: Respons
|
||||
);
|
||||
|
||||
if (!clientSecretData) throw ResourceNotFoundError();
|
||||
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
|
@ -33,11 +33,12 @@ import { ForbiddenError } from "@casl/ability";
|
||||
*/
|
||||
export const getOrganizationMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return organization memberships'
|
||||
#swagger.description = 'Return organization memberships'
|
||||
#swagger.summary = 'Return organization user memberships'
|
||||
#swagger.description = 'Return organization user memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
@ -94,11 +95,12 @@ export const getOrganizationMemberships = async (req: Request, res: Response) =>
|
||||
*/
|
||||
export const updateOrganizationMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update organization membership'
|
||||
#swagger.description = 'Update organization membership'
|
||||
#swagger.summary = 'Update organization user membership'
|
||||
#swagger.description = 'Update organization user membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
@ -214,11 +216,12 @@ export const updateOrganizationMembership = async (req: Request, res: Response)
|
||||
*/
|
||||
export const deleteOrganizationMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete organization membership'
|
||||
#swagger.description = 'Delete organization membership'
|
||||
#swagger.summary = 'Delete organization user membership'
|
||||
#swagger.description = 'Delete organization user membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
@ -425,6 +428,40 @@ export const deleteOrganizationById = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const getOrganizationIdentityMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return organization identity memberships'
|
||||
#swagger.description = 'Return organization identity memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['organizationId'] = {
|
||||
"description": "ID of organization",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMemberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/IdentityMembershipOrg"
|
||||
},
|
||||
"description": "Identity memberships of organization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { organizationId }
|
||||
} = await validateRequest(reqValidator.GetOrgIdentityMembershipsV2, req);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
import {
|
||||
IIdentity,
|
||||
IdentityMembership,
|
||||
IdentityMembershipOrg,
|
||||
Key,
|
||||
Key,
|
||||
Membership,
|
||||
ServiceTokenData,
|
||||
Workspace
|
||||
@ -182,11 +182,11 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
@ -211,7 +211,7 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
||||
receiver: req.user._id
|
||||
}).populate("sender", "+publicKey");
|
||||
|
||||
if (!key) throw new Error("Failed to find workspace key");
|
||||
if (!key) throw new Error(`getWorkspaceKey: Failed to find workspace key [workspaceId=${workspaceId}] [receiver=${req.user._id}]`);
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
@ -249,33 +249,34 @@ export const getWorkspaceServiceTokenData = async (req: Request, res: Response)
|
||||
*/
|
||||
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return project memberships'
|
||||
#swagger.description = 'Return project memberships'
|
||||
#swagger.summary = 'Return project user memberships'
|
||||
#swagger.description = 'Return project user memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"memberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/Membership"
|
||||
},
|
||||
"description": "Memberships of project"
|
||||
}
|
||||
}
|
||||
"properties": {
|
||||
"memberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/Membership"
|
||||
},
|
||||
"description": "Memberships of project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,26 +313,27 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
||||
*/
|
||||
export const updateWorkspaceMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update project membership'
|
||||
#swagger.description = 'Update project membership'
|
||||
#swagger.summary = 'Update project user membership'
|
||||
#swagger.description = 'Update project user membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to update",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to update",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
#swagger.requestBody = {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
@ -340,7 +342,7 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role of membership - either admin or member",
|
||||
"description": "Role to update to for project membership",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -352,13 +354,13 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Updated membership"
|
||||
}
|
||||
}
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Updated membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -402,36 +404,37 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
*/
|
||||
export const deleteWorkspaceMembership = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete project membership'
|
||||
#swagger.description = 'Delete project membership'
|
||||
#swagger.summary = 'Delete project user membership'
|
||||
#swagger.description = 'Delete project user membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to delete",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
#swagger.parameters['membershipId'] = {
|
||||
"description": "ID of project membership to delete",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Deleted membership"
|
||||
}
|
||||
}
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"membership": {
|
||||
$ref: "#/components/schemas/Membership",
|
||||
"description": "Deleted membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -511,14 +514,14 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const addIdentityToWorkspace = async (req: Request, res: Response) => {
|
||||
export const addIdentityToWorkspace = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { workspaceId, identityId },
|
||||
body: {
|
||||
role
|
||||
}
|
||||
} = await validateRequest(reqValidator.AddIdentityToWorkspaceV2, req);
|
||||
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
@ -538,7 +541,7 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
message: `Identity with id ${identityId} already exists in project with id ${workspaceId}`
|
||||
});
|
||||
|
||||
|
||||
|
||||
const workspace = await Workspace.findById(workspaceId);
|
||||
if (!workspace) throw ResourceNotFoundError();
|
||||
|
||||
@ -550,16 +553,16 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
if (!identityMembershipOrg) throw ResourceNotFoundError({
|
||||
message: `Failed to find identity with id ${identityId}`
|
||||
});
|
||||
|
||||
|
||||
if (!identityMembershipOrg.organization.equals(workspace.organization)) throw BadRequestError({
|
||||
message: "Failed to add identity to project in another organization"
|
||||
});
|
||||
|
||||
const rolePermission = await getWorkspaceRolePermissions(role, workspaceId);
|
||||
const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
|
||||
|
||||
|
||||
if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({
|
||||
message: "Failed to add identity to project with more privileged role"
|
||||
message: "Failed to add identity to project with more privileged role"
|
||||
});
|
||||
|
||||
let customRole;
|
||||
@ -571,18 +574,18 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
isOrgRole: false,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
identityMembership = await new IdentityMembership({
|
||||
identity: identityMembershipOrg.identity,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
role: customRole ? CUSTOM : role,
|
||||
customRole
|
||||
}).save();
|
||||
|
||||
|
||||
return res.status(200).send({
|
||||
identityMembership
|
||||
});
|
||||
@ -595,13 +598,66 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
* @param res
|
||||
*/
|
||||
export const updateIdentityWorkspaceRole = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update project identity membership'
|
||||
#swagger.description = 'Update project identity membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity whose membership to update in project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role to update to for identity project membership",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMembership": {
|
||||
$ref: "#/components/schemas/IdentityMembership",
|
||||
"description": "Updated identity membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { workspaceId, identityId },
|
||||
body: {
|
||||
role
|
||||
}
|
||||
} = await validateRequest(reqValidator.UpdateIdentityWorkspaceRoleV2, req);
|
||||
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
@ -611,7 +667,7 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
|
||||
let identityMembership = await IdentityMembership
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId),
|
||||
@ -625,21 +681,21 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
if (!identityMembership) throw BadRequestError({
|
||||
message: `Identity with id ${identityId} does not exist in project with id ${workspaceId}`
|
||||
});
|
||||
|
||||
|
||||
const identityRolePermission = await getWorkspaceRolePermissions(
|
||||
identityMembership?.customRole?.slug ?? identityMembership.role,
|
||||
identityMembership?.customRole?.slug ?? identityMembership.role,
|
||||
identityMembership.workspace.toString()
|
||||
);
|
||||
const isAsPrivilegedAsIdentity = isAtLeastAsPrivilegedWorkspace(permission, identityRolePermission);
|
||||
if (!isAsPrivilegedAsIdentity) throw ForbiddenRequestError({
|
||||
message: "Failed to update role of more privileged identity"
|
||||
message: "Failed to update role of more privileged identity"
|
||||
});
|
||||
|
||||
const rolePermission = await getWorkspaceRolePermissions(role, workspaceId);
|
||||
const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
|
||||
|
||||
|
||||
if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({
|
||||
message: "Failed to update identity to a more privileged role"
|
||||
message: "Failed to update identity to a more privileged role"
|
||||
});
|
||||
|
||||
let customRole;
|
||||
@ -651,11 +707,11 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
isOrgRole: false,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
|
||||
if (!customRole) throw BadRequestError({ message: "Role not found" });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
identityMembership = await IdentityMembership.findOneAndUpdate(
|
||||
{
|
||||
identity: identityMembership.identity._id,
|
||||
@ -676,16 +732,52 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete identity with id [identityId] to workspace
|
||||
* Delete identity with id [identityId] from workspace
|
||||
* with id [workspaceId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const deleteIdentityFromWorkspace = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete project identity membership'
|
||||
#swagger.description = 'Delete project identity membership'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity whose membership to delete in project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMembership": {
|
||||
$ref: "#/components/schemas/IdentityMembership",
|
||||
"description": "Deleted identity membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { workspaceId, identityId }
|
||||
} = await validateRequest(reqValidator.DeleteIdentityFromWorkspaceV2, req);
|
||||
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
@ -695,7 +787,7 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
|
||||
const identityMembership = await IdentityMembership
|
||||
.findOne({
|
||||
identity: new Types.ObjectId(identityId),
|
||||
@ -705,20 +797,20 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
identity: IIdentity,
|
||||
customRole: IRole
|
||||
}>("identity customRole");
|
||||
|
||||
|
||||
if (!identityMembership) throw ResourceNotFoundError({
|
||||
message: `Identity with id ${identityId} does not exist in project with id ${workspaceId}`
|
||||
});
|
||||
|
||||
|
||||
const identityRolePermission = await getWorkspaceRolePermissions(
|
||||
identityMembership?.customRole?.slug ?? identityMembership.role,
|
||||
identityMembership?.customRole?.slug ?? identityMembership.role,
|
||||
identityMembership.workspace.toString()
|
||||
);
|
||||
const isAsPrivilegedAsIdentity = isAtLeastAsPrivilegedWorkspace(permission, identityRolePermission);
|
||||
if (!isAsPrivilegedAsIdentity) throw ForbiddenRequestError({
|
||||
message: "Failed to remove more privileged identity from project"
|
||||
message: "Failed to remove more privileged identity from project"
|
||||
});
|
||||
|
||||
|
||||
await IdentityMembership.findByIdAndDelete(identityMembership._id);
|
||||
|
||||
return res.status(200).send({
|
||||
@ -733,10 +825,44 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const getWorkspaceIdentityMemberships = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return project identity memberships'
|
||||
#swagger.description = 'Return project identity memberships'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identityMemberships": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/IdentityMembership"
|
||||
},
|
||||
"description": "Identity memberships of project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { workspaceId }
|
||||
} = await validateRequest(reqValidator.GetWorkspaceIdentityMembersV2, req);
|
||||
|
||||
|
||||
const { permission } = await getAuthDataProjectPermissions({
|
||||
authData: req.authData,
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
|
@ -42,6 +42,58 @@ import { ForbiddenError } from "@casl/ability";
|
||||
* @returns
|
||||
*/
|
||||
export const createIdentity = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Create identity'
|
||||
#swagger.description = 'Create identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of entity to create",
|
||||
"example": "development"
|
||||
},
|
||||
"organizationId": {
|
||||
"type": "string",
|
||||
"description": "ID of organization where to create identity",
|
||||
"example": "dev-environment"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role to assume for organization membership",
|
||||
"example": "no-access"
|
||||
}
|
||||
},
|
||||
"required": ["name", "organizationId", "role"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identity": {
|
||||
$ref: '#/definitions/Identity'
|
||||
}
|
||||
},
|
||||
"description": "Details of the created identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
body: {
|
||||
name,
|
||||
@ -120,6 +172,59 @@ export const createIdentity = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const updateIdentity = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Update identity'
|
||||
#swagger.description = 'Update identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity to update",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.requestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of entity to update to",
|
||||
"example": "development"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "Role to update to for organization membership",
|
||||
"example": "no-access"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identity": {
|
||||
$ref: '#/definitions/Identity'
|
||||
}
|
||||
},
|
||||
"description": "Details of the updated identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId },
|
||||
body: {
|
||||
@ -242,6 +347,37 @@ export const createIdentity = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const deleteIdentity = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Delete identity'
|
||||
#swagger.description = 'Delete identity'
|
||||
|
||||
#swagger.security = [{
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['identityId'] = {
|
||||
"description": "ID of identity",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "path"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"identity": {
|
||||
$ref: '#/definitions/Identity'
|
||||
}
|
||||
},
|
||||
"description": "Details of the deleted identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
params: { identityId }
|
||||
} = await validateRequest(reqValidator.DeleteIdentityV1, req);
|
||||
|
@ -62,15 +62,30 @@ export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) =
|
||||
#swagger.description = 'Return project secret snapshots ids'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"description": "ID of project where to get secret snapshots for",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['environment'] = {
|
||||
"description": "Slug of environment where to get secret snapshots for",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"in": "query"
|
||||
}
|
||||
|
||||
#swagger.parameters['directory'] = {
|
||||
"description": "Path where to get secret snapshots for like / or /foo/bar. Default is /",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"in": "query"
|
||||
}
|
||||
|
||||
#swagger.parameters['offset'] = {
|
||||
"description": "Number of secret snapshots to skip",
|
||||
"required": false,
|
||||
@ -195,11 +210,12 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
|
||||
#swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
"apiKeyAuth": [],
|
||||
"bearerAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"description": "ID of project where to roll back",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
@ -211,6 +227,14 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"environment": {
|
||||
"type": "string",
|
||||
"description": "Slug of environment where to roll back"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"description": "Path where to roll back for like / or /foo/bar. Default is /"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"description": "Version of secret snapshot to roll back to",
|
||||
|
@ -7,7 +7,7 @@ import { workspaceController } from "../../controllers/v1";
|
||||
router.get(
|
||||
"/:workspaceId/secret-snapshots",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.getWorkspaceSecretSnapshots
|
||||
);
|
||||
@ -23,7 +23,7 @@ router.get(
|
||||
router.post(
|
||||
"/:workspaceId/secret-snapshots/rollback",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.rollbackWorkspaceSecretSnapshot
|
||||
);
|
||||
@ -31,7 +31,7 @@ router.post(
|
||||
router.get(
|
||||
"/:workspaceId/audit-logs",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.getWorkspaceAuditLogs
|
||||
);
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
SECRET_SHARED
|
||||
} from "../variables";
|
||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||
import { InternalServerError } from "../utils/errors";
|
||||
import { BotNotFoundError, InternalServerError } from "../utils/errors";
|
||||
import { Folder } from "../models";
|
||||
import { getFolderByPath } from "../services/FolderService";
|
||||
import { getAllImportedSecrets } from "../services/SecretImportService";
|
||||
@ -223,7 +223,7 @@ export const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) =
|
||||
workspace: workspaceId
|
||||
}).populate<{ sender: IUser }>("sender", "publicKey");
|
||||
|
||||
if (!botKey) throw new Error("Failed to find bot key");
|
||||
if (!botKey) throw BotNotFoundError({ message: `getKey: Failed to find bot key for [workspaceId=${workspaceId}]` })
|
||||
|
||||
const bot = await Bot.findOne({
|
||||
workspace: workspaceId
|
||||
|
@ -7,7 +7,7 @@ import { AuthMode } from "../../variables";
|
||||
router.post(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
secretImpsController.createSecretImp
|
||||
);
|
||||
@ -15,7 +15,7 @@ router.post(
|
||||
router.put(
|
||||
"/:id",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
secretImpsController.updateSecretImport
|
||||
);
|
||||
@ -23,7 +23,7 @@ router.put(
|
||||
router.delete(
|
||||
"/:id",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
secretImpsController.deleteSecretImport
|
||||
);
|
||||
@ -31,7 +31,7 @@ router.delete(
|
||||
router.get(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
secretImpsController.getSecretImports
|
||||
);
|
||||
|
@ -12,7 +12,7 @@ import { AuthMode } from "../../variables";
|
||||
router.post(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
createFolder
|
||||
);
|
||||
@ -20,7 +20,7 @@ router.post(
|
||||
router.patch(
|
||||
"/:folderName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
updateFolderById
|
||||
);
|
||||
@ -28,7 +28,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:folderName",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
deleteFolder
|
||||
);
|
||||
@ -36,7 +36,7 @@ router.delete(
|
||||
router.get(
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
getFolders
|
||||
);
|
||||
|
@ -18,15 +18,15 @@ router.post(
|
||||
router.post(
|
||||
"/universal-auth/identities/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.addIdentityUniversalAuth
|
||||
universalAuthController.attachIdentityUniversalAuth
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/universal-auth/identities/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.updateIdentityUniversalAuth
|
||||
);
|
||||
@ -34,7 +34,7 @@ router.patch(
|
||||
router.get(
|
||||
"/universal-auth/identities/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.getIdentityUniversalAuth
|
||||
);
|
||||
@ -42,7 +42,7 @@ router.get(
|
||||
router.post(
|
||||
"/universal-auth/identities/:identityId/client-secrets",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.createUniversalAuthClientSecret
|
||||
);
|
||||
@ -50,15 +50,15 @@ router.post(
|
||||
router.get(
|
||||
"/universal-auth/identities/:identityId/client-secrets",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.getUniversalAuthClientSecrets
|
||||
universalAuthController.getUniversalAuthClientSecretsDetails
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
universalAuthController.revokeUniversalAuthClientSecret
|
||||
);
|
||||
|
@ -7,7 +7,7 @@ import { AuthMode } from "../../variables";
|
||||
router.post(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
environmentController.createWorkspaceEnvironment
|
||||
);
|
||||
@ -15,7 +15,7 @@ router.post(
|
||||
router.put(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
environmentController.renameWorkspaceEnvironment
|
||||
);
|
||||
@ -23,7 +23,7 @@ router.put(
|
||||
router.patch(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
environmentController.reorderWorkspaceEnvironments
|
||||
);
|
||||
@ -31,7 +31,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
environmentController.deleteWorkspaceEnvironment
|
||||
);
|
||||
|
@ -9,7 +9,7 @@ import { organizationsController } from "../../controllers/v2";
|
||||
router.get(
|
||||
"/:organizationId/memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
organizationsController.getOrganizationMemberships
|
||||
);
|
||||
@ -17,7 +17,7 @@ router.get(
|
||||
router.patch(
|
||||
"/:organizationId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
organizationsController.updateOrganizationMembership
|
||||
);
|
||||
@ -25,7 +25,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:organizationId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
organizationsController.deleteOrganizationMembership
|
||||
);
|
||||
@ -33,7 +33,7 @@ router.delete(
|
||||
router.get(
|
||||
"/:organizationId/workspaces",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
organizationsController.getOrganizationWorkspaces
|
||||
);
|
||||
|
@ -62,7 +62,7 @@ router.get(
|
||||
// new - TODO: rewire dashboard to this route
|
||||
"/:workspaceId/memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.getWorkspaceMemberships
|
||||
);
|
||||
@ -71,7 +71,7 @@ router.patch(
|
||||
// TODO - rewire dashboard to this route
|
||||
"/:workspaceId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.updateWorkspaceMembership
|
||||
);
|
||||
@ -80,7 +80,7 @@ router.delete(
|
||||
// TODO - rewire dashboard to this route
|
||||
"/:workspaceId/memberships/:membershipId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.deleteWorkspaceMembership
|
||||
);
|
||||
@ -96,7 +96,7 @@ router.patch(
|
||||
router.post(
|
||||
"/:workspaceId/identity-memberships/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.addIdentityToWorkspace
|
||||
);
|
||||
@ -104,7 +104,7 @@ router.post(
|
||||
router.patch(
|
||||
"/:workspaceId/identity-memberships/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.updateIdentityWorkspaceRole
|
||||
);
|
||||
@ -112,7 +112,7 @@ router.patch(
|
||||
router.delete(
|
||||
"/:workspaceId/identity-memberships/:identityId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.deleteIdentityFromWorkspace
|
||||
);
|
||||
@ -120,7 +120,7 @@ router.delete(
|
||||
router.get(
|
||||
"/:workspaceId/identity-memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]
|
||||
}),
|
||||
workspaceController.getWorkspaceIdentityMemberships
|
||||
);
|
||||
|
@ -8,12 +8,12 @@ import {
|
||||
getTelemetryEnabled,
|
||||
} from "../config";
|
||||
import {
|
||||
Identity,
|
||||
ServiceTokenData,
|
||||
User,
|
||||
User
|
||||
} from "../models";
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
BadRequestError,
|
||||
} from "../utils/errors";
|
||||
|
||||
class Telemetry {
|
||||
@ -22,7 +22,7 @@ class Telemetry {
|
||||
*/
|
||||
static logTelemetryMessage = async () => {
|
||||
|
||||
if(!(await getTelemetryEnabled())){
|
||||
if (!(await getTelemetryEnabled())) {
|
||||
[
|
||||
"To improve, Infisical collects telemetry data about general usage.",
|
||||
"This helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth as we support Infisical as open-source software.",
|
||||
@ -42,8 +42,8 @@ class Telemetry {
|
||||
postHogClient = new PostHog(await getPostHogProjectApiKey(), {
|
||||
host: await getPostHogHost(),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return postHogClient;
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ class Telemetry {
|
||||
}: {
|
||||
authData: AuthData;
|
||||
}) => {
|
||||
|
||||
let distinctId = "";
|
||||
if (authData.authPayload instanceof User) {
|
||||
distinctId = authData.authPayload.email;
|
||||
@ -59,14 +60,14 @@ class Telemetry {
|
||||
if (authData.authPayload.user) {
|
||||
const user = await User.findById(authData.authPayload.user, "email");
|
||||
if (!user) throw AccountNotFoundError();
|
||||
distinctId = user.email;
|
||||
distinctId = user.email;
|
||||
}
|
||||
} else if (authData.authPayload instanceof Identity) {
|
||||
distinctId = `identity-${authData.authPayload._id.toString()}`
|
||||
} else {
|
||||
distinctId = "unknown-auth-data"
|
||||
}
|
||||
|
||||
if (distinctId === "") throw BadRequestError({
|
||||
message: "Failed to obtain distinct id for logging telemetry",
|
||||
});
|
||||
|
||||
|
||||
return distinctId;
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ export const initializeSamlStrategy = async () => {
|
||||
const organization = await Organization.findById(req.ssoConfig.organization);
|
||||
|
||||
if (!organization) return done(OrganizationNotFoundError());
|
||||
|
||||
|
||||
const email = profile.email;
|
||||
const firstName = profile.firstName;
|
||||
const lastName = profile.lastName;
|
||||
@ -154,6 +154,7 @@ export const initializeSamlStrategy = async () => {
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: organization?.name,
|
||||
organizationId: organization?._id,
|
||||
authMethod: req.ssoConfig.authProvider,
|
||||
isUserCompleted,
|
||||
...(req.body.RelayState ? {
|
||||
|
@ -116,7 +116,9 @@ export const AddUniversalAuthToIdentityV1 = z.object({
|
||||
.array()
|
||||
.min(1)
|
||||
.default([{ ipAddress: "0.0.0.0/0" }]),
|
||||
accessTokenTTL: z.number().int().min(0).default(7200),
|
||||
accessTokenTTL: z.number().int().min(1).refine(value => value !== 0, {
|
||||
message: "accessTokenTTL must have a non zero number",
|
||||
}).default(2592000),
|
||||
accessTokenMaxTTL: z.number().int().refine(value => value !== 0, {
|
||||
message: "accessTokenMaxTTL must have a non zero number",
|
||||
}).default(2592000), // 30 days
|
||||
@ -147,7 +149,7 @@ export const UpdateUniversalAuthToIdentityV1 = z.object({
|
||||
accessTokenNumUsesLimit: z.number().int().min(0).optional(),
|
||||
accessTokenMaxTTL: z.number().int().refine(value => value !== 0, {
|
||||
message: "accessTokenMaxTTL must have a non zero number",
|
||||
}).default(2592000),
|
||||
}).optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -30,7 +30,7 @@ const generateOpenAPISpec = async () => {
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
bearerFormat: "JWT",
|
||||
description: "A service token in Infisical"
|
||||
description: "An access token in Infisical"
|
||||
},
|
||||
apiKeyAuth: {
|
||||
type: "apiKey",
|
||||
@ -52,6 +52,41 @@ const generateOpenAPISpec = async () => {
|
||||
updatedAt: "2023-01-13T14:16:12.210Z",
|
||||
createdAt: "2023-01-13T14:16:12.210Z"
|
||||
},
|
||||
Identity: {
|
||||
_id: "",
|
||||
name: "Machine 1",
|
||||
authMethod: "universal-auth"
|
||||
},
|
||||
IdentityUniversalAuth: {
|
||||
_id: "",
|
||||
identity: "",
|
||||
clientId: "...",
|
||||
clientSecretTrustedIps: [{
|
||||
ipAddress: "0.0.0.0",
|
||||
type: "ipv4",
|
||||
prefix: "0"
|
||||
}],
|
||||
accessTokenTTL: 7200,
|
||||
accessTokenMaxTTL: 2592000,
|
||||
accessTokenNumUsesLimit: 0,
|
||||
accessTokenTrustedIps: [{
|
||||
ipAddress: "0.0.0.0",
|
||||
type: "ipv4",
|
||||
prefix: "0"
|
||||
}]
|
||||
},
|
||||
IdentityUniversalAuthClientSecretData: {
|
||||
_id: "",
|
||||
identityUniversalAuth: "",
|
||||
isClientSecretRevoked: false,
|
||||
description: "",
|
||||
clientSecretPrefix: "abc",
|
||||
clientSecretNumUses: 0,
|
||||
clientSecretNumUsesLimit: 0,
|
||||
clientSecretTTL: 0,
|
||||
createdAt: "2023-01-13T14:16:12.210Z",
|
||||
updatedAt: "2023-01-13T14:16:12.210Z"
|
||||
},
|
||||
Membership: {
|
||||
user: {
|
||||
_id: "",
|
||||
@ -79,6 +114,25 @@ const generateOpenAPISpec = async () => {
|
||||
role: "owner",
|
||||
status: "accepted"
|
||||
},
|
||||
IdentityMembership: {
|
||||
identity: {
|
||||
_id: "",
|
||||
name: "Machine 1",
|
||||
authMethod: "universal-auth"
|
||||
},
|
||||
workspace: "",
|
||||
role: "member"
|
||||
},
|
||||
IdentityMembershipOrg: {
|
||||
identity: {
|
||||
_id: "",
|
||||
name: "Machine 1",
|
||||
authMethod: "universal-auth"
|
||||
},
|
||||
organization: "",
|
||||
role: "member",
|
||||
status: "accepted"
|
||||
},
|
||||
Organization: {
|
||||
_id: "",
|
||||
name: "Acme Corp.",
|
||||
|
15
cli/agent-config.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
infisical:
|
||||
address: "http://localhost:8080"
|
||||
auth:
|
||||
type: "universal-auth"
|
||||
config:
|
||||
client-id: "./client-id"
|
||||
client-secret: "./client-secret"
|
||||
remove_client_secret_on_read: false
|
||||
sinks:
|
||||
- type: "file"
|
||||
config:
|
||||
path: "access-token"
|
||||
templates:
|
||||
- source-path: my-dot-ev-secret-template
|
||||
destination-path: my-dot-env.env
|
@ -1,17 +0,0 @@
|
||||
infisical:
|
||||
address: "http://localhost:8080"
|
||||
auth:
|
||||
type: "token"
|
||||
config:
|
||||
token-path: "./role-id"
|
||||
sinks:
|
||||
- type: "file"
|
||||
config:
|
||||
path: "/Users/maidulislam/Desktop/test/infisical-token"
|
||||
- type: "file"
|
||||
config:
|
||||
path: "access-token"
|
||||
- type: "file"
|
||||
config:
|
||||
path: "maiduls-access-token"
|
||||
templates:
|
@ -425,24 +425,44 @@ func CallCreateServiceToken(httpClient *resty.Client, request CreateServiceToken
|
||||
return createServiceTokenResponse, nil
|
||||
}
|
||||
|
||||
func CallServiceTokenV3Refresh(httpClient *resty.Client, request ServiceTokenV3RefreshTokenRequest) (ServiceTokenV3RefreshTokenResponse, error) {
|
||||
var serviceTokenV3RefreshTokenResponse ServiceTokenV3RefreshTokenResponse
|
||||
func CallUniversalAuthLogin(httpClient *resty.Client, request UniversalAuthLoginRequest) (UniversalAuthLoginResponse, error) {
|
||||
var universalAuthLoginResponse UniversalAuthLoginResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&serviceTokenV3RefreshTokenResponse).
|
||||
SetResult(&universalAuthLoginResponse).
|
||||
SetHeader("User-Agent", USER_AGENT).
|
||||
SetBody(request).
|
||||
Post(fmt.Sprintf("%v/v3/service-token/me/token", config.INFISICAL_URL))
|
||||
Post(fmt.Sprintf("%v/v1/auth/universal-auth/login/", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return ServiceTokenV3RefreshTokenResponse{}, fmt.Errorf("CallServiceTokenV3Refresh: Unable to complete api request [err=%s]", err)
|
||||
return UniversalAuthLoginResponse{}, fmt.Errorf("CallUniversalAuthLogin: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return ServiceTokenV3RefreshTokenResponse{}, fmt.Errorf("CallServiceTokenV3Refresh: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
||||
return UniversalAuthLoginResponse{}, fmt.Errorf("CallUniversalAuthLogin: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
||||
}
|
||||
|
||||
return serviceTokenV3RefreshTokenResponse, nil
|
||||
return universalAuthLoginResponse, nil
|
||||
}
|
||||
|
||||
func CallUniversalAuthRefreshAccessToken(httpClient *resty.Client, request UniversalAuthRefreshRequest) (UniversalAuthRefreshResponse, error) {
|
||||
var universalAuthRefreshResponse UniversalAuthRefreshResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&universalAuthRefreshResponse).
|
||||
SetHeader("User-Agent", USER_AGENT).
|
||||
SetBody(request).
|
||||
Post(fmt.Sprintf("%v/v1/auth/token/renew", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return UniversalAuthRefreshResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return UniversalAuthRefreshResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
||||
}
|
||||
|
||||
return universalAuthRefreshResponse, nil
|
||||
}
|
||||
|
||||
func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Request) (GetRawSecretsV3Response, error) {
|
||||
@ -461,12 +481,12 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques
|
||||
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unable to complete api request [err=%w]", err)
|
||||
}
|
||||
|
||||
if response.IsError() && strings.Contains(response.String(), "Failed to find bot key") {
|
||||
if response.IsError() && strings.Contains(response.String(), "bot_not_found_error") {
|
||||
return GetRawSecretsV3Response{}, fmt.Errorf("project with id %s is a legacy project type, please navigate to project settings and disable end to end encryption then try again", request.WorkspaceId)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v]", response.Request.Method, response.Request.URL, response.StatusCode())
|
||||
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
||||
}
|
||||
|
||||
return getRawSecretsV3Response, nil
|
||||
|
@ -463,14 +463,27 @@ type CreateServiceTokenResponse struct {
|
||||
ServiceTokenData ServiceTokenData `json:"serviceTokenData"`
|
||||
}
|
||||
|
||||
type ServiceTokenV3RefreshTokenRequest struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
type UniversalAuthLoginRequest struct {
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
ClientId string `json:"clientId"`
|
||||
}
|
||||
type ServiceTokenV3RefreshTokenResponse struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
|
||||
type UniversalAuthLoginResponse struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessTokenTTL int `json:"expiresIn"`
|
||||
TokenType string `json:"tokenType"`
|
||||
AccessTokenMaxTTL int `json:"accessTokenMaxTTL"`
|
||||
}
|
||||
|
||||
type UniversalAuthRefreshRequest struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
}
|
||||
|
||||
type UniversalAuthRefreshResponse struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessTokenTTL int `json:"expiresIn"`
|
||||
TokenType string `json:"tokenType"`
|
||||
AccessTokenMaxTTL int `json:"accessTokenMaxTTL"`
|
||||
}
|
||||
|
||||
type GetRawSecretsV3Request struct {
|
||||
|
@ -5,12 +5,13 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"text/template"
|
||||
"time"
|
||||
@ -44,8 +45,10 @@ type AuthConfig struct {
|
||||
Config interface{} `yaml:"config"`
|
||||
}
|
||||
|
||||
type TokenAuthConfig struct {
|
||||
TokenPath string `yaml:"token-path"`
|
||||
type UniversalAuth struct {
|
||||
ClientIDPath string `yaml:"client-id"`
|
||||
ClientSecretPath string `yaml:"client-secret"`
|
||||
RemoveClientSecretOnRead bool `yaml:"remove_client_secret_on_read"`
|
||||
}
|
||||
|
||||
type OAuthConfig struct {
|
||||
@ -149,11 +152,12 @@ func ParseAgentConfig(filePath string) (*Config, error) {
|
||||
}
|
||||
|
||||
switch rawConfig.Auth.Type {
|
||||
case "token":
|
||||
var tokenConfig TokenAuthConfig
|
||||
case "universal-auth":
|
||||
var tokenConfig UniversalAuth
|
||||
if err := yaml.Unmarshal(configBytes, &tokenConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Auth.Config = tokenConfig
|
||||
case "oauth": // aws, gcp, k8s service account, etc
|
||||
var oauthConfig OAuthConfig
|
||||
@ -186,7 +190,9 @@ func ProcessTemplate(templatePath string, data interface{}, accessToken string)
|
||||
"secret": secretFunction,
|
||||
}
|
||||
|
||||
tmpl, err := template.New(templatePath).Funcs(funcs).ParseFiles(templatePath)
|
||||
templateName := path.Base(templatePath)
|
||||
|
||||
tmpl, err := template.New(templateName).Funcs(funcs).ParseFiles(templatePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -199,59 +205,231 @@ func ProcessTemplate(templatePath string, data interface{}, accessToken string)
|
||||
return &buf, nil
|
||||
}
|
||||
|
||||
func refreshTokenAndProcessTemplate(refreshToken string, config *Config, errChan chan error) {
|
||||
for {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetRetryCount(10000).
|
||||
SetRetryMaxWaitTime(20 * time.Second).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
type TokenManager struct {
|
||||
accessToken string
|
||||
accessTokenTTL time.Duration
|
||||
accessTokenMaxTTL time.Duration
|
||||
accessTokenFetchedTime time.Time
|
||||
accessTokenRefreshedTime time.Time
|
||||
mutex sync.Mutex
|
||||
filePaths []Sink // Store file paths if needed
|
||||
templates []Template
|
||||
clientIdPath string
|
||||
clientSecretPath string
|
||||
newAccessTokenNotificationChan chan bool
|
||||
removeClientSecretOnRead bool
|
||||
cachedClientSecret string
|
||||
}
|
||||
|
||||
tokenResponse, err := api.CallServiceTokenV3Refresh(httpClient, api.ServiceTokenV3RefreshTokenRequest{RefreshToken: refreshToken})
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("unable to complete renewal because [%s]", err)
|
||||
}
|
||||
func NewTokenManager(fileDeposits []Sink, templates []Template, clientIdPath string, clientSecretPath string, newAccessTokenNotificationChan chan bool, removeClientSecretOnRead bool) *TokenManager {
|
||||
return &TokenManager{filePaths: fileDeposits, templates: templates, clientIdPath: clientIdPath, clientSecretPath: clientSecretPath, newAccessTokenNotificationChan: newAccessTokenNotificationChan, removeClientSecretOnRead: removeClientSecretOnRead}
|
||||
}
|
||||
|
||||
for _, sinkFile := range config.Sinks {
|
||||
if sinkFile.Type == "file" {
|
||||
err = ioutil.WriteFile(sinkFile.Config.Path, []byte(tokenResponse.AccessToken), 0644)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
} else {
|
||||
errChan <- errors.New("unsupported sink type. Only 'file' type is supported")
|
||||
return
|
||||
}
|
||||
}
|
||||
func (tm *TokenManager) SetToken(token string, accessTokenTTL time.Duration, accessTokenMaxTTL time.Duration) {
|
||||
tm.mutex.Lock()
|
||||
defer tm.mutex.Unlock()
|
||||
|
||||
refreshToken = tokenResponse.RefreshToken
|
||||
nextRefreshCycle := time.Duration(tokenResponse.ExpiresIn-5) * time.Second // when the next access refresh will happen
|
||||
tm.accessToken = token
|
||||
tm.accessTokenTTL = accessTokenTTL
|
||||
tm.accessTokenMaxTTL = accessTokenMaxTTL
|
||||
|
||||
d, err := time.ParseDuration(nextRefreshCycle.String())
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("unable to parse refresh time because %s", err)
|
||||
return
|
||||
}
|
||||
tm.newAccessTokenNotificationChan <- true
|
||||
}
|
||||
|
||||
log.Info().Msgf("token refreshed and saved to selected path; next cycle will occur in %s", d.String())
|
||||
func (tm *TokenManager) GetToken() string {
|
||||
tm.mutex.Lock()
|
||||
defer tm.mutex.Unlock()
|
||||
|
||||
for _, secretTemplate := range config.Templates {
|
||||
processedTemplate, err := ProcessTemplate(secretTemplate.SourcePath, nil, tokenResponse.AccessToken)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
return tm.accessToken
|
||||
}
|
||||
|
||||
if err := WriteBytesToFile(processedTemplate, secretTemplate.DestinationPath); err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Msgf("secret template at path %s has been rendered and saved to path %s", secretTemplate.SourcePath, secretTemplate.DestinationPath)
|
||||
}
|
||||
|
||||
time.Sleep(nextRefreshCycle)
|
||||
// Fetches a new access token using client credentials
|
||||
func (tm *TokenManager) FetchNewAccessToken() error {
|
||||
clientIDAsByte, err := ReadFile(tm.clientIdPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read client id from file path '%s' due to error: %v", tm.clientIdPath, err)
|
||||
}
|
||||
|
||||
clientSecretAsByte, err := ReadFile(tm.clientSecretPath)
|
||||
if err != nil {
|
||||
if len(tm.cachedClientSecret) == 0 {
|
||||
return fmt.Errorf("unable to read client secret from file and no cached client secret found: %v", err)
|
||||
} else {
|
||||
clientSecretAsByte = []byte(tm.cachedClientSecret)
|
||||
}
|
||||
}
|
||||
|
||||
// remove client secret after first read
|
||||
if tm.removeClientSecretOnRead {
|
||||
os.Remove(tm.clientSecretPath)
|
||||
}
|
||||
|
||||
clientId := string(clientIDAsByte)
|
||||
clientSecret := string(clientSecretAsByte)
|
||||
|
||||
// save as cache in memory
|
||||
tm.cachedClientSecret = clientSecret
|
||||
|
||||
err, loginResponse := universalAuthLogin(clientId, clientSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accessTokenTTL := time.Duration(loginResponse.AccessTokenTTL * int(time.Second))
|
||||
accessTokenMaxTTL := time.Duration(loginResponse.AccessTokenMaxTTL * int(time.Second))
|
||||
|
||||
if accessTokenTTL <= time.Duration(5)*time.Second {
|
||||
util.PrintErrorMessageAndExit("At this this, agent does not support refresh of tokens with 5 seconds or less ttl. Please increase access token ttl and try again")
|
||||
}
|
||||
|
||||
tm.accessTokenFetchedTime = time.Now()
|
||||
tm.SetToken(loginResponse.AccessToken, accessTokenTTL, accessTokenMaxTTL)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Refreshes the existing access token
|
||||
func (tm *TokenManager) RefreshAccessToken() error {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetRetryCount(10000).
|
||||
SetRetryMaxWaitTime(20 * time.Second).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
|
||||
accessToken := tm.GetToken()
|
||||
response, err := api.CallUniversalAuthRefreshAccessToken(httpClient, api.UniversalAuthRefreshRequest{AccessToken: accessToken})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accessTokenTTL := time.Duration(response.AccessTokenTTL * int(time.Second))
|
||||
accessTokenMaxTTL := time.Duration(response.AccessTokenMaxTTL * int(time.Second))
|
||||
tm.accessTokenRefreshedTime = time.Now()
|
||||
|
||||
tm.SetToken(response.AccessToken, accessTokenTTL, accessTokenMaxTTL)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) ManageTokenLifecycle() {
|
||||
for {
|
||||
accessTokenMaxTTLExpiresInTime := tm.accessTokenFetchedTime.Add(tm.accessTokenMaxTTL - (5 * time.Second))
|
||||
accessTokenRefreshedTime := tm.accessTokenRefreshedTime
|
||||
|
||||
if accessTokenRefreshedTime.IsZero() {
|
||||
accessTokenRefreshedTime = tm.accessTokenFetchedTime
|
||||
}
|
||||
|
||||
nextAccessTokenExpiresInTime := accessTokenRefreshedTime.Add(tm.accessTokenTTL - (5 * time.Second))
|
||||
|
||||
if tm.accessTokenFetchedTime.IsZero() && tm.accessTokenRefreshedTime.IsZero() {
|
||||
// case: init login to get access token
|
||||
log.Info().Msg("attempting to authenticate...")
|
||||
err := tm.FetchNewAccessToken()
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to authenticate because %v. Will retry in 30 seconds", err)
|
||||
|
||||
// wait a bit before trying again
|
||||
time.Sleep((30 * time.Second))
|
||||
continue
|
||||
}
|
||||
} else if time.Now().After(accessTokenMaxTTLExpiresInTime) {
|
||||
log.Info().Msgf("token has reached max ttl, attempting to re authenticate...")
|
||||
err := tm.FetchNewAccessToken()
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to authenticate because %v. Will retry in 30 seconds", err)
|
||||
|
||||
// wait a bit before trying again
|
||||
time.Sleep((30 * time.Second))
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
log.Info().Msgf("attempting to refresh existing token...")
|
||||
err := tm.RefreshAccessToken()
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to refresh token because %v. Will retry in 30 seconds", err)
|
||||
|
||||
// wait a bit before trying again
|
||||
time.Sleep((30 * time.Second))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if accessTokenRefreshedTime.IsZero() {
|
||||
accessTokenRefreshedTime = tm.accessTokenFetchedTime
|
||||
} else {
|
||||
accessTokenRefreshedTime = tm.accessTokenRefreshedTime
|
||||
}
|
||||
|
||||
nextAccessTokenExpiresInTime = accessTokenRefreshedTime.Add(tm.accessTokenTTL - (5 * time.Second))
|
||||
accessTokenMaxTTLExpiresInTime = tm.accessTokenFetchedTime.Add(tm.accessTokenMaxTTL - (5 * time.Second))
|
||||
|
||||
if nextAccessTokenExpiresInTime.After(accessTokenMaxTTLExpiresInTime) {
|
||||
// case: Refreshed so close that the next refresh would occur beyond max ttl (this is because currently, token renew tries to add +access-token-ttl amount of time)
|
||||
// example: access token ttl is 11 sec and max ttl is 30 sec. So it will start with 11 seconds, then 22 seconds but the next time you call refresh it would try to extend it to 33 but max ttl only allows 30, so the token will be valid until 30 before we need to reauth
|
||||
time.Sleep(tm.accessTokenTTL - nextAccessTokenExpiresInTime.Sub(accessTokenMaxTTLExpiresInTime))
|
||||
} else {
|
||||
time.Sleep(tm.accessTokenTTL - (5 * time.Second))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *TokenManager) WriteTokenToFiles() {
|
||||
token := tm.GetToken()
|
||||
for _, sinkFile := range tm.filePaths {
|
||||
if sinkFile.Type == "file" {
|
||||
err := ioutil.WriteFile(sinkFile.Config.Path, []byte(token), 0644)
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to write file sink to path '%s' because %v", sinkFile.Config.Path, err)
|
||||
}
|
||||
|
||||
log.Info().Msgf("new access token saved to file at path '%s'", sinkFile.Config.Path)
|
||||
|
||||
} else {
|
||||
log.Error().Msg("unsupported sink type. Only 'file' type is supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *TokenManager) FetchSecrets() {
|
||||
log.Info().Msgf("template engine started...")
|
||||
for {
|
||||
token := tm.GetToken()
|
||||
if token != "" {
|
||||
for _, secretTemplate := range tm.templates {
|
||||
processedTemplate, err := ProcessTemplate(secretTemplate.SourcePath, nil, token)
|
||||
if err != nil {
|
||||
log.Error().Msgf("template engine: unable to render secrets because %s. Will try again on next cycle", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := WriteBytesToFile(processedTemplate, secretTemplate.DestinationPath); err != nil {
|
||||
log.Error().Msgf("template engine: unable to write secrets to path because %s. Will try again on next cycle", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
log.Info().Msgf("template engine: secret template at path %s has been rendered and saved to path %s", secretTemplate.SourcePath, secretTemplate.DestinationPath)
|
||||
}
|
||||
|
||||
// fetch new secrets every 5 minutes (TODO: add PubSub in the future )
|
||||
time.Sleep(5 * time.Minute)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func universalAuthLogin(clientId string, clientSecret string) (error, api.UniversalAuthLoginResponse) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetRetryCount(10000).
|
||||
SetRetryMaxWaitTime(20 * time.Second).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
|
||||
tokenResponse, err := api.CallUniversalAuthLogin(httpClient, api.UniversalAuthLoginRequest{ClientId: clientId, ClientSecret: clientSecret})
|
||||
if err != nil {
|
||||
return err, api.UniversalAuthLoginResponse{}
|
||||
}
|
||||
|
||||
return nil, tokenResponse
|
||||
}
|
||||
|
||||
// runCmd represents the run command
|
||||
@ -260,7 +438,7 @@ var agentCmd = &cobra.Command{
|
||||
infisical agent
|
||||
`,
|
||||
Use: "agent",
|
||||
Short: "Used to launch a client daemon that streamlines authentication and secret retrieval processes in some environments",
|
||||
Short: "Used to launch a client daemon that streamlines authentication and secret retrieval processes in various environments",
|
||||
DisableFlagsInUseLine: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
@ -282,36 +460,31 @@ var agentCmd = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
errChan := make(chan error)
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
switch configAuthType := agentConfig.Auth.Config.(type) {
|
||||
case TokenAuthConfig:
|
||||
content, err := ReadFile(configAuthType.TokenPath)
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to read initial token from file path %s because %v", configAuthType.TokenPath, err)
|
||||
return
|
||||
}
|
||||
|
||||
refreshToken := string(content)
|
||||
go refreshTokenAndProcessTemplate(refreshToken, agentConfig, errChan)
|
||||
|
||||
case OAuthConfig:
|
||||
// future auth types
|
||||
default:
|
||||
log.Error().Msgf("unknown auth config type. Only 'file' type is supported")
|
||||
return
|
||||
if agentConfig.Auth.Type != "universal-auth" {
|
||||
util.PrintErrorMessageAndExit("Only auth type of 'universal-auth' is supported at this time")
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-errChan:
|
||||
log.Fatal().Msgf("agent stopped due to error: %v", err)
|
||||
os.Exit(1)
|
||||
case <-sigChan:
|
||||
log.Info().Msg("agent is gracefully shutting...")
|
||||
os.Exit(1)
|
||||
configUniversalAuthType := agentConfig.Auth.Config.(UniversalAuth)
|
||||
|
||||
tokenRefreshNotifier := make(chan bool)
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
filePaths := agentConfig.Sinks
|
||||
tm := NewTokenManager(filePaths, agentConfig.Templates, configUniversalAuthType.ClientIDPath, configUniversalAuthType.ClientSecretPath, tokenRefreshNotifier, configUniversalAuthType.RemoveClientSecretOnRead)
|
||||
|
||||
go tm.ManageTokenLifecycle()
|
||||
go tm.FetchSecrets()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tokenRefreshNotifier:
|
||||
go tm.WriteTokenToFiles()
|
||||
case <-sigChan:
|
||||
log.Info().Msg("agent is gracefully shutting...")
|
||||
// TODO: check if we are in the middle of writing files to disk
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
@ -204,7 +204,8 @@ func init() {
|
||||
func executeSingleCommandWithEnvs(args []string, secretsCount int, env []string) error {
|
||||
command := args[0]
|
||||
argsForCommand := args[1:]
|
||||
color.Green("Injecting %v Infisical secrets into your application process", secretsCount)
|
||||
|
||||
log.Info().Msgf(color.GreenString("Injecting %v Infisical secrets into your application process", secretsCount))
|
||||
|
||||
cmd := exec.Command(command, argsForCommand...)
|
||||
cmd.Stdin = os.Stdin
|
||||
@ -232,7 +233,7 @@ func executeMultipleCommandWithEnvs(fullCommand string, secretsCount int, env []
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = env
|
||||
|
||||
color.Green("Injecting %v Infisical secrets into your application process", secretsCount)
|
||||
log.Info().Msgf(color.GreenString("Injecting %v Infisical secrets into your application process", secretsCount))
|
||||
log.Debug().Msgf("executing command: %s %s %s \n", shell[0], shell[1], fullCommand)
|
||||
|
||||
return execCmd(cmd)
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/rs/zerolog/log"
|
||||
@ -21,16 +20,16 @@ func CheckForUpdate() {
|
||||
if checkEnv := os.Getenv("INFISICAL_DISABLE_UPDATE_CHECK"); checkEnv != "" {
|
||||
return
|
||||
}
|
||||
latestVersion, publishedDate, err := getLatestTag("Infisical", "infisical")
|
||||
latestVersion, _, err := getLatestTag("Infisical", "infisical")
|
||||
if err != nil {
|
||||
log.Debug().Err(err)
|
||||
// do nothing and continue
|
||||
return
|
||||
}
|
||||
|
||||
daysSinceRelease, _ := daysSinceDate(publishedDate)
|
||||
// daysSinceRelease, _ := daysSinceDate(publishedDate)
|
||||
|
||||
if latestVersion != CLI_VERSION && daysSinceRelease > 2 {
|
||||
if latestVersion != CLI_VERSION {
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
blue := color.New(color.FgCyan).SprintFunc()
|
||||
black := color.New(color.FgBlack).SprintFunc()
|
||||
@ -151,15 +150,15 @@ func IsRunningInDocker() bool {
|
||||
return strings.Contains(string(cgroup), "docker")
|
||||
}
|
||||
|
||||
func daysSinceDate(dateString string) (int, error) {
|
||||
layout := "2006-01-02T15:04:05Z"
|
||||
parsedDate, err := time.Parse(layout, dateString)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// func daysSinceDate(dateString string) (int, error) {
|
||||
// layout := "2006-01-02T15:04:05Z"
|
||||
// parsedDate, err := time.Parse(layout, dateString)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
|
||||
currentTime := time.Now()
|
||||
difference := currentTime.Sub(parsedDate)
|
||||
days := int(difference.Hours() / 24)
|
||||
return days, nil
|
||||
}
|
||||
// currentTime := time.Now()
|
||||
// difference := currentTime.Sub(parsedDate)
|
||||
// days := int(difference.Hours() / 24)
|
||||
// return days, nil
|
||||
// }
|
||||
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
title: "Create"
|
||||
openapi: "POST /api/v1/workspace/{workspaceId}/environments"
|
||||
openapi: "POST /api/v2/workspace/{workspaceId}/environments"
|
||||
---
|
||||
|
4
docs/api-reference/endpoints/identities/create.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Create"
|
||||
openapi: "POST /api/v1/identities/"
|
||||
---
|
4
docs/api-reference/endpoints/identities/delete.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Delete"
|
||||
openapi: "DELETE /api/v1/identities/{identityId}"
|
||||
---
|
4
docs/api-reference/endpoints/identities/update.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Update"
|
||||
openapi: "PATCH /api/v1/identities/{identityId}"
|
||||
---
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
title: "Delete Membership"
|
||||
title: "Delete User Membership"
|
||||
openapi: "DELETE /api/v2/organizations/{organizationId}/memberships/{membershipId}"
|
||||
---
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "List Identity Memberships"
|
||||
openapi: "GET /api/v2/organizations/{organizationId}/identity-memberships"
|
||||
---
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
title: "Get Memberships"
|
||||
title: "Get User Memberships"
|
||||
openapi: "GET /api/v2/organizations/{organizationId}/memberships"
|
||||
---
|
||||
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
title: "Update Membership"
|
||||
title: "Update User Membership"
|
||||
openapi: "PATCH /api/v2/organizations/{organizationId}/memberships/{membershipId}"
|
||||
---
|
||||
|
@ -2,3 +2,9 @@
|
||||
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>
|
@ -2,3 +2,9 @@
|
||||
title: "Get"
|
||||
openapi: "GET /api/v2/service-token/"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This endpoint will be deprecated in the near future with the removal of service tokens in Q1/Q2 2024.
|
||||
|
||||
We recommend switching to using [identities](/documentation/platform/identities/overview) if your client supports it.
|
||||
</Warning>
|
||||
|
4
docs/api-reference/endpoints/universal-auth/attach.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Attach"
|
||||
openapi: "POST /api/v1/auth/universal-auth/identities/{identityId}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Create Client Secret"
|
||||
openapi: "POST /api/v1/auth/universal-auth/identities/{identityId}/client-secrets"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "List Client Secrets"
|
||||
openapi: "GET /api/v1/auth/universal-auth/identities/{identityId}/client-secrets"
|
||||
---
|
4
docs/api-reference/endpoints/universal-auth/login.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Login"
|
||||
openapi: "POST /api/v1/auth/universal-auth/login"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Renew Access Token"
|
||||
openapi: "POST /api/v1/auth/token/renew"
|
||||
---
|
4
docs/api-reference/endpoints/universal-auth/retrieve.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Retrieve"
|
||||
openapi: "GET /api/v1/auth/universal-auth/identities/{identityId}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Revoke Client Secret"
|
||||
openapi: "POST /api/v1/auth/universal-auth/identities/{identityId}/client-secrets/{clientSecretId}/revoke"
|
||||
---
|
4
docs/api-reference/endpoints/universal-auth/update.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Update"
|
||||
openapi: "PATCH /api/v1/auth/universal-auth/identities/{identityId}"
|
||||
---
|
@ -2,3 +2,9 @@
|
||||
title: "Get My User"
|
||||
openapi: "GET /api/v2/users/me"
|
||||
---
|
||||
|
||||
<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>
|
@ -2,3 +2,9 @@
|
||||
title: "Get My Organizations"
|
||||
openapi: "GET /api/v2/users/me/organizations"
|
||||
---
|
||||
|
||||
<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>
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Delete Identity Membership"
|
||||
openapi: "DELETE /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}"
|
||||
---
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
title: "Delete Membership"
|
||||
title: "Delete User Membership"
|
||||
openapi: "DELETE /api/v2/workspace/{workspaceId}/memberships/{membershipId}"
|
||||
---
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "List Identity Memberships"
|
||||
openapi: "GET /api/v2/workspace/{workspaceId}/identity-memberships"
|
||||
---
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
title: "Get Memberships"
|
||||
title: "Get User Memberships"
|
||||
openapi: "GET /api/v2/workspace/{workspaceId}/memberships"
|
||||
---
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Update Identity Membership"
|
||||
openapi: "PATCH /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}"
|
||||
---
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
title: "Update Membership"
|
||||
title: "Update User Membership"
|
||||
openapi: "PATCH /api/v2/workspace/{workspaceId}/memberships/{membershipId}"
|
||||
---
|
||||
|
@ -2,3 +2,8 @@
|
||||
title: "Get Key"
|
||||
openapi: "GET /api/v2/workspace/{workspaceId}/encrypted-key"
|
||||
---
|
||||
<Warning>
|
||||
This endpoint will be deprecated in the near future in Q1/Q2 2024.
|
||||
|
||||
We recommend using Infisical in non-E2EE mode going forward.
|
||||
</Warning>
|
@ -3,29 +3,34 @@ title: "Authentication"
|
||||
description: "How to authenticate with the Infisical Public API"
|
||||
---
|
||||
|
||||
The Public API accepts multiple modes of authentication being via [Infisical Token](/documentation/platform/token) or API Key.
|
||||
You can authenticate with the Infisical API using [Identities](/documentation/platform/identities/overview) paired with authentication modes such as [Universal Auth](/documentation/platform/identities/universal-auth).
|
||||
|
||||
- [Infisical Token](/documentation/platform/token): Provides short-lived, scoped CRUD access to the secrets of a specific project and environment.
|
||||
- API Key: Provides full access to all endpoints representing the user without ability to encrypt/decrypt secrets for **E2EE** endpoints.
|
||||
To interact with the Infisical API, you will need to obtain an access token. Follow the step by [step guide](/documentation/platform/identities/universal-auth) to get an access token via Universal Auth.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Infisical Token">
|
||||
The Infisical Token mode uses an Infisical Token to authenticate with the API.
|
||||
|
||||
To authenticate requests with Infisical using the Infisical Token, you must include your Infisical Token in the `Authorization` header of HTTP requests made to the platform with the value `Bearer <infisical_token>`.
|
||||
**FAQ**
|
||||
|
||||
You can obtain an Infisical Token in Project Settings > Service Tokens.
|
||||
<AccordionGroup>
|
||||
<Accordion title="What happened to the Service Token and API Key authentication modes?">
|
||||
The Service Token and API Key authentication modes are being deprecated out in favor of [Identities](/documentation/platform/identity).
|
||||
We expect to make a deprecation notice in the coming months alongside a larger deprecation initiative planned for Q1/Q2 2024.
|
||||
|
||||

|
||||
</Tab>
|
||||
<Tab title="API Key">
|
||||
The API key mode uses an API key to authenticate with the API.
|
||||
|
||||
To authenticate requests with Infisical using the API Key, you must include an API key in the `X-API-KEY` header of HTTP requests made to the platform.
|
||||
|
||||
You can obtain an API key in User Settings > API Keys
|
||||
|
||||

|
||||

|
||||
</Tab>
|
||||
</Tabs>
|
||||
With identities, we're improving significantly over the shortcomings of Service Tokens and API Keys. Amongst many differences, identities provide broader access over the Infisical API, utilizes the same role-based
|
||||
permission system used by users, and comes with ample more configurable security measures.
|
||||
</Accordion>
|
||||
<Accordion title="Why can I not create, read, update, or delete an identity?">
|
||||
There are a few reasons for why this might happen:
|
||||
|
||||
- You have insufficient organization permissions to create, read, update, delete identities.
|
||||
- The identity you are trying to read, update, or delete is more privileged than yourself.
|
||||
- The role you are trying to create an identity for or update an identity to is more privileged than yours.
|
||||
</Accordion>
|
||||
<Accordion title="Why is the Infisical API rejecting my identity credentials?">
|
||||
There are a few reasons for why this might happen:
|
||||
|
||||
- The client secret or access token has expired.
|
||||
- The identity is insufficently permissioned to interact with the resources you wish to access.
|
||||
- You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE.
|
||||
- The client secret/access token is being used from an untrusted IP.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "Blind Indices"
|
||||
---
|
||||
|
||||
In April 2023, we added the capability for users to query for secrets by name to improve the user experience of Infisical. Previously, it was only possible to query by id of the secret or fetch all secrets belonging to a project and environment.
|
||||
|
||||
Blind indexing must be enabled for projects created prior to April 2023 to take effect. If your project can be blind indexed, then you'll see a section in your project settings appear as shown below:
|
||||
|
||||

|
||||
|
||||
|
||||
It works using virtually irreversible blind indices generated by applying `argon2id` to the name of each secret and a random 128-bit salt assigned to each project on the server. We continue to keep the values of secrets E2EE by default.
|
||||
|
||||
You can read more about it [here](/security/mechanics).
|
||||
|
||||
<Note>
|
||||
As previously mentioned, all projects made after April 2023 are automatically blind indexed. If you created a project before this date, you have to enable it manually in your project settings.
|
||||
</Note>
|
@ -2,6 +2,10 @@
|
||||
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.
|
||||
|
@ -6,8 +6,4 @@ Infisical's Public (REST) API provides users an alternative way to programmatica
|
||||
secrets via HTTPS requests. This can be useful for automating tasks, such as
|
||||
rotating credentials, or for integrating secret management into a larger system.
|
||||
|
||||
With the Public API, users can create, read, update, and delete secrets, as well as manage access control, query audit logs, and more.
|
||||
|
||||
<Warning>
|
||||
In April 2023, we added the capability for users to query for secrets by name to improve the user experience of Infisical. If your project was created prior to April 2023, please read and follow the section on [blind indices](./blind-indices) and how to enable them for better usage of Infisical.
|
||||
</Warning>
|
||||
With the Public API, you can create, read, update, and delete secrets, as well as manage access control, query audit logs, and more.
|
@ -2,58 +2,127 @@
|
||||
title: "REST API"
|
||||
---
|
||||
|
||||
Infisical's Public (REST) API is the most flexible, platform-agnostic way to read/write secrets for your application.
|
||||
Infisical's REST API is the most flexible way to read/write secrets for your application.
|
||||
|
||||
Prerequisites:
|
||||
In this brief, we'll explore how to fetch a secret back from a project on [Infisical Cloud](https://app.infisical.com) via the REST API.
|
||||
|
||||
- Have a project with secrets ready in [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) scoped to an environment in your project in Infisical.
|
||||
<Steps>
|
||||
<Step title="Create a project with a secret">
|
||||
To create a project, head to your Organization Overview and press **Add New Project**; we'll call the project **Demo App**.
|
||||

|
||||
|
||||

|
||||
|
||||
Next, let's head to the **Development** environment of the project and add a secret `FOO=BAR` to it.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
To keep it simple, we're going to fetch secrets from the API with **End-to-End Encryption (E2EE)** disabled.
|
||||
<Note>
|
||||
For this brief, you'll need to disable end-to-end encryption in your Project Settings
|
||||
</Note>
|
||||
</Step>
|
||||
<Step title="Create an identity">
|
||||
Next, we need to create an identity to represent your application. To create one, head to your Organization Settings > Access Control > Machine Identities and press **Create identity**.
|
||||
|
||||
<Note>
|
||||
It's possible to use the API with **E2EE** enabled but this means learning about how encryption works with Infisical and performing client-side encryption/decryption operations yourself.
|
||||
yourself.
|
||||
|
||||
If **E2EE** is a must for your team, we recommend either using one of the [Infisical SDKs](/documentation/getting-started/sdks) or checking out the [examples for E2EE](/api-reference/overview/examples/e2ee-disabled).
|
||||
</Note>
|
||||

|
||||
|
||||
When creating an identity, you specify an organization level [role](/documentation/platform/role-based-access-controls) for it to assume; you can configure roles in Organization Settings > Access Control > Organization Roles.
|
||||
|
||||

|
||||
|
||||
Once you've created an identity, you'll be prompted to configure the **Universal Auth** authentication method for it.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
</Step>
|
||||
<Step title="Create a Client Secret">
|
||||
In order to use the identity, you'll need the non-sensitive **Client ID**
|
||||
of the identity and a **Client Secret** for it; you can think of these credentials akin to a username
|
||||
and password used to authenticate with the Infisical API. With that, press on the key icon on the identity to generate a **Client Secret**
|
||||
for it.
|
||||
|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
<Step title="Add the identity to the project">
|
||||
To enable the identity to access your project, we need to add it to the project. To do this, head over to the **Demo App** Project Settings > Access Control > Machine Identities and press **Add identity**.
|
||||
|
||||
Head to your Project Settings, where you created your service token, and un-check the **E2EE** setting.
|
||||
Next, select the identity you want to add to the project and the role you want to assign it.
|
||||
|
||||
## Retrieve Secret
|
||||

|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Get an access token for the Infisical API">
|
||||
To access the Infisical API as the identity, you should first perform a login operation
|
||||
that is to exchange the **Client ID** and **Client Secret** of the identity for an access token
|
||||
by making a request to the `/api/v1/auth/universal-auth/login` endpoint.
|
||||
|
||||
#### Sample request
|
||||
|
||||
Retrieve a secret from the project and environment in Infisical scoped to your service token by making a HTTP request with the following format/details:
|
||||
```
|
||||
curl --location --request POST 'https://app.infisical.com/api/v1/auth/universal-auth/login' \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'clientSecret=<client_secret>' \
|
||||
--data-urlencode 'clientId=<client_id>'
|
||||
```
|
||||
|
||||
#### Sample response
|
||||
|
||||
```
|
||||
{
|
||||
"accessToken": "...",
|
||||
"expiresIn": 7200,
|
||||
"tokenType": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
curl --location --request GET 'https://app.infisical.com/api/v3/secrets/raw/secretName?workspaceId=workspaceId&environment=environment' \
|
||||
--header 'Authorization: Bearer serviceToken'
|
||||
```
|
||||
Next, we can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction) to read/write secrets
|
||||
|
||||
<Note>
|
||||
Each identity access token has a time-to-live (TLL) which you can infer from the response of the login operation;
|
||||
the default TTL is `7200` seconds which can be adjusted.
|
||||
|
||||
<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>
|
||||
If an identity access token expires, it can no longer authenticate with the Infisical API. In this case,
|
||||
a new access token should be obtained from the aforementioned login operation.
|
||||
</Note>
|
||||
</Step>
|
||||
<Step title="Fetch back secret">
|
||||
Finally, you can fetch the secret `FOO=BAR` back from **Step 1** by including the access token in the previous step in another request to the `/api/v3/secrets/raw/{secretName}` endpoint.
|
||||
|
||||
Depending on your application requirements, you may wish to use Infisical's API in different ways such as by retaining **E2EE**
|
||||
or fetching multiple secrets at once instead of one at a time.
|
||||
### Sample request
|
||||
|
||||
```
|
||||
curl --location --request GET 'http://localhost:8080/api/v3/secrets/raw/FOO?workspaceId=657830d579cfc8415d06ce5b&environment=dev' \
|
||||
--header 'Authorization: Bearer <access_token>'
|
||||
```
|
||||
|
||||
### Sample response
|
||||
|
||||
Whatever the case, we recommend glossing over the [API Examples](/api-reference/overview/examples/note)
|
||||
to gain a deeper understanding of how you to best leverage the Infisical API for your use-case.
|
||||
```
|
||||
{
|
||||
"secret": {
|
||||
"_id": "6564234b934d634e1fcd6cdf",
|
||||
"version": 1,
|
||||
"workspace": "6564173e934d634e1fcd6950",
|
||||
"type": "shared",
|
||||
"environment": "dev",
|
||||
"secretKey": "FOO2",
|
||||
"secretValue": "BAR2",
|
||||
"secretComment": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that you can fetch a list of secrets back by making a request to the `/api/v3/secrets/raw` endpoint.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
See also:
|
||||
|
||||
- Explore the [API Examples](/api-reference/overview/examples/note)
|
||||
- [API Reference](/api-reference/overview/introduction)
|
54
docs/documentation/platform/identities/overview.mdx
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Identities
|
||||
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.
|
||||
|
||||
We will be releasing compatibility with it across clients in the coming quarter.
|
||||
</Note>
|
||||
|
||||
## Concept
|
||||
|
||||
A (machine) identity is an entity that you can create in an Infisical organization to represent a workload or application that requires access to the Infisical API. This is conceptually similar to an IAM user in AWS or service account in Google Cloud Platform (GCP).
|
||||
|
||||
Each identity must authenticate with the API using a supported authentication method like [Universal Auth](/documentation/platform/identities/universal-auth) to get back a short-lived access token to be used in subsequent requests.
|
||||
|
||||
Key Features:
|
||||
|
||||
- Role Assignment: Identities must be assigned [roles](/documentation/platform/role-based-access-controls). These roles determine the scope of access to resources, either at the organization level or project level.
|
||||
- Auth/Token Configuration: Identities must be configured with auth methods and access token properties to securely interact with the Infisical API.
|
||||
|
||||
## Workflow
|
||||
|
||||
A typical workflow for using identities consists of four steps:
|
||||
|
||||
1. Creating the identity with a name and [role](/documentation/platform/role-based-access-controls) in Organization Access Control > Machine Identities.
|
||||
This step also involves configuring an authentication method for it such as [Universal Auth](/documentation/platform/identities/universal-auth).
|
||||
2. Adding the identity to the project(s) you want it to have access to.
|
||||
3. Authenticating the identity with the Infisical API based on the configured authentication method on it and receiving a short-lived access token back.
|
||||
4. Authenticating subsequent requests with the Infisical API using the short-lived access token.
|
||||
|
||||
Check out the following authentication method-specific guides for step-by-step instruction on how to use identities to access Infisical:
|
||||
|
||||
- [Universal Auth](/documentation/platform/identities/universal-auth)
|
||||
|
||||
**FAQ**
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What is the difference between an identity and service token?">
|
||||
A service token is a project-level authentication method that is being phased out in favor of identities.
|
||||
|
||||
Amongst many differences, identities provide broader access over the Infisical API, utilizes the same role-based
|
||||
permission system used by users, and comes with ample more configurable authentication and security features.
|
||||
</Accordion>
|
||||
<Accordion title="Why can I not create, read, update, or delete an identity?">
|
||||
There are a few reasons for why this might happen:
|
||||
|
||||
- You have insufficient organization permissions to create, read, update, delete identities.
|
||||
- The identity you are trying to read, update, or delete is more privileged than yourself.
|
||||
- The role you are trying to create an identity for or update an identity to is more privileged than yours.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
140
docs/documentation/platform/identities/universal-auth.mdx
Normal file
@ -0,0 +1,140 @@
|
||||
---
|
||||
title: Universal Auth
|
||||
description: "Authenticate with Infisical from any platform/environment"
|
||||
---
|
||||
|
||||
**Universal Auth** is the most versatile authentication method that can be configured on an identity from any platform/environment to access Infisical.
|
||||
|
||||
In this method, each identity is given a **Client ID** for which you can generate one or more **Client Secret(s)**. Together, a **Client ID** and **Client Secret** can be exchanged for an access token to authenticate with the Infisical API.
|
||||
|
||||
## Properties
|
||||
|
||||
Universal Auth supports many settings that can be beneficial for tightening your workflow security configuration:
|
||||
|
||||
- Support for restrictions on the number of times that the **Client Secret(s)** and access token(s) can be used.
|
||||
- Support for expiration, so, if specified, the **Client Secret** of the identity will automatically be defunct after a period of time.
|
||||
- Support for IP allowlisting; this means you can restrict the usage of **Client Secret(s)** and access token to a specific IP or CIDR range.
|
||||
|
||||
## Workflow
|
||||
|
||||
In the following steps, we explore how to create and use identities for your workloads and applications to access the Infisical API
|
||||
using the Universal Auth authentication method.
|
||||
|
||||
<Steps>
|
||||
<Step title="Creating an identity">
|
||||
To create an identity, head to your Organization Settings > Access Control > Machine Identities and press **Create identity**.
|
||||
|
||||

|
||||
|
||||
When creating an identity, you specify an organization level [role](/documentation/platform/role-based-access-controls) for it to assume; you can configure roles in Organization Settings > Access Control > Organization Roles.
|
||||
|
||||

|
||||
|
||||
Now input a few details for your new identity. Here's some guidance for each field:
|
||||
|
||||
- Name (required): A friendly name for the identity.
|
||||
- Role (required): A role from the **Organization Roles** tab for the identity to assume. The organization role assigned will determine what organization level resources this identity can have access to.
|
||||
|
||||
Once you've created an identity, you'll be prompted to configure the **Universal Auth** authentication method for it.
|
||||
|
||||

|
||||
|
||||
Here's some more guidance on each field:
|
||||
|
||||
- Access Token TTL (default is `2592000` equivalent to 30 days): The lifetime for an acccess token in seconds. This value will be referenced at renewal time.
|
||||
- Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time.
|
||||
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
|
||||
- Client Secret Trusted IPs: The IPs or CIDR ranges that the **Client Secret** can be used from together with the **Client ID** to get back an access token. By default, **Client Secrets** are given the `0.0.0.0/0`, allowing usage from any network address.
|
||||
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address.
|
||||
|
||||
<Warning>
|
||||
Restricting **Client Secret** and access token usage to specific trusted IPs is a paid feature.
|
||||
|
||||
If you’re using Infisical Cloud, then it is available under the Pro Tier. If you’re self-hosting Infisical, then you should contact team@infisical.com to purchase an enterprise license to use it.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
<Step title="Creating a Client Secret">
|
||||
In order to use the identity, you'll need the non-sensitive **Client ID**
|
||||
of the identity and a **Client Secret** for it; you can think of these credentials akin to a username
|
||||
and password used to authenticate with the Infisical API. With that, press on the key icon on the identity to generate a **Client Secret**
|
||||
for it.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Feel free to input any (optional) details for the **Client Secret** configuration:
|
||||
|
||||
- Description: A description for the **Client Secret**.
|
||||
- TTL (default is `0`): The time-to-live for the **Client Secret**. By default, the TTL will be set to 0 which implies that the **Client Secret** will never expire; a value of `0` implies an infinite lifetime.
|
||||
- Max Number of Uses (default is `0`): The maximum number of times that the **Client Secret** can be used together with the **Client ID** to get back an access token; a value of `0` implies infinite number of uses.
|
||||
</Step>
|
||||
<Step title="Adding an identity to a project">
|
||||
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.
|
||||
|
||||
To do this, head over to the project you want to add the identity to and go to Project Settings > Access Control > Machine Identities and press **Add identity**.
|
||||
|
||||
Next, select the identity you want to add to the project and the project level role you want to allow it to assume. The project role assigned will determine what project level resources this identity can have access to.
|
||||
|
||||

|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Accessing the Infisical API with the identity">
|
||||
To access the Infisical API as the identity, you should first perform a login operation
|
||||
that is to exchange the **Client ID** and **Client Secret** of the identity for an access token
|
||||
by making a request to the `/api/v1/auth/universal-auth/login` endpoint.
|
||||
|
||||
#### Sample request
|
||||
|
||||
```
|
||||
curl --location --request POST 'https://app.infisical.com/api/v1/auth/universal-auth/login' \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'clientSecret=...' \
|
||||
--data-urlencode 'clientId=...'
|
||||
```
|
||||
|
||||
#### Sample response
|
||||
|
||||
```
|
||||
{
|
||||
"accessToken": "...",
|
||||
"expiresIn": 7200,
|
||||
"accessTokenMaxTTL": 43244
|
||||
"tokenType": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
Next, you can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction)
|
||||
|
||||
<Note>
|
||||
Each identity access token has a time-to-live (TLL) which you can infer from the response of the login operation;
|
||||
the default TTL is `7200` seconds which can be adjusted.
|
||||
|
||||
If an identity access token expires, it can no longer authenticate with the Infisical API. In this case,
|
||||
a new access token should be obtained by performing another login operation.
|
||||
</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
**FAQ**
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Why is the Infisical API rejecting my identity credentials?">
|
||||
There are a few reasons for why this might happen:
|
||||
|
||||
- The client secret or access token has expired.
|
||||
- The identity is insufficently permissioned to interact with the resources you wish to access.
|
||||
- You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE.
|
||||
- The client secret/access token is being used from an untrusted IP.
|
||||
</Accordion>
|
||||
<Accordion title="What is access token renewal and TTL/Max TTL?">
|
||||
A identity access token can have a time-to-live (TTL) or incremental lifetime afterwhich it expires.
|
||||
|
||||
In certain cases, you may want to extend the lifespan of an access token; to do so, you must set a max TTL parameter.
|
||||
|
||||
A token can be renewed any number of time and each call to renew it will extend the toke life by increments of access token TTL.
|
||||
Regardless of how frequently an access token is renewed, its lifespan remains bound to the maximum TTL determined at its creation
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
@ -1,168 +0,0 @@
|
||||
---
|
||||
title: Identity
|
||||
description: "Programmatically interact with Infisical"
|
||||
---
|
||||
|
||||
A (machine) identity is an entity that you can create in Infisical.
|
||||
Each identity represents a workload that wishes to access the Infisical API via an authentication method; this is similar to an IAM user in AWS or service account in GCP.
|
||||
|
||||
An identity can be provisioned scoped access to resources at the organization or project-level via [role-based access controls (RBAC)](/documentation/platform/role-based-access-controls). For instance, you may create a identity with scoped access to
|
||||
fetch secrets back from the `/` path of the `development` environment in some project.
|
||||
|
||||
<Note>
|
||||
The identity feature is in beta.
|
||||
|
||||
Currently, an identity can only be used to make authenticated requests to the Infisical API and does 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.
|
||||
|
||||
We will be releasing compatibility with it across clients in the coming quarter.
|
||||
</Note>
|
||||
|
||||
Each identity can be configured an authentication method. The only supported method at the moment is **Universal Auth (UA)**
|
||||
which has the following properties:
|
||||
|
||||
- In UA, each identity is assigned a **Client ID** for which you can generate one or more **Client Secret(s)**. Together, a **Client ID** and **Client Secret** can be exchanged for an access token (i.e. login operation) to authenticate with the Infisical API.
|
||||
- UA supports restrictions on the number of times that the **Client Secret(s)** and access token(s) can be used.
|
||||
- UA supports token renewal that is the ability to extend the lifetime of a token by its TTL up to its maximum TTL since its creation.
|
||||
- UA supports IP allowlisting; this means you can restrict the usage of **Client Secret(s)** and access token to a specific IP or CIDR range.
|
||||
- UA support expiration, so, if specified, the client secret of the identity will automatically be defunct after a period of time.
|
||||
- UA tracks most recent usage of their client secrets and access tokens; it also keeps track of each token's usage count.
|
||||
|
||||
## Using identities
|
||||
|
||||
In the following steps, we explore how to create and use identities for your applications to access the Infisical API.
|
||||
|
||||
<Steps>
|
||||
<Step title="Creating an identity">
|
||||
To create an identity, head to your Organization Settings > Access Control > Machine Identities and press **Create identity**.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Now input a few details for your new identity. Here's some guidance for each field:
|
||||
|
||||
- Name (required): A friendly name for the identity.
|
||||
- Role (required): A role from the **Organization Roles** tab to permit the identity to access certain resources.
|
||||
|
||||
Once you've created an identity, you'll be prompted to configure the **Universal Auth** authentication method for it.
|
||||
|
||||
- Access Token TTL (default is `7200`): The incremental lifetime for an acccess token in seconds; a value of `0` implies an infinite incremental lifetime.
|
||||
- Access Token Max TTL (default is `7200`): The maximum lifetime for an acccess token in seconds; a value of `0` implies an infinite maximum lifetime.
|
||||
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
|
||||
- Client Secret Trusted IPs: The IPs or CIDR ranges that the **Client Secret** can be used from together with the **Client ID** to get back an access token. By default, **Client Secrets** are given the `0.0.0.0/0` entry representing all possible IPv4 addresses.
|
||||
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0` entry representing all possible IPv4 addresses.
|
||||
|
||||
<Warning>
|
||||
Restricting **Client Secret** and access token usage to specific trusted IPs is a paid feature.
|
||||
|
||||
If you’re using Infisical Cloud, then it is available under the Pro Tier. If you’re self-hosting Infisical, then you should contact team@infisical.com to purchase an enterprise license to use it.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
<Step title="Creating a Client Secret">
|
||||
In order to use the identity, you'll need the non-sensitive **Client ID**
|
||||
of the identity and a **Client Secret** for it; you can think of these credentials akin to a username
|
||||
and password used to authenticate with the Infisical API. With that, press on the key icon on the identity to generate a **Client Secret**
|
||||
for it.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Feel free to input any (optional) details for the **Client Secret** configuration:
|
||||
|
||||
- Description: A description for the **Client Secret**.
|
||||
- TTL (default is `0`): The time-to-live for the **Client Secret**. By default, the TTL will be set to 0 which implies that the **Client Secret** will never expire; a value of `0` implies an infinite lifetime.
|
||||
- Max Number of Uses (default is `0`): The maximum number of times that the **Client Secret** can be used together with the **Client ID** to get back an access token; a value of `0` implies infinite number of uses.
|
||||
</Step>
|
||||
<Step title="Adding an identity to a project">
|
||||
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.
|
||||
|
||||
To do this, head over to the project you want to add the identity to and go to Project Settings > Access Control > Machine Identities and press **Add identity**.
|
||||
|
||||
Next, select the identity you want to add to the project and the role you want to assign it.
|
||||
|
||||

|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Accessing the Infisical API with the identity">
|
||||
To access the Infisical API as the identity, you should first perform a login operation
|
||||
that is to exchange the **Client ID** and **Client Secret** of the MI for an access token
|
||||
by making a request to the `/api/v1/auth/universal-auth/login` endpoint.
|
||||
|
||||
#### Sample request
|
||||
|
||||
```
|
||||
curl --location --request POST 'https://app.infisical.com/api/v1/auth/universal-auth/login' \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode 'clientSecret=...' \
|
||||
--data-urlencode 'clientId=...'
|
||||
```
|
||||
|
||||
#### Sample response
|
||||
|
||||
```
|
||||
{
|
||||
"accessToken": "...",
|
||||
"expiresIn": 7200,
|
||||
"tokenType": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
Next, you can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction)
|
||||
|
||||
<Note>
|
||||
Each identity access token has a time-to-live (TLL) which you can infer from the response of the login operation;
|
||||
the default TTL is `7200` seconds which can be adjusted.
|
||||
|
||||
If an identity access token expires, it can no longer authenticate with the Infisical API. In this case,
|
||||
a new access token should be obtained from the aforementioned login operation.
|
||||
</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
**FAQ**
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What is the difference between an identity and service token?">
|
||||
A service token is a project-level authentication method that is being phased out in favor of identities.
|
||||
|
||||
Amongst many differences, identities provide broader access over the Infisical API, utilizes the same role-based
|
||||
permission system used by users, and comes with ample more configurable security measures.
|
||||
</Accordion>
|
||||
<Accordion title="Why is the Infisical API rejecting my identity credentials?">
|
||||
There are a few reasons for why this might happen:
|
||||
|
||||
- The client secret or access token has expired.
|
||||
- The identity is insufficently permissioned to interact with the resources you wish to access.
|
||||
- You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE.
|
||||
- The client secret/access token is being used from an untrusted IP.
|
||||
</Accordion>
|
||||
<Accordion title="What is token renewal and TTL/Max TTL?">
|
||||
A identity access token can have a time-to-live (TTL) or incremental lifetime afterwhich it expires.
|
||||
|
||||
In certain cases, you may want to extend the lifespan of an access token; to do so, you must use the max TTL parameter.
|
||||
When TTL and max TTL are equal, a token is not renewable; when max TTL is greater than TTL, a token is renewable.
|
||||
In the latter case, a token still expires at its TTL but its lifetime can be extended/renewed up until its max TLL.
|
||||
|
||||
Note that the max TTL cannot be less than the TTL for an access token.
|
||||
</Accordion>
|
||||
<Accordion title="Why can I not create, read, update, or delete an identity?">
|
||||
There are a few reasons for why this might happen:
|
||||
|
||||
- You have insufficient organization permissions to create, read, update, delete identities.
|
||||
- The identity you are trying to read, update, or delete is more privileged than yourself.
|
||||
- The role you are trying to create an identity for or update an identity to is more privileged than yours.
|
||||
</Accordion>
|
||||
<Accordion title="Can you provide examples for using glob patterns?">
|
||||
1. `/**`: This pattern matches all folders at any depth in the directory structure. For example, it would match folders like `/folder1/`, `/folder1/subfolder/`, and so on.
|
||||
|
||||
2. `/*`: This pattern matches all immediate subfolders in the current directory. It does not match any folders at a deeper level. For example, it would match folders like `/folder1/`, `/folder2/`, but not `/folder1/subfolder/`.
|
||||
|
||||
3. `/*/*`: This pattern matches all subfolders at a depth of two levels in the current directory. It does not match any folders at a shallower or deeper level. For example, it would match folders like `/folder1/subfolder/`, `/folder2/subfolder/`, but not `/folder1/` or `/folder1/subfolder/subsubfolder/`.
|
||||
|
||||
4. `/folder1/*`: This pattern matches all immediate subfolders within the `/folder1/` directory. It does not match any folders outside of `/folder1/`, nor does it match any subfolders within those immediate subfolders. For example, it would match folders like `/folder1/subfolder1/`, `/folder1/subfolder2/`, but not `/folder2/subfolder/`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
BIN
docs/images/getting-started/api/org-create-project-1.png
Normal file
After Width: | Height: | Size: 621 KiB |
BIN
docs/images/getting-started/api/org-create-project-2.png
Normal file
After Width: | Height: | Size: 437 KiB |
BIN
docs/images/getting-started/api/project-create-secret.png
Normal file
After Width: | Height: | Size: 399 KiB |
BIN
docs/images/getting-started/api/project-dashboard.png
Normal file
After Width: | Height: | Size: 509 KiB |
BIN
docs/images/getting-started/api/project-explore-env.png
Normal file
After Width: | Height: | Size: 606 KiB |
After Width: | Height: | Size: 514 KiB |
After Width: | Height: | Size: 504 KiB |
BIN
docs/images/platform/identities/identities-org-client-secret.png
Normal file
After Width: | Height: | Size: 678 KiB |
After Width: | Height: | Size: 494 KiB |
BIN
docs/images/platform/identities/identities-org-create.png
Normal file
After Width: | Height: | Size: 410 KiB |
BIN
docs/images/platform/identities/identities-org.png
Normal file
After Width: | Height: | Size: 656 KiB |
BIN
docs/images/platform/identities/identities-project-create.png
Normal file
After Width: | Height: | Size: 414 KiB |
BIN
docs/images/platform/identities/identities-project.png
Normal file
After Width: | Height: | Size: 645 KiB |
Before Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 1.2 MiB |
@ -12,7 +12,7 @@ It eliminates the need to modify application logic by enabling clients to decide
|
||||
- Templating: Renders secrets via user provided templates to desired formats for applications to consume
|
||||
|
||||
### Token renewal
|
||||
The Infisical agent can help manage the life cycle of access tokens. The token renewal process is split into two main components: a Method, which is the authentication process suitable for your current setup, and Sinks, which are the places where the agent deposits the new access token whenever it receives updates.
|
||||
The Infisical agent can help manage the life cycle of access tokens. The token renewal process is split into two main components: a `Method`, which is the authentication process suitable for your current setup, and `Sinks`, which are the places where the agent deposits the new access token whenever it receives updates.
|
||||
|
||||
When the Infisical Agent is started, it will attempt to obtain a valid access token using the authentication method you have configured. If the agent is unable to fetch a valid token, the agent will keep trying, increasing the time between each attempt.
|
||||
|
||||
@ -43,8 +43,10 @@ While specifying an authentication method is mandatory to start the agent, confi
|
||||
| Field | Description |
|
||||
| ---------------------------- | ----------- |
|
||||
| `infisical.address` | The URL of the Infisical service. Default: `"https://app.infisical.com"`. |
|
||||
| `auth.type` | The type of authentication method used. Only `"token"` type is currently available |
|
||||
| `auth.config.token-path` | The file path where the initial token for authentication is stored. |
|
||||
| `auth.type` | The type of authentication method used. Only `"universal-auth"` type is currently available |
|
||||
| `auth.config.client-id` | The file path where the universal-auth client id is stored. |
|
||||
| `auth.config.client-secret` | The file path where the universal-auth client secret is stored. |
|
||||
| `auth.config.remove_client_secret_on_read` | This will instruct the agent to remove the client secret from disk. |
|
||||
| `sinks[].type` | The type of sink in a list of sinks. Each item specifies a sink type. Currently, only `"file"` type is available. |
|
||||
| `sinks[].config.path` | The file path where the access token should be stored for each sink in the list. |
|
||||
| `templates[].source-path` | The path to the template file that should be used to render secrets. |
|
||||
@ -54,15 +56,19 @@ While specifying an authentication method is mandatory to start the agent, confi
|
||||
## Quick start Infisical Agent
|
||||
To install the Infisical agent, you must first install the [Infisical CLI](../cli/overview) in the desired environment where you'd like the agent to run. This is because the Infisical agent is a sub-command of the Infisical CLI.
|
||||
|
||||
Once you have the CLI installed, you will need to create a agent configuration file in yaml.
|
||||
Once you have the CLI installed, you will need to provision programmatic access for the agent via [Universal Auth](/documentation/platform/identities/universal-auth). To obtain a **Client ID** and a **Client Secret**, follow the step by step guide outlined [here](/documentation/platform/identities/universal-auth).
|
||||
|
||||
Next, create agent config file as shown below.
|
||||
|
||||
```yaml example-agent-config-file.yaml
|
||||
infisical:
|
||||
address: "https://app.infisical.com"
|
||||
auth:
|
||||
type: "token"
|
||||
type: "universal-auth"
|
||||
config:
|
||||
token-path: "/path/to/initial/token"
|
||||
client-id: "./client-id"
|
||||
client-secret: "./client-secret"
|
||||
remove_client_secret_on_read: false
|
||||
sinks:
|
||||
- type: "file"
|
||||
config:
|
||||
|
@ -7,25 +7,26 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Bitbucket">
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for Bitbucket
|
||||
Press on the Bitbucket tile and grant Infisical access to your Bitbucket account.
|
||||
|
||||
Press on the Bitbucket tile and grant Infisical access to your Bitbucket account.
|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Bitbucket repo and press start integration to start syncing secrets to the repo.
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Bitbucket repo and press start integration to start syncing secrets to the repo.
|
||||
|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -7,30 +7,31 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for CircleCI">
|
||||
Obtain an API token in User Settings > Personal API Tokens
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for CircleCI
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
Obtain an API token in User Settings > Personal API Tokens
|
||||

|
||||
|
||||

|
||||
Press on the CircleCI tile and input your CircleCI API token to grant Infisical access to your CircleCI account.
|
||||
|
||||
Press on the CircleCI tile and input your CircleCI API token to grant Infisical access to your CircleCI account.
|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which CircleCI project and press create integration to start syncing secrets to CircleCI.
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which CircleCI project and press create integration to start syncing secrets to CircleCI.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -7,31 +7,32 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Codefresh">
|
||||
Obtain an API key in User Settings > API Keys
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
## Authorize Infisical for Codefresh
|
||||

|
||||
|
||||
Press on the Codefresh tile and input your Codefresh API key to grant Infisical access to your Codefresh account.
|
||||
|
||||
Obtain an API key in User Settings > API Keys
|
||||

|
||||
|
||||

|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Codefresh service and press create integration to start syncing secrets to Codefresh.
|
||||
|
||||
Press on the Codefresh tile and input your Codefresh API key to grant Infisical access to your Codefresh account.
|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Codefresh service and press create integration to start syncing secrets to Codefresh.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -5,71 +5,71 @@ description: "How to sync secrets from Infisical to GitHub Actions"
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Warning>
|
||||
Infisical can sync secrets to GitHub repo secrets only. If your repo uses environment secrets, then stay tuned with this [issue](https://github.com/Infisical/infisical/issues/54).
|
||||
</Warning>
|
||||
<Warning>
|
||||
Infisical can sync secrets to GitHub repo secrets only. If your repo uses environment secrets, then stay tuned with this [issue](https://github.com/Infisical/infisical/issues/54).
|
||||
</Warning>
|
||||
|
||||
Prerequisites:
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Ensure you have admin privileges to the repo you want to sync secrets to.
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Ensure you have admin privileges to the repo you want to sync secrets to.
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for GitHub">
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for GitHub
|
||||
Press on the GitHub tile and grant Infisical access to your GitHub account (repo privileges only).
|
||||
|
||||
Press on the GitHub tile and grant Infisical access to your GitHub account (repo privileges only).
|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
|
||||
Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which GitHub repo and press start integration to start syncing secrets to the repo.
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
|
||||
Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which GitHub repo and press start integration to start syncing secrets to the repo.
|
||||
|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="Self-Hosted Setup">
|
||||
Using the GitHub integration on a self-hosted instance of Infisical requires configuring an OAuth application in GitHub
|
||||
and registering your instance with it.
|
||||
|
||||
## Create an OAuth application in GitHub
|
||||
|
||||
Navigate to your user Settings > Developer settings > OAuth Apps to create a new GitHub OAuth application.
|
||||
|
||||

|
||||

|
||||

|
||||
<Steps>
|
||||
<Step title="Create an OAuth application in GitHub">
|
||||
Navigate to your user Settings > Developer settings > OAuth Apps to create a new GitHub OAuth application.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Create the OAuth application. As part of the form, set the **Homepage URL** to your self-hosted domain `https://your-domain.com`
|
||||
and the **Authorization callback URL** to `https://your-domain.com/integrations/github/oauth2/callback`.
|
||||
Create the OAuth application. As part of the form, set the **Homepage URL** to your self-hosted domain `https://your-domain.com`
|
||||
and the **Authorization callback URL** to `https://your-domain.com/integrations/github/oauth2/callback`.
|
||||
|
||||

|
||||
|
||||
<Note>
|
||||
If you have a GitHub organization, you can create an OAuth application under it
|
||||
in your organization Settings > Developer settings > OAuth Apps > New Org OAuth App.
|
||||
</Note>
|
||||
|
||||
## Add your OAuth application credentials to Infisical
|
||||
|
||||
Obtain the **Client ID** and generate a new **Client Secret** for your GitHub OAuth application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your GitHub OAuth application:
|
||||

|
||||
|
||||
<Note>
|
||||
If you have a GitHub organization, you can create an OAuth application under it
|
||||
in your organization Settings > Developer settings > OAuth Apps > New Org OAuth App.
|
||||
</Note>
|
||||
</Step>
|
||||
<Step title="Add your OAuth application credentials to Infisical">
|
||||
Obtain the **Client ID** and generate a new **Client Secret** for your GitHub OAuth application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your GitHub OAuth application:
|
||||
|
||||
- `CLIENT_ID_GITHUB`: The **Client ID** of your GitHub OAuth application.
|
||||
- `CLIENT_SECRET_GITHUB`: The **Client Secret** of your GitHub OAuth application.
|
||||
|
||||
Once added, restart your Infisical instance and use the GitHub integration.
|
||||
|
||||
- `CLIENT_ID_GITHUB`: The **Client ID** of your GitHub OAuth application.
|
||||
- `CLIENT_SECRET_GITHUB`: The **Client Secret** of your GitHub OAuth application.
|
||||
|
||||
Once added, restart your Infisical instance and use the GitHub integration.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
@ -5,112 +5,112 @@ description: "How to sync secrets from Infisical to GitLab"
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
Prerequisites:
|
||||
Prerequisites:
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
<AccordionGroup>
|
||||
<Accordion title="Standard">
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for GitLab">
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Standard">
|
||||
## Navigate to your project's integrations tab
|
||||

|
||||
|
||||

|
||||
Press on the GitLab tile and grant Infisical access to your GitLab account.
|
||||
|
||||
## Authorize Infisical for GitLab
|
||||

|
||||
|
||||
Press on the GitLab tile and grant Infisical access to your GitLab account.
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which GitLab repository and press create integration to start syncing secrets to GitLab.
|
||||
|
||||

|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
## Start integration
|
||||
Note that the GitLab integration supports a few options in the **Options** tab:
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which GitLab repository and press create integration to start syncing secrets to GitLab.
|
||||
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||
|
||||

|
||||
Setting a secret prefix or suffix ensures that existing secrets in GitLab are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GitLab without the specified prefix or suffix.
|
||||
|
||||
Note that the GitLab integration supports a few options in the **Options** tab:
|
||||

|
||||
|
||||
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
</Accordion>
|
||||
<Accordion title="Pipeline">
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for GitLab">
|
||||
Generate an [Infisical Token](/documentation/platform/token) for the specific project and environment in Infisical.
|
||||
|
||||
Setting a secret prefix or suffix ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GitLab without the specified prefix or suffix.
|
||||
Next, create a new variable called `INFISICAL_TOKEN` with the value set to the token from the previous step in Settings > CI/CD > Variables of your GitLab repository.
|
||||
</Step>
|
||||
<Step title="Configure Infisical in your pipeline">
|
||||
Edit your `.gitlab-ci.yml` to include the Infisical CLI installation. This will allow you to use the CLI for fetching and injecting secrets into any script or command within your Gitlab CI/CD process.
|
||||
|
||||

|
||||
#### Example
|
||||
|
||||

|
||||
</Accordion>
|
||||
<Accordion title="Pipeline">
|
||||
## Generate service token
|
||||
```yaml
|
||||
image: ubuntu
|
||||
|
||||
Generate an [Infisical Token](/documentation/platform/token) for the specific project and environment in Infisical.
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
|
||||
## Set the Infisical Token in Gitlab
|
||||
|
||||
Create a new variable called `INFISICAL_TOKEN` with the value set to the token from the previous step in Settings > CI/CD > Variables of your GitLab repository.
|
||||
|
||||
## Configure Infisical in your pipeline
|
||||
|
||||
Edit your `.gitlab-ci.yml` to include the Infisical CLI installation. This will allow you to use the CLI for fetching and injecting secrets into any script or command within your Gitlab CI/CD process.
|
||||
|
||||
#### Example
|
||||
|
||||
```yaml
|
||||
image: ubuntu
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
|
||||
build-job:
|
||||
stage: build
|
||||
script:
|
||||
- apt update && apt install -y curl
|
||||
- curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash
|
||||
- apt-get update && apt-get install -y infisical
|
||||
- infisical run -- npm run build
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
build-job:
|
||||
stage: build
|
||||
script:
|
||||
- apt update && apt install -y curl
|
||||
- curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash
|
||||
- apt-get update && apt-get install -y infisical
|
||||
- infisical run -- npm run build
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</Tab>
|
||||
<Tab title="Self-Hosted Setup">
|
||||
Using the GitLab integration on a self-hosted instance of Infisical requires configuring an application in GitLab
|
||||
and registering your instance with it.
|
||||
|
||||
## Create an OAuth application in GitLab
|
||||
Using the GitLab integration on a self-hosted instance of Infisical requires configuring an application in GitLab
|
||||
and registering your instance with it.
|
||||
|
||||
Navigate to your user Settings > Applications to create a new GitLab application.
|
||||
|
||||

|
||||

|
||||
|
||||
Create the application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/gitlab/oauth2/callback`.
|
||||
<Steps>
|
||||
<Step title="Create an OAuth application in GitLab">
|
||||
Navigate to your user Settings > Applications to create a new GitLab application.
|
||||
|
||||

|
||||

|
||||
|
||||
Create the application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/gitlab/oauth2/callback`.
|
||||
|
||||

|
||||
|
||||
<Note>
|
||||
If you have a GitLab group, you can create an OAuth application under it
|
||||
in your group Settings > Applications.
|
||||
</Note>
|
||||
|
||||
## Add your OAuth application credentials to Infisical
|
||||
|
||||
Obtain the **Application ID** and **Secret** for your GitLab application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your GitLab application:
|
||||

|
||||
|
||||
<Note>
|
||||
If you have a GitLab group, you can create an OAuth application under it
|
||||
in your group Settings > Applications.
|
||||
</Note>
|
||||
</Step>
|
||||
<Step title="Add your OAuth application credentials to Infisical">
|
||||
Obtain the **Application ID** and **Secret** for your GitLab application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your GitLab application:
|
||||
|
||||
- `CLIENT_ID_GITLAB`: The **Client ID** of your GitLab application.
|
||||
- `CLIENT_SECRET_GITLAB`: The **Secret** of your GitLab application.
|
||||
|
||||
Once added, restart your Infisical instance and use the GitLab integration.
|
||||
|
||||
- `CLIENT_ID_GITLAB`: The **Client ID** of your GitLab application.
|
||||
- `CLIENT_SECRET_GITLAB`: The **Secret** of your GitLab application.
|
||||
|
||||
Once added, restart your Infisical instance and use the GitLab integration.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
@ -7,30 +7,31 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Travis CI">
|
||||
Obtain your API token in User Settings > API authentication > Token
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for Travis CI
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
Obtain your API token in User Settings > API authentication > Token
|
||||

|
||||
|
||||

|
||||
Press on the Travis CI tile and input your Travis CI API token to grant Infisical access to your Travis CI account.
|
||||
|
||||
Press on the Travis CI tile and input your Travis CI API token to grant Infisical access to your Travis CI account.
|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Travis CI repository and press create integration to start syncing secrets to Travis CI.
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Travis CI repository and press create integration to start syncing secrets to Travis CI.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -8,68 +8,69 @@ Prerequisites:
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Set up AWS and have/create an IAM user
|
||||
|
||||
## Grant the IAM user permissions to access AWS Parameter Store
|
||||
<Steps>
|
||||
<Step title="Grant the IAM user permissions to access AWS Parameter Store">
|
||||
Navigate to your IAM user permissions and add a permission policy to grant access to AWS Parameter Store.
|
||||
|
||||
Navigate to your IAM user permissions and add a permission policy to grant access to AWS Parameter Store.
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
For enhanced security, here's a custom policy containing the minimum permissions required by Infisical to sync secrets to AWS Parameter Store for the IAM user that you can use:
|
||||
|
||||
For enhanced security, here's a custom policy containing the minimum permissions required by Infisical to sync secrets to AWS Parameter Store for the IAM user that you can use:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
```json
|
||||
{
|
||||
"Sid": "AllowSSMAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ssm:PutParameter",
|
||||
"ssm:DeleteParameter",
|
||||
"ssm:GetParametersByPath",
|
||||
"ssm:DeleteParameters"
|
||||
],
|
||||
"Resource": "*"
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowSSMAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ssm:PutParameter",
|
||||
"ssm:DeleteParameter",
|
||||
"ssm:GetParametersByPath",
|
||||
"ssm:DeleteParameters"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
</Step>
|
||||
<Step title="Authorize Infisical for AWS Parameter store">
|
||||
Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||

|
||||

|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for AWS Parameter store
|
||||
Press on the AWS Parameter Store tile and input your AWS access key ID and secret access key from the previous step.
|
||||
|
||||
Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys
|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which AWS Parameter Store region and indicate the path for your secrets. Then, press create integration to start syncing secrets to AWS Parameter Store.
|
||||
|
||||
Press on the AWS Parameter Store tile and input your AWS access key ID and secret access key from the previous step.
|
||||

|
||||
|
||||

|
||||
<Tip>
|
||||
Infisical requires you to add a path for your secrets to be stored in AWS
|
||||
Parameter Store and recommends setting the path structure to
|
||||
`/[project_name]/[environment]/` according to best practices. This enables a
|
||||
secret like `TEST` to be stored as `/[project_name]/[environment]/TEST` in AWS
|
||||
Parameter Store.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which AWS Parameter Store region and indicate the path for your secrets. Then, press create integration to start syncing secrets to AWS Parameter Store.
|
||||
|
||||

|
||||
|
||||
<Tip>
|
||||
Infisical requires you to add a path for your secrets to be stored in AWS
|
||||
Parameter Store and recommends setting the path structure to
|
||||
`/[project_name]/[environment]/` according to best practices. This enables a
|
||||
secret like `TEST` to be stored as `/[project_name]/[environment]/TEST` in AWS
|
||||
Parameter Store.
|
||||
</Tip>
|
||||
|
@ -8,66 +8,66 @@ Prerequisites:
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Set up AWS and have/create an IAM user
|
||||
|
||||
## Grant the IAM user permissions to access AWS Secrets Manager
|
||||
<Steps>
|
||||
<Step title="Grant the IAM user permissions to access AWS Secrets Manager">
|
||||
Navigate to your IAM user permissions and add a permission policy to grant access to AWS Secrets Manager.
|
||||
|
||||
Navigate to your IAM user permissions and add a permission policy to grant access to AWS Secrets Manager.
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
For better security, here's a custom policy containing the minimum permissions required by Infisical to sync secrets to AWS Secrets Manager for the IAM user that you can use:
|
||||
|
||||
For better security, here's a custom policy containing the minimum permissions required by Infisical to sync secrets to AWS Secrets Manager for the IAM user that you can use:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
```json
|
||||
{
|
||||
"Sid": "AllowSecretsManagerAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue",
|
||||
"secretsmanager:CreateSecret",
|
||||
"secretsmanager:UpdateSecret"
|
||||
],
|
||||
"Resource": "*"
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowSecretsManagerAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue",
|
||||
"secretsmanager:CreateSecret",
|
||||
"secretsmanager:UpdateSecret"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
</Step>
|
||||
<Step title="Authorize Infisical for AWS Secrets Manager">
|
||||
Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||

|
||||

|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for AWS Secrets Manager
|
||||
Press on the AWS Secrets Manager tile and input your AWS access key ID and secret access key from the previous step.
|
||||
|
||||
Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys
|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which AWS Secrets Manager region and under which secret name. Then, press create integration to start syncing secrets to AWS Secrets Manager.
|
||||
|
||||
Press on the AWS Secrets Manager tile and input your AWS access key ID and secret access key from the previous step.
|
||||

|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which AWS Secrets Manager region and under which secret name. Then, press create integration to start syncing secrets to AWS Secrets Manager.
|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
Infisical currently syncs environment variables to AWS Secrets Manager as
|
||||
key-value pairs under one secret. We're actively exploring ways to help users
|
||||
group environment variable key-pairs under multiple secrets for greater
|
||||
control.
|
||||
</Info>
|
||||
<Info>
|
||||
Infisical currently syncs environment variables to AWS Secrets Manager as
|
||||
key-value pairs under one secret. We're actively exploring ways to help users
|
||||
group environment variable key-pairs under multiple secrets for greater
|
||||
control.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
@ -5,69 +5,69 @@ description: "How to sync secrets from Infisical to Azure Key Vault"
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
Prerequisites:
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Set up Azure and have an existing key vault
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Set up Azure and have an existing key vault
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Azure Key Vault">
|
||||
Navigate to your project's integrations tab
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for Azure Key Vault
|
||||
Press on the Azure Key Vault tile and grant Infisical access to Azure Key Vault.
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Obtain the Vault URI of your key vault in the Overview tab.
|
||||
|
||||
Press on the Azure Key Vault tile and grant Infisical access to Azure Key Vault.
|
||||

|
||||
|
||||
## Start Integration
|
||||
Select which Infisical environment secrets you want to sync to your key vault. Then, input your Vault URI from the previous step. Finally, press create integration to start syncing secrets to Azure Key Vault.
|
||||
|
||||
Obtain the Vault URI of your key vault in the Overview tab.
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
Select which Infisical environment secrets you want to sync to your key vault. Then, input your Vault URI from the previous step. Finally, press create integration to start syncing secrets to Azure Key Vault.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="Self-Hosted Setup">
|
||||
Using the Azure KV integration on a self-hosted instance of Infisical requires configuring an application in Azure
|
||||
and registering your instance with it.
|
||||
|
||||
## Create an application in Azure
|
||||
|
||||
Navigate to Azure Active Directory > App registrations to create a new application.
|
||||
|
||||

|
||||

|
||||
Using the Azure KV integration on a self-hosted instance of Infisical requires configuring an application in Azure
|
||||
and registering your instance with it.
|
||||
|
||||
Create the application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/azure-key-vault/oauth2/callback`.
|
||||
|
||||

|
||||
|
||||
## Add your application credentials to Infisical
|
||||
|
||||
Obtain the **Application (Client) ID** in Overview and generate a **Client Secret** in Certificate & secrets for your Azure application.
|
||||
<Steps>
|
||||
<Step title="Create an application in Azure">
|
||||
Navigate to Azure Active Directory > App registrations to create a new application.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your Azure application.
|
||||
Create the application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/azure-key-vault/oauth2/callback`.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Add your application credentials to Infisical">
|
||||
Obtain the **Application (Client) ID** in Overview and generate a **Client Secret** in Certificate & secrets for your Azure application.
|
||||
|
||||
- `CLIENT_ID_AZURE`: The **Application (Client) ID** of your Azure application.
|
||||
- `CLIENT_SECRET_AZURE`: The **Client Secret** of your Azure application.
|
||||
|
||||
Once added, restart your Infisical instance and use the Azure KV integration.
|
||||

|
||||

|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your Azure application.
|
||||
|
||||
|
||||
- `CLIENT_ID_AZURE`: The **Application (Client) ID** of your Azure application.
|
||||
- `CLIENT_SECRET_AZURE`: The **Client Secret** of your Azure application.
|
||||
|
||||
Once added, restart your Infisical instance and use the Azure KV integration.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
@ -7,44 +7,45 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Checkly">
|
||||
Obtain a Checkly API Key in User Settings > API Keys.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Enter your Checkly API Key
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
Obtain a Checkly API Key in User Settings > API Keys.
|
||||

|
||||
|
||||

|
||||

|
||||
Press on the Checkly tile and input your Checkly API Key to grant Infisical access to your Checkly account.
|
||||
|
||||
Press on the Checkly tile and input your Checkly API Key to grant Infisical access to your Checkly account.
|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to Checkly and press create integration to start syncing secrets.
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||

|
||||
|
||||
## Start integration
|
||||
<Note>
|
||||
Infisical integrates with Checkly's environment variables at the **global** and **group** levels.
|
||||
|
||||
To sync secrets to a specific group, you can select a group from the Checkly Group dropdown; otherwise, leaving it empty will sync secrets globally.
|
||||
</Note>
|
||||
|
||||
Select which Infisical environment secrets you want to sync to Checkly and press create integration to start syncing secrets.
|
||||

|
||||
|
||||

|
||||
|
||||
<Note>
|
||||
Infisical integrates with Checkly's environment variables at the **global** and **group** levels.
|
||||
|
||||
To sync secrets to a specific group, you can select a group from the Checkly Group dropdown; otherwise, leaving it empty will sync secrets globally.
|
||||
</Note>
|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
In the new version of the Checkly integration, you are able to specify suffixes that depend on the secrets' environment and path.
|
||||
If you choose to do so, you should utilize such suffixes for ALL Checkly integrations – otherwise the integration system
|
||||
might run into issues with deleting secrets from the wrong environments.
|
||||
</Info>
|
||||
<Info>
|
||||
In the new version of the Checkly integration, you are able to specify suffixes that depend on the secrets' environment and path.
|
||||
If you choose to do so, you should utilize such suffixes for ALL Checkly integrations – otherwise the integration system
|
||||
might run into issues with deleting secrets from the wrong environments.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
@ -7,38 +7,39 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Cloudflare Pages">
|
||||
Obtain a Cloudflare [API token](https://dash.cloudflare.com/profile/api-tokens) and [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/):
|
||||
|
||||

|
||||
Create a new [API token](https://dash.cloudflare.com/profile/api-tokens) in My Profile > API Tokens
|
||||
|
||||
## Authorize Infisical for Cloudflare Pages
|
||||

|
||||

|
||||

|
||||
|
||||
Obtain a Cloudflare [API token](https://dash.cloudflare.com/profile/api-tokens) and [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/):
|
||||
Copy your [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/) from Account > Workers & Pages > Overview
|
||||
|
||||
1. Create a new [API token](https://dash.cloudflare.com/profile/api-tokens) in My Profile > API Tokens
|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
2. Copy your [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/) from Account > Workers & Pages > Overview
|
||||
Press on the Cloudflare Pages tile and input your Cloudflare API token and account ID to grant Infisical access to your Cloudflare Pages.
|
||||
|
||||

|
||||

|
||||
|
||||
Press on the Cloudflare Pages tile and input your Cloudflare API token and account ID to grant Infisical access to your Cloudflare Pages.
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to Cloudflare and press create integration to start syncing secrets.
|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to Cloudflare and press create integration to start syncing secrets.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -7,37 +7,38 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Cloudflare Workers">
|
||||
Obtain a Cloudflare [API token](https://dash.cloudflare.com/profile/api-tokens) and [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/):
|
||||
|
||||

|
||||
Create a new [API token](https://dash.cloudflare.com/profile/api-tokens) in My Profile > API Tokens
|
||||
|
||||
## Authorize Infisical for Cloudflare Workers
|
||||

|
||||

|
||||

|
||||
|
||||
Obtain a Cloudflare [API token](https://dash.cloudflare.com/profile/api-tokens) and [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/):
|
||||
Copy your [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/) from Account > Workers & Pages > Overview
|
||||
|
||||
1. Create a new [API token](https://dash.cloudflare.com/profile/api-tokens) in My Profile > API Tokens
|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
2. Copy your [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/) from Account > Workers & Pages > Overview
|
||||

|
||||
|
||||

|
||||
Press on the Cloudflare Workers tile and input your Cloudflare API token and account ID to grant Infisical access to your Cloudflare Workers.
|
||||
|
||||
Press on the Cloudflare Workers tile and input your Cloudflare API token and account ID to grant Infisical access to your Cloudflare Workers.
|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to Cloudflare Workers and press create integration to start syncing secrets.
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to Cloudflare Workers and press create integration to start syncing secrets.
|
||||
|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -7,31 +7,32 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Fly.io">
|
||||
Obtain a Fly.io access token in Access Tokens
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Enter your Fly.io Access Token
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
Obtain a Fly.io access token in Access Tokens
|
||||

|
||||
|
||||

|
||||

|
||||
Press on the Fly.io tile and input your Fly.io access token to grant Infisical access to your Fly.io account.
|
||||
|
||||
Press on the Fly.io tile and input your Fly.io access token to grant Infisical access to your Fly.io account.
|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Fly.io app and press create integration to start syncing secrets to Fly.io.
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Fly.io app and press create integration to start syncing secrets to Fly.io.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -5,148 +5,145 @@ description: "How to sync secrets from Infisical to GCP Secret Manager"
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<AccordionGroup>
|
||||
<Accordion title="Connect with OAuth2">
|
||||
Prerequisites:
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Connect with OAuth2">
|
||||
|
||||
Prerequisites:
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for GCP">
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
## Navigate to your project's integrations tab
|
||||

|
||||
|
||||

|
||||
Press on the GCP Secret Manager tile and select **Continue with OAuth**
|
||||
|
||||
## Authorize Infisical for GCP
|
||||

|
||||
|
||||
Press on the GCP Secret Manager tile and select **Continue with OAuth**
|
||||
Grant Infisical access to GCP.
|
||||
|
||||

|
||||

|
||||
|
||||
Grant Infisical access to GCP.
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
In the **Connection** tab, select which Infisical environment secrets you want to sync to which GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
||||
|
||||

|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
Note that the GCP Secret Manager integration supports a few options in the **Options** tab:
|
||||
|
||||
## Start integration
|
||||
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||
- Label in GCP Secret Manager: If selected, every secret will be labeled in GCP Secret Manager (e.g. as `managed-by:infisical`); labels can be customized.
|
||||
|
||||
In the **Connection** tab, select which Infisical environment secrets you want to sync to which GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
||||
Setting a secret prefix, suffix, or enabling the labeling option ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GCP Secret Manager without the specified prefix, suffix, or attached label.
|
||||
|
||||

|
||||

|
||||
|
||||
Note that the GCP Secret Manager integration supports a few options in the **Options** tab:
|
||||

|
||||
|
||||
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||
- Label in GCP Secret Manager: If selected, every secret will be labeled in GCP Secret Manager (e.g. as `managed-by:infisical`); labels can be customized.
|
||||
<Warning>
|
||||
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
||||
the Service Usage API and Cloud Resource Manager API in the Google Cloud project you want to sync secrets to. More on that [here](https://cloud.google.com/service-usage/docs/set-up-development-environment).
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
</Accordion>
|
||||
<Accordion title="Connect with Service Account JSON">
|
||||
Prerequisites:
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Have a GCP project and have/create a [service account](https://cloud.google.com/iam/docs/service-account-overview) in it
|
||||
|
||||
Setting a secret prefix, suffix, or enabling the labeling option ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GCP Secret Manager without the specified prefix, suffix, or attached label.
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for GCP">
|
||||
Navigate to **IAM & Admin** page in GCP and add the **Secret Manager Admin** and **Service Usage Admin** roles to the service account.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
<Info>
|
||||
For enhanced security, you may want to assign more granular permissions to the service account. At minimum,
|
||||
the service account should be able to read/write secrets from/to GCP Secret Manager (e.g. **Secret Manager Admin** role)
|
||||
and list which GCP services are enabled/disabled (e.g. **Service Usage Admin** role).
|
||||
</Info>
|
||||
|
||||
<Warning>
|
||||
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
||||
the Service Usage API and Cloud Resource Manager API in the Google Cloud project you want to sync secrets to. More on that [here](https://cloud.google.com/service-usage/docs/set-up-development-environment).
|
||||
</Warning>
|
||||
</Accordion>
|
||||
<Accordion title="Connect with Service Account JSON">
|
||||
Prerequisites:
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Have a GCP project and have/create a [service account](https://cloud.google.com/iam/docs/service-account-overview) in it
|
||||

|
||||
|
||||
## Grant the service account permissions for GCP Secret Manager
|
||||
Press on the GCP Secret Manager tile and paste in your **GCP Service Account JSON** (you can create and download the JSON for your
|
||||
service account in IAM & Admin > Service Accounts > Service Account > Keys).
|
||||
|
||||
Navigate to **IAM & Admin** page in GCP and add the **Secret Manager Admin** and **Service Usage Admin** roles to the service account.
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
<Info>
|
||||
For enhanced security, you may want to assign more granular permissions to the service account. At minimum,
|
||||
the service account should be able to read/write secrets from/to GCP Secret Manager (e.g. **Secret Manager Admin** role)
|
||||
and list which GCP services are enabled/disabled (e.g. **Service Usage Admin** role).
|
||||
</Info>
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
In the **Connection** tab, select which Infisical environment secrets you want to sync to the GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||

|
||||
|
||||

|
||||
Note that the GCP Secret Manager integration supports a few options in the **Options** tab:
|
||||
|
||||
## Authorize Infisical for GCP
|
||||
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||
- Label in GCP Secret Manager: If selected, every secret will be labeled in GCP Secret Manager (e.g. as `managed-by:infisical`); labels can be customized.
|
||||
|
||||
Press on the GCP Secret Manager tile and paste in your **GCP Service Account JSON** (you can create and download the JSON for your
|
||||
service account in IAM & Admin > Service Accounts > Service Account > Keys).
|
||||
Setting a secret prefix, suffix, or enabling the labeling option ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GCP Secret Manager without the specified prefix, suffix, or attached label.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
In the **Connection** tab, select which Infisical environment secrets you want to sync to the GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
||||
|
||||

|
||||
|
||||
Note that the GCP Secret Manager integration supports a few options in the **Options** tab:
|
||||
|
||||
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||
- Label in GCP Secret Manager: If selected, every secret will be labeled in GCP Secret Manager (e.g. as `managed-by:infisical`); labels can be customized.
|
||||
|
||||
Setting a secret prefix, suffix, or enabling the labeling option ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GCP Secret Manager without the specified prefix, suffix, or attached label.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
<Warning>
|
||||
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
||||
the Service Usage API and Cloud Resource Manager API in the Google Cloud project you want to sync secrets to. More on that [here](https://cloud.google.com/service-usage/docs/set-up-development-environment).
|
||||
</Warning>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
<Warning>
|
||||
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
||||
the Service Usage API and Cloud Resource Manager API in the Google Cloud project you want to sync secrets to. More on that [here](https://cloud.google.com/service-usage/docs/set-up-development-environment).
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</Tab>
|
||||
<Tab title="Self-Hosted Setup">
|
||||
Using the GCP Secret Manager integration (via the OAuth2 method) on a self-hosted instance of Infisical requires configuring an OAuth2 application in GCP
|
||||
and registering your instance with it.
|
||||
|
||||
## Create an OAuth2 application in GCP
|
||||
|
||||
Navigate to your project API & Services > Credentials to create a new OAuth2 application.
|
||||
|
||||

|
||||

|
||||
|
||||
Create the application. As part of the form, add to **Authorized redirect URIs**: `https://your-domain.com/integrations/gcp-secret-manager/oauth2/callback`.
|
||||
|
||||

|
||||
|
||||
## Add your OAuth2 application credentials to Infisical
|
||||
|
||||
Obtain the **Client ID** and **Client Secret** for your GCP OAuth2 application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your GCP OAuth2 application:
|
||||
Using the GCP Secret Manager integration (via the OAuth2 method) on a self-hosted instance of Infisical requires configuring an OAuth2 application in GCP
|
||||
and registering your instance with it.
|
||||
|
||||
- `CLIENT_ID_GCP_SECRET_MANAGER`: The **Client ID** of your GCP OAuth2 application.
|
||||
- `CLIENT_SECRET_GCP_SECRET_MANAGER`: The **Client Secret** of your GCP OAuth2 application.
|
||||
|
||||
Once added, restart your Infisical instance and use the GCP Secret Manager integration.
|
||||
<Steps>
|
||||
<Step title="Create an OAuth2 application in GCP">
|
||||
Navigate to your project API & Services > Credentials to create a new OAuth2 application.
|
||||
|
||||

|
||||

|
||||
|
||||
Create the application. As part of the form, add to **Authorized redirect URIs**: `https://your-domain.com/integrations/gcp-secret-manager/oauth2/callback`.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Add your OAuth2 application credentials to Infisical">
|
||||
Obtain the **Client ID** and **Client Secret** for your GCP OAuth2 application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your GCP OAuth2 application:
|
||||
|
||||
- `CLIENT_ID_GCP_SECRET_MANAGER`: The **Client ID** of your GCP OAuth2 application.
|
||||
- `CLIENT_SECRET_GCP_SECRET_MANAGER`: The **Client Secret** of your GCP OAuth2 application.
|
||||
|
||||
Once added, restart your Infisical instance and use the GCP Secret Manager integration.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
@ -7,30 +7,31 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Hasura Cloud">
|
||||
Obtain a Hasura Cloud Access Token in My Account > Access Tokens
|
||||
|
||||

|
||||

|
||||
|
||||
## Enter your Hasura Cloud Access Token
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
Obtain a Hasura Cloud Access Token in My Account > Access Tokens
|
||||

|
||||
|
||||
Press on the Hasura Cloud tile and input your Hasura Cloud access token to grant Infisical access to your Hasura Cloud account.
|
||||
|
||||

|
||||

|
||||
|
||||
Press on the Hasura Cloud tile and input your Hasura Cloud access token to grant Infisical access to your Hasura Cloud account.
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Hasura Cloud project and press create integration to start syncing secrets to Hasura Cloud.
|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Hasura Cloud project and press create integration to start syncing secrets to Hasura Cloud.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
@ -5,63 +5,63 @@ description: "How to sync secrets from Infisical to Heroku"
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
Prerequisites:
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Heroku">
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for Heroku
|
||||
Press on the Heroku tile and grant Infisical access to your Heroku account.
|
||||
|
||||
Press on the Heroku tile and grant Infisical access to your Heroku account.
|
||||

|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Heroku app and press create integration to start syncing secrets to Heroku.
|
||||
|
||||

|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Heroku app and press create integration to start syncing secrets to Heroku.
|
||||
|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="Self-Hosted Setup">
|
||||
Using the Heroku integration on a self-hosted instance of Infisical requires configuring an API client in Heroku
|
||||
and registering your instance with it.
|
||||
|
||||
## Create an API client in Heroku
|
||||
|
||||
Navigate to your user Account settings > Applications to create a new API client.
|
||||
Using the Heroku integration on a self-hosted instance of Infisical requires configuring an API client in Heroku
|
||||
and registering your instance with it.
|
||||
<Steps>
|
||||
<Step title="Create an API client in Heroku">
|
||||
Navigate to your user Account settings > Applications to create a new API client.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Create the API client. As part of the form, set the **OAuth callback URL** to `https://your-domain.com/integrations/heroku/oauth2/callback`.
|
||||

|
||||

|
||||

|
||||
|
||||
Create the API client. As part of the form, set the **OAuth callback URL** to `https://your-domain.com/integrations/heroku/oauth2/callback`.
|
||||
|
||||

|
||||
|
||||
## Add your Heroku API client credentials to Infisical
|
||||
|
||||
Obtain the **Client ID** and **Client Secret** for your Heroku API client.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your Heroku API client.
|
||||
|
||||
- `CLIENT_ID_HEROKU`: The **Client ID** of your Heroku API client.
|
||||
- `CLIENT_SECRET_HEROKU`: The **Client Secret** of your Heroku API client.
|
||||
|
||||
Once added, restart your Infisical instance and use the Heroku integration.
|
||||

|
||||
</Step>
|
||||
<Step title="Add your Heroku API client credentials to Infisical">
|
||||
Obtain the **Client ID** and **Client Secret** for your Heroku API client.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your Heroku API client.
|
||||
|
||||
- `CLIENT_ID_HEROKU`: The **Client ID** of your Heroku API client.
|
||||
- `CLIENT_SECRET_HEROKU`: The **Client Secret** of your Heroku API client.
|
||||
|
||||
Once added, restart your Infisical instance and use the Heroku integration.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
@ -7,36 +7,38 @@ Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Laravel Forge">
|
||||
Obtain a Laravel Forge access token in API Tokens
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Enter your Laravel Forge Access Token and Server Id
|
||||
Obtain your Laravel Forge Server ID in Servers > Server ID
|
||||
|
||||
Obtain a Laravel Forge access token in API Tokens
|
||||

|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Obtain your Laravel Forge Server ID in Servers > Server ID
|
||||
Press on the Laravel Forge tile and input your Laravel Forge access token and server ID to grant Infisical access to your Laravel Forge account.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Press on the Laravel Forge tile and input your Laravel Forge access token and server ID to grant Infisical access to your Laravel Forge account.
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Laravel Forge site and press create integration to start syncing secrets to Laravel Forge.
|
||||
|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Laravel Forge site and press create integration to start syncing secrets to Laravel Forge.
|
||||
|
||||

|
||||

|
||||
|
@ -5,68 +5,68 @@ description: "How to sync secrets from Infisical to Netlify"
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Warning>
|
||||
Infisical integrates with Netlify's new environment variable experience. If
|
||||
your site uses Netlify's old environment variable experience, you'll have to
|
||||
upgrade it to the new one to use this integration.
|
||||
</Warning>
|
||||
<Warning>
|
||||
Infisical integrates with Netlify's new environment variable experience. If
|
||||
your site uses Netlify's old environment variable experience, you'll have to
|
||||
upgrade it to the new one to use this integration.
|
||||
</Warning>
|
||||
|
||||
Prerequisites:
|
||||
|
||||
Prerequisites:
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Netlify">
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
Press on the Netlify tile and grant Infisical access to your Netlify account.
|
||||
|
||||

|
||||

|
||||
|
||||
## Authorize Infisical for Netlify
|
||||
|
||||
Press on the Netlify tile and grant Infisical access to your Netlify account.
|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Netlify app and context. Lastly, press create integration to start syncing secrets to Netlify.
|
||||
|
||||

|
||||

|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Netlify app and context. Lastly, press create integration to start syncing secrets to Netlify.
|
||||
|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="Self-Hosted Setup">
|
||||
Using the Netlify integration on a self-hosted instance of Infisical requires configuring an OAuth application in Netlify
|
||||
and registering your instance with it.
|
||||
|
||||
## Create an OAuth application in Netlify
|
||||
|
||||
Navigate to your User settings > Applications > OAuth to create a new OAuth application.
|
||||
|
||||

|
||||

|
||||
|
||||
Create the OAuth application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/netlify/oauth2/callback`.
|
||||
<Steps>
|
||||
<Step title="Create an OAuth application in Netlify">
|
||||
Navigate to your User settings > Applications > OAuth to create a new OAuth application.
|
||||
|
||||

|
||||

|
||||
|
||||
Create the OAuth application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/netlify/oauth2/callback`.
|
||||
|
||||

|
||||

|
||||
</Step>
|
||||
<Step title="Add your Netlify OAuth application credentials to Infisical">
|
||||
Obtain the **Client ID** and **Secret** for your Netlify OAuth application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your Netlify OAuth application.
|
||||
|
||||
## Add your Netlify OAuth application credentials to Infisical
|
||||
|
||||
Obtain the **Client ID** and **Secret** for your Netlify OAuth application.
|
||||
|
||||

|
||||
|
||||
Back in your Infisical instance, add two new environment variables for the credentials of your Netlify OAuth application.
|
||||
|
||||
- `CLIENT_ID_NETLIFY`: The **Client ID** of your Netlify OAuth application.
|
||||
- `CLIENT_SECRET_NETLIFY`: The **Secret** of your Netlify OAuth application.
|
||||
|
||||
Once added, restart your Infisical instance and use the Netlify integration.
|
||||
- `CLIENT_ID_NETLIFY`: The **Client ID** of your Netlify OAuth application.
|
||||
- `CLIENT_SECRET_NETLIFY`: The **Secret** of your Netlify OAuth application.
|
||||
|
||||
Once added, restart your Infisical instance and use the Netlify integration.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
@ -8,31 +8,32 @@ Prerequisites:
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Have a [Northflank](https://northflank.com) project with a secret group ready
|
||||
|
||||
## Navigate to your project's integrations tab
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Northflank">
|
||||
Obtain a Northflank API token in Account settings > API > Tokens
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||
## Enter your Northflank API Token
|
||||

|
||||
|
||||
Obtain a Northflank API token in Account settings > API > Tokens
|
||||
Press on the Northflank tile and input your Northflank API token to grant Infisical access to your Northflank account.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Press on the Northflank tile and input your Northflank API token to grant Infisical access to your Northflank account.
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which Northflank project and secret group. Finally, press create integration to start syncing secrets to Northflank.
|
||||
|
||||

|
||||
|
||||
<Info>
|
||||
If this is your project's first cloud integration, then you'll have to grant
|
||||
Infisical access to your project's environment variables. Although this step
|
||||
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||
the cloud platform.
|
||||
</Info>
|
||||
|
||||
## Start integration
|
||||
|
||||
Select which Infisical environment secrets you want to sync to which Northflank project and secret group. Finally, press create integration to start syncing secrets to Northflank.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
</Steps>
|