Compare commits
	
		
			1 Commits
		
	
	
		
			dependabot
			...
			AUT-405
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a650e3beaa | 
							
								
								
									
										34
									
								
								packages/backend/src/apps/amazon-s3/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								packages/backend/src/apps/amazon-s3/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" width="428" height="512" viewBox="0 0 428 512"> | ||||||
|  |   <defs> | ||||||
|  |     <style> | ||||||
|  |       .cls-1 { | ||||||
|  |         fill: #e25444; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .cls-1, .cls-2, .cls-3 { | ||||||
|  |         fill-rule: evenodd; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .cls-2 { | ||||||
|  |         fill: #7b1d13; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .cls-3 { | ||||||
|  |         fill: #58150d; | ||||||
|  |       } | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <path class="cls-1" d="M378,99L295,257l83,158,34-19V118Z"/> | ||||||
|  |   <path class="cls-2" d="M378,99L212,118,127.5,257,212,396l166,19V99Z"/> | ||||||
|  |   <path class="cls-3" d="M43,99L16,111V403l27,12L212,257Z"/> | ||||||
|  |   <path class="cls-1" d="M42.637,98.667l169.587,47.111V372.444L42.637,415.111V98.667Z"/> | ||||||
|  |   <path class="cls-3" d="M212.313,170.667l-72.008-11.556,72.008-81.778,71.83,81.778Z"/> | ||||||
|  |   <path class="cls-3" d="M284.143,159.111l-71.919,11.733-71.919-11.733V77.333"/> | ||||||
|  |   <path class="cls-3" d="M212.313,342.222l-72.008,13.334,72.008,70.222,71.83-70.222Z"/> | ||||||
|  |   <path class="cls-2" d="M212,16L140,54V159l72.224-20.333Z"/> | ||||||
|  |   <path class="cls-2" d="M212.224,196.444l-71.919,7.823V309.105l71.919,8.228V196.444Z"/> | ||||||
|  |   <path class="cls-2" d="M212.224,373.333L140.305,355.3V458.363L212.224,496V373.333Z"/> | ||||||
|  |   <path class="cls-1" d="M284.143,355.3l-71.919,18.038V496l71.919-37.637V355.3Z"/> | ||||||
|  |   <path class="cls-1" d="M212.224,196.444l71.919,7.823V309.105l-71.919,8.228V196.444Z"/> | ||||||
|  |   <path class="cls-1" d="M212,16l72,38V159l-72-20V16Z"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										56
									
								
								packages/backend/src/apps/amazon-s3/auth/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								packages/backend/src/apps/amazon-s3/auth/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | import verifyCredentials from './verify-credentials'; | ||||||
|  | import isStillVerified from './is-still-verified'; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   fields: [ | ||||||
|  |     { | ||||||
|  |       key: 'oAuthRedirectUrl', | ||||||
|  |       label: 'OAuth Redirect URL', | ||||||
|  |       type: 'string' as const, | ||||||
|  |       required: true, | ||||||
|  |       readOnly: true, | ||||||
|  |       value: '{WEB_APP_URL}/app/amazon-s3/connections/add', | ||||||
|  |       placeholder: null, | ||||||
|  |       description: | ||||||
|  |         'When asked to input a redirect URL in AWS, enter the URL above.', | ||||||
|  |       clickToCopy: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'accessKeyId', | ||||||
|  |       label: 'Access Key ID', | ||||||
|  |       type: 'string' as const, | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: null, | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'secretAccessKey', | ||||||
|  |       label: 'Secret Access Key', | ||||||
|  |       type: 'string' as const, | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: null, | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'screenName', | ||||||
|  |       label: 'Screen Name', | ||||||
|  |       type: 'string' as const, | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: | ||||||
|  |         'Screen name of your connection to be used on Automatisch UI.', | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   verifyCredentials, | ||||||
|  |   isStillVerified, | ||||||
|  | }; | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | import { IGlobalVariable } from '@automatisch/types'; | ||||||
|  | import getCurrentUser from '../common/get-current-user'; | ||||||
|  |  | ||||||
|  | const isStillVerified = async ($: IGlobalVariable) => { | ||||||
|  |   const currentUser = await getCurrentUser($); | ||||||
|  |   return !!currentUser.resourceName; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default isStillVerified; | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | import { IGlobalVariable } from '@automatisch/types'; | ||||||
|  |  | ||||||
|  | const verifyCredentials = async ($: IGlobalVariable) => { | ||||||
|  |   const { data } = await $.http.get('/'); | ||||||
|  |  | ||||||
|  |   console.log('data:', data); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default verifyCredentials; | ||||||
							
								
								
									
										153
									
								
								packages/backend/src/apps/amazon-s3/common/add-auth-header.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								packages/backend/src/apps/amazon-s3/common/add-auth-header.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | |||||||
|  | import { IJSONObject, TBeforeRequest } from '@automatisch/types'; | ||||||
|  | import crypto from 'crypto'; | ||||||
|  | import { getISODate, getYYYYMMDD } from './get-current-date'; | ||||||
|  |  | ||||||
|  | function hmac(key: string | Buffer, data: string) { | ||||||
|  |   return crypto.createHmac('sha256', key).update(data).digest('hex'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function hmacWoHex(key: Buffer | string, data: string) { | ||||||
|  |   return crypto.createHmac('sha256', key).update(data).digest(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function hash(data: string) { | ||||||
|  |   return crypto.createHash('sha256').update(data).digest('hex'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function prepareCanonicalRequest( | ||||||
|  |   method: string, | ||||||
|  |   path: string, | ||||||
|  |   queryParams: IJSONObject | string, | ||||||
|  |   headers: IJSONObject, | ||||||
|  |   payload: string | ||||||
|  | ) { | ||||||
|  |   const canonicalRequest = [method, encodeURIComponent(path)]; | ||||||
|  |  | ||||||
|  |   // Step 3: Canonical Query String | ||||||
|  |   if (typeof queryParams === 'string') { | ||||||
|  |     canonicalRequest.push(''); | ||||||
|  |   } else { | ||||||
|  |     const sortedQueryParams = Object.keys(queryParams) | ||||||
|  |       .map( | ||||||
|  |         (key) => | ||||||
|  |           `${encodeURIComponent(key)}=${encodeURIComponent( | ||||||
|  |             queryParams[key] as string | ||||||
|  |           )}` | ||||||
|  |       ) | ||||||
|  |       .sort(); | ||||||
|  |     canonicalRequest.push(sortedQueryParams.join('&')); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Step 4: Canonical Headers | ||||||
|  |   const sortedHeaders = Object.keys(headers) | ||||||
|  |     .sort() | ||||||
|  |     .map((key) => `${key.toLowerCase()}:${(headers[key] as string).trim()}`); | ||||||
|  |  | ||||||
|  |   canonicalRequest.push(sortedHeaders.join('\n')); | ||||||
|  |  | ||||||
|  |   // Step 5: Signed Headers | ||||||
|  |   const signedHeaders = Object.keys(headers) | ||||||
|  |     .sort() | ||||||
|  |     .map((key) => key.toLowerCase()) | ||||||
|  |     .join(';'); | ||||||
|  |   canonicalRequest.push(signedHeaders); | ||||||
|  |  | ||||||
|  |   const hashedPayload = hash(payload); | ||||||
|  |   canonicalRequest.push(hashedPayload); | ||||||
|  |  | ||||||
|  |   return canonicalRequest.join('\n'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function prepareStringToSign( | ||||||
|  |   datetime: string, | ||||||
|  |   credentialScope: string, | ||||||
|  |   hashedCanonicalRequest: string | ||||||
|  | ) { | ||||||
|  |   const stringToSign = [ | ||||||
|  |     'AWS4-HMAC-SHA256', | ||||||
|  |     datetime, | ||||||
|  |     credentialScope, | ||||||
|  |     hashedCanonicalRequest, | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|  |   return stringToSign.join('\n'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function calculateSigningKey( | ||||||
|  |   secretKey: string, | ||||||
|  |   date: string, | ||||||
|  |   region: string, | ||||||
|  |   service: string | ||||||
|  | ) { | ||||||
|  |   const dateKey = hmacWoHex('AWS4' + secretKey, date); | ||||||
|  |   const dateRegionKey = hmacWoHex(dateKey, region); | ||||||
|  |   const dateRegionServiceKey = hmacWoHex(dateRegionKey, service); | ||||||
|  |   const signingKey = hmacWoHex(dateRegionServiceKey, 'aws4_request'); | ||||||
|  |   return signingKey; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function createAuthorizationHeader( | ||||||
|  |   accessKey: string, | ||||||
|  |   credentialScope: string, | ||||||
|  |   signedHeaders: string, | ||||||
|  |   signature: string | ||||||
|  | ) { | ||||||
|  |   return `AWS4-HMAC-SHA256 Credential=${accessKey}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const addAuthHeader: TBeforeRequest = ($, requestConfig) => { | ||||||
|  |   const accessKeyId = $.auth.data.accessKeyId as string; | ||||||
|  |   const secretAccessKey = $.auth.data.secretAccessKey as string; | ||||||
|  |   const date = getYYYYMMDD(); | ||||||
|  |   const formattedDate = getISODate(); | ||||||
|  |   const region = 'us-east-1'; | ||||||
|  |   const method = 'GET'; | ||||||
|  |   const path = '/'; | ||||||
|  |   const queryParams = ''; | ||||||
|  |   const payload = ''; | ||||||
|  |   const headers = { | ||||||
|  |     Host: 's3.amazonaws.com', | ||||||
|  |     'X-Amz-Content-Sha256': hash(payload), | ||||||
|  |     'X-Amz-Date': formattedDate, | ||||||
|  |   }; | ||||||
|  |   const headerKeys = Object.keys(headers) | ||||||
|  |     .sort() | ||||||
|  |     .map((header) => header.toLowerCase()) | ||||||
|  |     .join(';'); | ||||||
|  |  | ||||||
|  |   const canonicalRequest = prepareCanonicalRequest( | ||||||
|  |     method, | ||||||
|  |     path, | ||||||
|  |     queryParams, | ||||||
|  |     headers, | ||||||
|  |     payload | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const stringToSign = prepareStringToSign( | ||||||
|  |     formattedDate, | ||||||
|  |     `${date}/${region}/s3/aws4_request`, | ||||||
|  |     hash(canonicalRequest) | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const signingKey = calculateSigningKey(secretAccessKey, date, region, 's3'); | ||||||
|  |  | ||||||
|  |   const signature = hmac(signingKey, stringToSign); | ||||||
|  |  | ||||||
|  |   const authorizationHeader = createAuthorizationHeader( | ||||||
|  |     accessKeyId, | ||||||
|  |     `${date}/${region}/s3/aws4_request`, | ||||||
|  |     headerKeys, | ||||||
|  |     signature | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   if ($.auth.data?.secretAccessKey && $.auth.data?.accessKeyId) { | ||||||
|  |     requestConfig.headers.Authorization = authorizationHeader; | ||||||
|  |     requestConfig.headers['Host'] = 's3.amazonaws.com'; | ||||||
|  |     requestConfig.headers['X-Amz-Content-Sha256'] = hash(payload); | ||||||
|  |     requestConfig.headers['X-Amz-Date'] = formattedDate; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return requestConfig; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default addAuthHeader; | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | export const getYYYYMMDD = () => { | ||||||
|  |   const today = new Date(); | ||||||
|  |   const year = today.getFullYear(); | ||||||
|  |   const month = (today.getMonth() + 1).toString().padStart(2, '0'); | ||||||
|  |   const day = today.getDate().toString().padStart(2, '0'); | ||||||
|  |  | ||||||
|  |   const formattedDate = `${year}${month}${day}`; | ||||||
|  |   return formattedDate; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const getISODate = () => { | ||||||
|  |   return new Date().toISOString().replace(/[:-]|\.\d{3}/g, ''); | ||||||
|  | }; | ||||||
| @@ -1,9 +1,7 @@ | |||||||
| import { IGlobalVariable } from '@automatisch/types'; | import { IGlobalVariable } from '@automatisch/types'; | ||||||
| 
 | 
 | ||||||
| const getCurrentUser = async ($: IGlobalVariable) => { | const getCurrentUser = async ($: IGlobalVariable) => { | ||||||
|   const { data: currentUser } = await $.http.get( |   const { data: currentUser } = await $.http.get('/'); | ||||||
|     'https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses' |  | ||||||
|   ); |  | ||||||
|   return currentUser; |   return currentUser; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
							
								
								
									
										0
									
								
								packages/backend/src/apps/amazon-s3/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								packages/backend/src/apps/amazon-s3/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
									
										16
									
								
								packages/backend/src/apps/amazon-s3/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/backend/src/apps/amazon-s3/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | import defineApp from '../../helpers/define-app'; | ||||||
|  | import addAuthHeader from './common/add-auth-header'; | ||||||
|  | import auth from './auth'; | ||||||
|  |  | ||||||
|  | export default defineApp({ | ||||||
|  |   name: 'Amazon S3', | ||||||
|  |   key: 'amazon-s3', | ||||||
|  |   baseUrl: '', | ||||||
|  |   apiBaseUrl: 'https://s3.amazonaws.com', | ||||||
|  |   iconUrl: '{BASE_URL}/apps/amazon-s3/assets/favicon.svg', | ||||||
|  |   authDocUrl: 'https://automatisch.io/docs/apps/amazon-s3/connection', | ||||||
|  |   primaryColor: '7B1D13', | ||||||
|  |   supportsConnections: true, | ||||||
|  |   beforeRequest: [addAuthHeader], | ||||||
|  |   auth, | ||||||
|  | }); | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { IGlobalVariable } from '@automatisch/types'; | import { IGlobalVariable } from '@automatisch/types'; | ||||||
| import getCurrentUser from '../common/get-current-user'; | import getCurrentUser from '../../amazon-s3/common/get-current-user'; | ||||||
|  |  | ||||||
| const isStillVerified = async ($: IGlobalVariable) => { | const isStillVerified = async ($: IGlobalVariable) => { | ||||||
|   const currentUser = await getCurrentUser($); |   const currentUser = await getCurrentUser($); | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { IField, IGlobalVariable } from '@automatisch/types'; | import { IField, IGlobalVariable } from '@automatisch/types'; | ||||||
| import getCurrentUser from '../common/get-current-user'; | import getCurrentUser from '../../amazon-s3/common/get-current-user'; | ||||||
|  |  | ||||||
| type TUser = { | type TUser = { | ||||||
|   displayName: string; |   displayName: string; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user