mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 22:46:24 +00:00
Merge remote-tracking branch 'origin/main' into feat/add-key-usages-for-template-and-cert
This commit is contained in:
@@ -12,7 +12,6 @@ import { BadRequestError, DatabaseError, UnauthorizedError } from "@app/lib/erro
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
import { TTokenDALFactory } from "../auth-token/auth-token-dal";
|
||||
import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
@@ -34,7 +33,6 @@ type TAuthLoginServiceFactoryDep = {
|
||||
orgDAL: TOrgDALFactory;
|
||||
tokenService: TAuthTokenServiceFactory;
|
||||
smtpService: TSmtpService;
|
||||
tokenDAL: TTokenDALFactory;
|
||||
};
|
||||
|
||||
export type TAuthLoginFactory = ReturnType<typeof authLoginServiceFactory>;
|
||||
@@ -42,8 +40,7 @@ export const authLoginServiceFactory = ({
|
||||
userDAL,
|
||||
tokenService,
|
||||
smtpService,
|
||||
orgDAL,
|
||||
tokenDAL
|
||||
orgDAL
|
||||
}: TAuthLoginServiceFactoryDep) => {
|
||||
/*
|
||||
* Private
|
||||
@@ -376,8 +373,6 @@ export const authLoginServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
await tokenDAL.incrementTokenSessionVersion(user.id, decodedToken.tokenVersionId);
|
||||
|
||||
const tokens = await generateUserTokens({
|
||||
authMethod: decodedToken.authMethod,
|
||||
user,
|
||||
|
||||
@@ -7,7 +7,7 @@ const isValidDate = (dateString: string) => {
|
||||
|
||||
export const validateCaDateField = z.string().trim().refine(isValidDate, { message: "Invalid date format" });
|
||||
|
||||
export const hostnameRegex = /^(?!:\/\/)([a-zA-Z0-9-_]{1,63}\.?)+(?!:\/\/)([a-zA-Z]{2,63})$/;
|
||||
export const hostnameRegex = /^(?!:\/\/)(\*\.)?([a-zA-Z0-9-_]{1,63}\.?)+(?!:\/\/)([a-zA-Z]{2,63})$/;
|
||||
export const validateAltNamesField = z
|
||||
.string()
|
||||
.trim()
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import picomatch from "picomatch";
|
||||
|
||||
export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) =>
|
||||
policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
|
||||
@@ -28,6 +28,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TOrgBotDALFactory } from "../org/org-bot-dal";
|
||||
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
|
||||
import { doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
|
||||
import {
|
||||
TAttachOidcAuthDTO,
|
||||
TGetOidcAuthDTO,
|
||||
@@ -123,7 +124,7 @@ export const identityOidcAuthServiceFactory = ({
|
||||
}) as Record<string, string>;
|
||||
|
||||
if (identityOidcAuth.boundSubject) {
|
||||
if (tokenData.sub !== identityOidcAuth.boundSubject) {
|
||||
if (!doesFieldValueMatchOidcPolicy(tokenData.sub, identityOidcAuth.boundSubject)) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Access denied: OIDC subject not allowed."
|
||||
});
|
||||
@@ -131,7 +132,11 @@ export const identityOidcAuthServiceFactory = ({
|
||||
}
|
||||
|
||||
if (identityOidcAuth.boundAudiences) {
|
||||
if (!identityOidcAuth.boundAudiences.split(", ").includes(tokenData.aud)) {
|
||||
if (
|
||||
!identityOidcAuth.boundAudiences
|
||||
.split(", ")
|
||||
.some((policyValue) => doesFieldValueMatchOidcPolicy(tokenData.aud, policyValue))
|
||||
) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Access denied: OIDC audience not allowed."
|
||||
});
|
||||
@@ -142,7 +147,9 @@ export const identityOidcAuthServiceFactory = ({
|
||||
Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => {
|
||||
const claimValue = (identityOidcAuth.boundClaims as Record<string, string>)[claimKey];
|
||||
// handle both single and multi-valued claims
|
||||
if (!claimValue.split(", ").some((claimEntry) => tokenData[claimKey] === claimEntry)) {
|
||||
if (
|
||||
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(tokenData[claimKey], claimEntry))
|
||||
) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Access denied: OIDC claim not allowed."
|
||||
});
|
||||
|
||||
@@ -567,8 +567,8 @@ const syncSecretsAWSParameterStore = async ({
|
||||
});
|
||||
ssm.config.update(config);
|
||||
|
||||
const metadata = z.record(z.any()).parse(integration.metadata || {});
|
||||
const awsParameterStoreSecretsObj: Record<string, AWS.SSM.Parameter> = {};
|
||||
const metadata = IntegrationMetadataSchema.parse(integration.metadata);
|
||||
const awsParameterStoreSecretsObj: Record<string, AWS.SSM.Parameter & { KeyId?: string }> = {};
|
||||
logger.info(
|
||||
`getIntegrationSecrets: integration sync triggered for ssm with [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [shouldDisableDelete=${metadata.shouldDisableDelete}]`
|
||||
);
|
||||
@@ -598,18 +598,57 @@ const syncSecretsAWSParameterStore = async ({
|
||||
nextToken = parameters.NextToken;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`getIntegrationSecrets: all fetched keys from AWS SSM [projectId=${projectId}] [environment=${
|
||||
integration.environment.slug
|
||||
}] [secretPath=${integration.secretPath}] [awsParameterStoreSecretsObj=${Object.keys(
|
||||
awsParameterStoreSecretsObj
|
||||
).join(",")}]`
|
||||
);
|
||||
logger.info(
|
||||
`getIntegrationSecrets: all secrets from Infisical to send to AWS SSM [projectId=${projectId}] [environment=${
|
||||
integration.environment.slug
|
||||
}] [secretPath=${integration.secretPath}] [secrets=${Object.keys(secrets).join(",")}]`
|
||||
);
|
||||
let areParametersKmsKeysFetched = false;
|
||||
|
||||
if (metadata.kmsKeyId) {
|
||||
// we put this inside a try catch so that existing integrations without the ssm:DescribeParameters
|
||||
// AWS permission will not break
|
||||
try {
|
||||
let hasNextDescribePage = true;
|
||||
let describeNextToken: string | undefined;
|
||||
|
||||
while (hasNextDescribePage) {
|
||||
const parameters = await ssm
|
||||
.describeParameters({
|
||||
MaxResults: 10,
|
||||
NextToken: describeNextToken,
|
||||
ParameterFilters: [
|
||||
{
|
||||
Key: "Path",
|
||||
Option: "OneLevel",
|
||||
Values: [integration.path as string]
|
||||
}
|
||||
]
|
||||
})
|
||||
.promise();
|
||||
|
||||
if (parameters.Parameters) {
|
||||
parameters.Parameters.forEach((parameter) => {
|
||||
if (parameter.Name) {
|
||||
const secKey = parameter.Name.substring((integration.path as string).length);
|
||||
awsParameterStoreSecretsObj[secKey].KeyId = parameter.KeyId;
|
||||
}
|
||||
});
|
||||
}
|
||||
areParametersKmsKeysFetched = true;
|
||||
hasNextDescribePage = Boolean(parameters.NextToken);
|
||||
describeNextToken = parameters.NextToken;
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((error as any).code === "AccessDeniedException") {
|
||||
logger.error(
|
||||
`AWS Parameter Store Error [integration=${integration.id}]: double check AWS account permissions (refer to the Infisical docs)`
|
||||
);
|
||||
}
|
||||
|
||||
response = {
|
||||
isSynced: false,
|
||||
syncMessage: (error as AWSError)?.message || "Error syncing with AWS Parameter Store"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Identify secrets to create
|
||||
// don't use Promise.all() and promise map here
|
||||
// it will cause rate limit
|
||||
@@ -620,7 +659,7 @@ const syncSecretsAWSParameterStore = async ({
|
||||
// -> create secret
|
||||
if (secrets[key].value) {
|
||||
logger.info(
|
||||
`getIntegrationSecrets: create secret in AWS SSM for [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [key=${key}]`
|
||||
`getIntegrationSecrets: create secret in AWS SSM for [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}]`
|
||||
);
|
||||
await ssm
|
||||
.putParameter({
|
||||
@@ -648,7 +687,7 @@ const syncSecretsAWSParameterStore = async ({
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
err,
|
||||
`getIntegrationSecrets: create secret in AWS SSM for failed [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [key=${key}]`
|
||||
`getIntegrationSecrets: create secret in AWS SSM for failed [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}]`
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((err as any).code === "AccessDeniedException") {
|
||||
@@ -667,16 +706,23 @@ const syncSecretsAWSParameterStore = async ({
|
||||
// case: secret exists in AWS parameter store
|
||||
} else {
|
||||
logger.info(
|
||||
`getIntegrationSecrets: update secret in AWS SSM for [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [key=${key}]`
|
||||
`getIntegrationSecrets: update secret in AWS SSM for [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}]`
|
||||
);
|
||||
// -> update secret
|
||||
if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) {
|
||||
|
||||
const shouldUpdateKms =
|
||||
areParametersKmsKeysFetched &&
|
||||
Boolean(metadata.kmsKeyId) &&
|
||||
awsParameterStoreSecretsObj[key].KeyId !== metadata.kmsKeyId;
|
||||
|
||||
// we ensure that the KMS key configured in the integration is applied for ALL parameters on AWS
|
||||
if (shouldUpdateKms || awsParameterStoreSecretsObj[key].Value !== secrets[key].value) {
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
Overwrite: true
|
||||
Overwrite: true,
|
||||
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId })
|
||||
})
|
||||
.promise();
|
||||
}
|
||||
@@ -698,7 +744,7 @@ const syncSecretsAWSParameterStore = async ({
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
err,
|
||||
`getIntegrationSecrets: update secret in AWS SSM for failed [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [key=${key}]`
|
||||
`getIntegrationSecrets: update secret in AWS SSM for failed [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}]`
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((err as any).code === "AccessDeniedException") {
|
||||
@@ -728,11 +774,11 @@ const syncSecretsAWSParameterStore = async ({
|
||||
for (const key in awsParameterStoreSecretsObj) {
|
||||
if (Object.hasOwn(awsParameterStoreSecretsObj, key)) {
|
||||
logger.info(
|
||||
`getIntegrationSecrets: inside of shouldDisableDelete AWS SSM [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [key=${key}] [step=2]`
|
||||
`getIntegrationSecrets: inside of shouldDisableDelete AWS SSM [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [step=2]`
|
||||
);
|
||||
if (!(key in secrets)) {
|
||||
logger.info(
|
||||
`getIntegrationSecrets: inside of shouldDisableDelete AWS SSM [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [key=${key}] [step=3]`
|
||||
`getIntegrationSecrets: inside of shouldDisableDelete AWS SSM [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [step=3]`
|
||||
);
|
||||
// case:
|
||||
// -> delete secret
|
||||
@@ -742,7 +788,7 @@ const syncSecretsAWSParameterStore = async ({
|
||||
})
|
||||
.promise();
|
||||
logger.info(
|
||||
`getIntegrationSecrets: inside of shouldDisableDelete AWS SSM [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [key=${key}] [step=4]`
|
||||
`getIntegrationSecrets: inside of shouldDisableDelete AWS SSM [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}] [step=4]`
|
||||
);
|
||||
}
|
||||
await new Promise((resolve) => {
|
||||
|
||||
Reference in New Issue
Block a user