mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: enhance PAM account handling with type safety and improved response structure
- Introduced type inference for sanitized accounts to ensure consistent data handling. - Updated account response structure to explicitly cast accounts to the sanitized type. - Refined the decryption function to omit sensitive fields from the returned account object. - Improved error handling in SQL resource factory by enforcing required gateway ID validation.
This commit is contained in:
@@ -23,6 +23,8 @@ const SanitizedAccountSchema = z.union([
|
||||
SanitizedAwsIamAccountWithResourceSchema
|
||||
]);
|
||||
|
||||
type TSanitizedAccount = z.infer<typeof SanitizedAccountSchema>;
|
||||
|
||||
export const registerPamAccountRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
@@ -95,7 +97,7 @@ export const registerPamAccountRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
});
|
||||
|
||||
return { accounts, folders, totalCount, folderId, folderPaths };
|
||||
return { accounts: accounts as TSanitizedAccount[], folders, totalCount, folderId, folderPaths };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -72,17 +72,24 @@ export const decryptAccount = async <
|
||||
account: T,
|
||||
projectId: string,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
): Promise<T & { credentials: TPamAccountCredentials; lastRotationMessage: string | null }> => {
|
||||
): Promise<
|
||||
Omit<T, "encryptedCredentials" | "encryptedLastRotationMessage"> & {
|
||||
credentials: TPamAccountCredentials;
|
||||
lastRotationMessage: string | null;
|
||||
}
|
||||
> => {
|
||||
const { encryptedCredentials, encryptedLastRotationMessage, ...rest } = account;
|
||||
|
||||
return {
|
||||
...account,
|
||||
...rest,
|
||||
credentials: await decryptAccountCredentials({
|
||||
encryptedCredentials: account.encryptedCredentials,
|
||||
encryptedCredentials,
|
||||
projectId,
|
||||
kmsService
|
||||
}),
|
||||
lastRotationMessage: account.encryptedLastRotationMessage
|
||||
lastRotationMessage: encryptedLastRotationMessage
|
||||
? await decryptAccountMessage({
|
||||
encryptedMessage: account.encryptedLastRotationMessage,
|
||||
encryptedMessage: encryptedLastRotationMessage,
|
||||
projectId,
|
||||
kmsService
|
||||
})
|
||||
|
||||
@@ -439,7 +439,7 @@ export const pamAccountServiceFactory = ({
|
||||
const totalCount = totalFolderCount + totalAccountCount;
|
||||
|
||||
const decryptedAndPermittedAccounts: Array<
|
||||
TPamAccounts & {
|
||||
Omit<TPamAccounts, "encryptedCredentials" | "encryptedLastRotationMessage"> & {
|
||||
resource: Pick<TPamResources, "id" | "name" | "resourceType"> & { rotationCredentialsConfigured: boolean };
|
||||
credentials: TPamAccountCredentials;
|
||||
lastRotationMessage: string | null;
|
||||
|
||||
@@ -170,12 +170,6 @@ export const generateConsoleFederationUrl = async ({
|
||||
|
||||
const federationEndpoint = "https://signin.aws.amazon.com/federation";
|
||||
|
||||
// Console destination can be regional
|
||||
const getConsoleHost = () =>
|
||||
connectionDetails.region === "us-east-1"
|
||||
? "console.aws.amazon.com"
|
||||
: `${connectionDetails.region}.console.aws.amazon.com`;
|
||||
|
||||
const signinTokenUrl = `${federationEndpoint}?Action=getSigninToken&Session=${encodeURIComponent(sessionJson)}`;
|
||||
|
||||
const tokenResponse = await fetch(signinTokenUrl);
|
||||
@@ -199,7 +193,7 @@ export const generateConsoleFederationUrl = async ({
|
||||
throw new Error(`AWS federation endpoint did not return a SigninToken: ${responseText.substring(0, 200)}`);
|
||||
}
|
||||
|
||||
const consoleDestination = `https://${getConsoleHost()}/`;
|
||||
const consoleDestination = `https://console.aws.amazon.com/`;
|
||||
const consoleUrl = `${federationEndpoint}?Action=login&SigninToken=${encodeURIComponent(tokenData.SigninToken)}&Destination=${encodeURIComponent(consoleDestination)}`;
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import RE2 from "re2";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { AwsIamResourceListItemSchema } from "./aws-iam-resource-schemas";
|
||||
@@ -14,7 +16,7 @@ export const getAwsIamResourceListItem = () => {
|
||||
* ARN format: arn:aws:iam::123456789012:role/RoleName
|
||||
*/
|
||||
export const extractAwsAccountIdFromArn = (roleArn: string): string => {
|
||||
const match = roleArn.match(/^arn:aws:iam::(\d{12}):role\//);
|
||||
const match = roleArn.match(new RE2("^arn:aws:iam::(\\d{12}):role/"));
|
||||
if (!match) {
|
||||
throw new BadRequestError({ message: "Invalid IAM Role ARN format" });
|
||||
}
|
||||
|
||||
@@ -67,9 +67,7 @@ export const AwsIamAccountSchema = BasePamAccountSchema.extend({
|
||||
});
|
||||
|
||||
export const CreateAwsIamAccountSchema = BaseCreatePamAccountSchema.extend({
|
||||
credentials: AwsIamAccountCredentialsSchema,
|
||||
// AWS IAM doesn't support credential rotation - credentials are generated on-the-fly via STS
|
||||
rotationEnabled: z.boolean().optional().default(false)
|
||||
credentials: AwsIamAccountCredentialsSchema
|
||||
});
|
||||
|
||||
export const UpdateAwsIamAccountSchema = BaseUpdatePamAccountSchema.extend({
|
||||
|
||||
@@ -233,6 +233,10 @@ export const sqlResourceFactory: TPamResourceFactory<TSqlResourceConnectionDetai
|
||||
gatewayV2Service
|
||||
) => {
|
||||
const validateConnection = async () => {
|
||||
if (!gatewayId) {
|
||||
throw new BadRequestError({ message: "Gateway ID is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
await executeWithGateway({ connectionDetails, gatewayId, resourceType }, gatewayV2Service, async (client) => {
|
||||
await client.validate(true);
|
||||
@@ -255,6 +259,10 @@ export const sqlResourceFactory: TPamResourceFactory<TSqlResourceConnectionDetai
|
||||
credentials
|
||||
) => {
|
||||
try {
|
||||
if (!gatewayId) {
|
||||
throw new BadRequestError({ message: "Gateway ID is required" });
|
||||
}
|
||||
|
||||
await executeWithGateway(
|
||||
{
|
||||
connectionDetails,
|
||||
@@ -296,6 +304,10 @@ export const sqlResourceFactory: TPamResourceFactory<TSqlResourceConnectionDetai
|
||||
currentCredentials
|
||||
) => {
|
||||
const newPassword = alphaNumericNanoId(32);
|
||||
if (!gatewayId) {
|
||||
throw new BadRequestError({ message: "Gateway ID is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
return await executeWithGateway(
|
||||
{
|
||||
|
||||
@@ -73,7 +73,11 @@ const CreateForm = ({
|
||||
);
|
||||
case PamResourceType.AwsIam:
|
||||
return (
|
||||
<AwsIamAccountForm onSubmit={onSubmit} resourceId={resourceId} resourceType={resourceType} />
|
||||
<AwsIamAccountForm
|
||||
onSubmit={onSubmit}
|
||||
resourceId={resourceId}
|
||||
resourceType={resourceType}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
throw new Error(`Unhandled resource: ${resourceType}`);
|
||||
|
||||
@@ -103,7 +103,7 @@ export const PamAccountRow = ({
|
||||
</span>
|
||||
</Badge>
|
||||
)}
|
||||
{account.lastRotatedAt && (
|
||||
{"lastRotatedAt" in account && account.lastRotatedAt && (
|
||||
<Tooltip
|
||||
className="max-w-sm text-center"
|
||||
isDisabled={!account.lastRotationMessage}
|
||||
|
||||
Reference in New Issue
Block a user