1717 */
1818
1919import { ChangeEvent , useEffect , useMemo , useRef } from 'react' ;
20- import { Input } from 'antd' ;
20+ import { Input , Radio } from 'antd' ;
2121
2222import { Block } from '@/components' ;
2323
@@ -32,7 +32,12 @@ interface Props {
3232const ACCESS_KEY_PATTERN = / ^ [ A - Z 0 - 9 ] { 16 , 32 } $ / ;
3333const REGION_PATTERN = / ^ [ a - z ] { 2 } - [ a - z ] + - \d $ / ;
3434
35- const syncError = ( key : string , error : string , setErrors : ( errors : any ) => void , ref : React . MutableRefObject < string | undefined > ) => {
35+ const syncError = (
36+ key : string ,
37+ error : string ,
38+ setErrors : ( errors : any ) => void ,
39+ ref : React . MutableRefObject < string | undefined > ,
40+ ) => {
3641 if ( ref . current !== error ) {
3742 ref . current = error ;
3843 setErrors ( { [ key ] : error } ) ;
@@ -42,9 +47,18 @@ const syncError = (key: string, error: string, setErrors: (errors: any) => void,
4247export const AwsCredentials = ( { type, initialValues, values, setValues, setErrors } : Props ) => {
4348 const isUpdate = type === 'update' ;
4449
50+ const authType = values . authType ?? 'access_key' ;
4551 const accessKeyId = values . accessKeyId ?? '' ;
4652 const secretAccessKey = values . secretAccessKey ?? '' ;
4753 const region = values . region ?? '' ;
54+
55+ const isAccessKeyAuth = authType === 'access_key' ;
56+
57+ useEffect ( ( ) => {
58+ if ( values . authType === undefined ) {
59+ setValues ( { authType : initialValues . authType ?? 'access_key' } ) ;
60+ }
61+ } , [ initialValues . authType , values . authType , setValues ] ) ;
4862
4963 useEffect ( ( ) => {
5064 if ( values . accessKeyId === undefined ) {
@@ -65,31 +79,33 @@ export const AwsCredentials = ({ type, initialValues, values, setValues, setErro
6579 } , [ initialValues . region , values . region , setValues ] ) ;
6680
6781 const accessKeyError = useMemo ( ( ) => {
82+ if ( ! isAccessKeyAuth ) return '' ; // Not required for IAM role auth
6883 if ( ! accessKeyId ) {
69- return isUpdate ? '' : 'AWS Access Key ID is required. ' ;
84+ return isUpdate ? '' : 'AWS Access Key ID is required' ;
7085 }
7186 if ( ! ACCESS_KEY_PATTERN . test ( accessKeyId ) ) {
72- return 'AWS Access Key ID must contain 16-32 upper case letters or digits. ' ;
87+ return 'AWS Access Key ID must contain 16-32 uppercase letters or digits' ;
7388 }
7489 return '' ;
75- } , [ accessKeyId , isUpdate ] ) ;
90+ } , [ accessKeyId , isUpdate , isAccessKeyAuth ] ) ;
7691
7792 const secretKeyError = useMemo ( ( ) => {
93+ if ( ! isAccessKeyAuth ) return '' ; // Not required for IAM role auth
7894 if ( ! secretAccessKey ) {
79- return isUpdate ? '' : 'AWS Secret Access Key is required. ' ;
95+ return isUpdate ? '' : 'AWS Secret Access Key is required' ;
8096 }
8197 if ( secretAccessKey && secretAccessKey . length < 40 ) {
82- return 'AWS Secret Access Key looks too short. ' ;
98+ return 'AWS Secret Access Key looks too short' ;
8399 }
84100 return '' ;
85- } , [ secretAccessKey , isUpdate ] ) ;
101+ } , [ secretAccessKey , isUpdate , isAccessKeyAuth ] ) ;
86102
87103 const regionError = useMemo ( ( ) => {
88104 if ( ! region ) {
89- return 'AWS Region is required. ' ;
105+ return 'AWS Region is required' ;
90106 }
91107 if ( ! REGION_PATTERN . test ( region ) ) {
92- return 'AWS Region should look like us-east-1. ' ;
108+ return 'AWS Region should look like us-east-1' ;
93109 }
94110 return '' ;
95111 } , [ region ] ) ;
@@ -122,31 +138,67 @@ export const AwsCredentials = ({ type, initialValues, values, setValues, setErro
122138 setValues ( { region : e . target . value . trim ( ) } ) ;
123139 } ;
124140
141+ const handleAuthTypeChange = ( e : any ) => {
142+ const newAuthType = e . target . value ;
143+ setValues ( { authType : newAuthType } ) ;
144+
145+ // Clear access key fields when switching to IAM role
146+ if ( newAuthType === 'iam_role' ) {
147+ setValues ( {
148+ authType : newAuthType ,
149+ accessKeyId : '' ,
150+ secretAccessKey : ''
151+ } ) ;
152+ }
153+ } ;
154+
125155 return (
126156 < >
127- < Block title = "AWS Access Key ID" description = "Use the Access Key ID of the IAM user that can access your S3 bucket." required >
128- < Input
129- style = { { width : 386 } }
130- placeholder = "AKIAIOSFODNN7EXAMPLE"
131- value = { accessKeyId }
132- onChange = { handleAccessKeyChange }
133- status = { accessKeyError ? 'error' : '' }
134- />
135- { accessKeyError && < div style = { { marginTop : 4 , color : '#f5222d' } } > { accessKeyError } </ div > }
136- </ Block >
137-
138- < Block title = "AWS Secret Access Key" description = "Use the Secret Access Key paired with the Access Key ID." required >
139- < Input . Password
140- style = { { width : 386 } }
141- placeholder = { isUpdate ? '********' : 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' }
142- value = { secretAccessKey }
143- onChange = { handleSecretKeyChange }
144- status = { secretKeyError ? 'error' : '' }
145- />
146- { secretKeyError && < div style = { { marginTop : 4 , color : '#f5222d' } } > { secretKeyError } </ div > }
157+ < Block title = "Authentication Type" description = "Choose how to authenticate with AWS" required >
158+ < Radio . Group value = { authType } onChange = { handleAuthTypeChange } >
159+ < Radio value = "access_key" > Access Key & Secret </ Radio >
160+ < Radio value = "iam_role" > IAM Role (for EC2/ECS/Lambda)</ Radio >
161+ </ Radio . Group >
147162 </ Block >
148163
149- < Block title = "AWS Region" description = "Region of the S3 bucket, e.g. us-east-1." required >
164+ { isAccessKeyAuth && (
165+ < >
166+ < Block title = "AWS Access Key ID" description = "Use the Access Key ID of the IAM user that can access your S3 bucket" required >
167+ < Input
168+ style = { { width : 386 } }
169+ placeholder = "AKIAIOSFODNN7EXAMPLE"
170+ value = { accessKeyId }
171+ onChange = { handleAccessKeyChange }
172+ status = { accessKeyError ? 'error' : '' }
173+ />
174+ { accessKeyError && < div style = { { marginTop : 4 , color : '#f5222d' } } > { accessKeyError } </ div > }
175+ </ Block >
176+
177+ < Block title = "AWS Secret Access Key" description = "Use the Secret Access Key paired with the Access Key ID" required >
178+ < Input . Password
179+ style = { { width : 386 } }
180+ placeholder = { isUpdate ? '********' : 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' }
181+ value = { secretAccessKey }
182+ onChange = { handleSecretKeyChange }
183+ status = { secretKeyError ? 'error' : '' }
184+ />
185+ { secretKeyError && < div style = { { marginTop : 4 , color : '#f5222d' } } > { secretKeyError } </ div > }
186+ </ Block >
187+ </ >
188+ ) }
189+
190+ { ! isAccessKeyAuth && (
191+ < Block title = "IAM Role Authentication" description = "DevLake will use the IAM role attached to the EC2 instance, ECS task, or Lambda function" >
192+ < div style = { { padding : '12px' , backgroundColor : '#f6f8fa' , borderRadius : '6px' , color : '#586069' } } >
193+ < p style = { { margin : 0 } } >
194+ Make sure the IAM role has the necessary S3 permissions to access your bucket.
195+ No additional credentials are required when using IAM role authentication.
196+ </ p >
197+ </ div >
198+ </ Block >
199+ ) }
200+
201+ < Block title = "AWS Region" description = "Region of the S3 bucket, e.g. us-east-1" required >
150202 < Input
151203 style = { { width : 386 } }
152204 placeholder = "us-east-1"
0 commit comments