Compare commits
1 Commits
helix-new-
...
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';
|
||||
|
||||
const getCurrentUser = async ($: IGlobalVariable) => {
|
||||
const { data: currentUser } = await $.http.get(
|
||||
'https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses'
|
||||
);
|
||||
const { data: currentUser } = await $.http.get('/');
|
||||
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 getCurrentUser from '../common/get-current-user';
|
||||
import getCurrentUser from '../../amazon-s3/common/get-current-user';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
const currentUser = await getCurrentUser($);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||
import getCurrentUser from '../common/get-current-user';
|
||||
import getCurrentUser from '../../amazon-s3/common/get-current-user';
|
||||
|
||||
type TUser = {
|
||||
displayName: string;
|
||||
|
Reference in New Issue
Block a user