Skip to content

Commit 70bbabb

Browse files
committed
Validate AWS connection credentials at creation time
1 parent 9f554c8 commit 70bbabb

3 files changed

Lines changed: 537 additions & 14 deletions

File tree

packages/openops/src/lib/aws/auth.ts

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { BlockAuth, Property } from '@openops/blocks-framework';
33
import { SharedSystemProp, system } from '@openops/server-shared';
44
import { parseArn } from './arn-handler';
5-
import { assumeRole } from './sts-common';
5+
import { assumeRole, getAccountId } from './sts-common';
66

77
const isImplicitRoleEnabled = system.getBoolean(
88
SharedSystemProp.AWS_ENABLE_IMPLICIT_ROLE,
@@ -166,6 +166,77 @@ export function getAwsAccountsSingleSelectDropdown() {
166166
return createAwsAccountsDropdown(false);
167167
}
168168

169+
async function validateRequiredFields(
170+
auth: any,
171+
): Promise<{ valid: true } | { valid: false; error: string }> {
172+
if (!auth.defaultRegion) {
173+
return { valid: false, error: 'Default region is required' };
174+
}
175+
176+
const hasCredentials = auth.accessKeyId && auth.secretAccessKey;
177+
if (!hasCredentials && !isImplicitRoleEnabled) {
178+
return {
179+
valid: false,
180+
error: 'Access Key ID and Secret Access Key are required',
181+
};
182+
}
183+
184+
return { valid: true };
185+
}
186+
187+
async function validateBaseCredentials(
188+
auth: any,
189+
): Promise<{ valid: true } | { valid: false; error: string }> {
190+
try {
191+
const credentials = {
192+
accessKeyId: auth.accessKeyId || '',
193+
secretAccessKey: auth.secretAccessKey || '',
194+
endpoint: auth.endpoint,
195+
};
196+
await getAccountId(credentials, auth.defaultRegion);
197+
return { valid: true };
198+
} catch (error) {
199+
const errorMessage =
200+
error instanceof Error ? error.message : 'Unknown error';
201+
return {
202+
valid: false,
203+
error: `Base credentials validation failed: ${errorMessage}`,
204+
};
205+
}
206+
}
207+
208+
async function validateRoleAssumptions(
209+
auth: any,
210+
): Promise<{ valid: true } | { valid: false; error: string }> {
211+
if (!auth.roles || auth.roles.length === 0) {
212+
return { valid: true };
213+
}
214+
215+
const accessKeyId = auth.accessKeyId || '';
216+
const secretAccessKey = auth.secretAccessKey || '';
217+
218+
for (const role of auth.roles as Role[]) {
219+
try {
220+
await assumeRole(
221+
accessKeyId,
222+
secretAccessKey,
223+
auth.defaultRegion,
224+
role.assumeRoleArn,
225+
role.assumeRoleExternalId,
226+
);
227+
} catch (error) {
228+
const errorMessage =
229+
error instanceof Error ? error.message : 'Unknown error';
230+
return {
231+
valid: false,
232+
error: `Role validation failed for ARN "${role.assumeRoleArn}" (${role.accountName}): ${errorMessage}`,
233+
};
234+
}
235+
}
236+
237+
return { valid: true };
238+
}
239+
169240
export const amazonAuth = BlockAuth.CustomAuth({
170241
authProviderKey: 'AWS',
171242
authProviderDisplayName: 'AWS',
@@ -229,16 +300,19 @@ For large or complex setups, enhanced features are available, including:
229300
},
230301
required: true,
231302
validate: async ({ auth }) => {
232-
if (!auth.defaultRegion) {
233-
return { valid: false, error: 'Default region is required' };
303+
const fieldValidation = await validateRequiredFields(auth);
304+
if (!fieldValidation.valid) {
305+
return fieldValidation;
234306
}
235307

236-
if (!auth.accessKeyId && !isImplicitRoleEnabled) {
237-
return { valid: false, error: 'Access Key ID is required' };
308+
const baseCredentialsValidation = await validateBaseCredentials(auth);
309+
if (!baseCredentialsValidation.valid) {
310+
return baseCredentialsValidation;
238311
}
239312

240-
if (!auth.secretAccessKey && !isImplicitRoleEnabled) {
241-
return { valid: false, error: 'Secret Access Key is required' };
313+
const roleValidation = await validateRoleAssumptions(auth);
314+
if (!roleValidation.valid) {
315+
return roleValidation;
242316
}
243317

244318
return { valid: true };

0 commit comments

Comments
 (0)