-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy paths3presignedUrl.fmfunction
104 lines (83 loc) · 4.83 KB
/
s3presignedUrl.fmfunction
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
==================================================================
PURPOSE:
Get a presigned URL for an object in S3 that is valid for the specified period of time and is safe to make public (ie: does not compromise your secret key)
PARAMETERS:
method | GET or PUT or DELETE
bucket | the bucket name
region | eg: ca-central-1
theFilePath | the full path to the file in the bucket eg: /folder/filename.pdf
expireSeconds | the length of time that the URL will be valid
accessKey | AWS Access Key
secretKey | AWS Secret Key
optionsObj | empty or JSON object containing optional options
RETURNS:
(string) Presigned URL
NOTES & DEPENDENCIES:
Optional options in optionsObj:
host | the provider's domain, with or without the bucket name. not required for AWS.
token | the session token (e.g., from AWS STS), if your role requires you to include one.
CONTRIBUTORS
JW / Jason Wood / Define Database / www.definedatabase.com
CLC / Cristos Lianides-Chin / Codence / www.codence.com
CHANGELOG
2021-03-20 JW Created
2021-03-23 JW Added method parameter
2021-03-25 JW Added URL encoding of file path
2021-03-30 JW Added optionsObj parameter to pass optional parameters
2021-07-23 JW Updated to convert "%2F" to "/" after signing, for compatibility with web viewer in WebDirect
2021-09-08 JW Added substitutions to account for GetAsURLEncoded() failure to encode some special characters: ' ! ( ) * :
2022-01-13 JW Modified how third party provider hostnames are handled for better compatibility. You must now pass the full hostname for non-AWS providers.
2023-06-29 JW Transitioned from S3 dash Region endpoint structure to S3 dot Region endpoint structure to expand support to all AWS regions.
2024-06-03 CLC Added support for optional `X-Amz-Security-Token` header via `token` in `optionsObj` when using AWS STS session tokens
==================================================================
*/
Let ([
~method = Upper ( method ) ;
~accessKey = accessKey ;
~secretKey = secretKey ;
~region = region ;
~bucket = bucket ;
~file = GetAsURLEncoded ( theFilePath ) ; //should start with "/"
~file = Substitute ( ~file ; [ "'" ; "%27" ] ; [ "!" ; "%21" ] ; [ "(" ; "%28" ] ; [ ")" ; "%29" ] ; [ "*" ; "%2A" ] ; [ ":" ; "%3A" ] ) ; //strange limitation of GetAsURLEncoded
~expireSeconds = expireSeconds ; //up to 604800 (7 days), 86400 is 1 day
~optionsObj = If ( Left ( JSONGetElement ( optionsObj ; "xalsdjfaluksdfjlnzvoiysehsz" ) ; 3 ) = "? *" ; "{}" ; optionsObj ) ;
~optionsHost = JSONGetElement ( ~optionsObj ; "host" ) ;
~optionsToken = GetAsURLEncoded ( JSONGetElement ( ~optionsObj ; "token" ) );
~host = If ( IsEmpty ( ~optionsHost ) ; ~bucket & ".s3." & ~region & ".amazonaws.com" ; ~optionsHost ) ;
~file = Substitute ( ~file ; "%2F" ; "/" ) ;
~file = If ( Left ( ~file ; 1 ) = "/" ; ~file ; "/" & ~file ) ;
~timestampfm = GetAsTimestamp ( Int( Get ( CurrentTimeUTCMilliseconds ) / 1000 ) ) ;
~date = Year ( ~timestampfm ) & Right( "00" & Month ( ~timestampfm ); 2 ) & Right( "00" & Day ( ~timestampfm ); 2 ) ;
~timestamp = ~date & "T"
& Right( "00" & Hour ( ~timestampfm ); 2 )
& Right( "00" & Minute ( ~timestampfm ); 2 )
& Right( "00" & Seconds ( ~timestampfm ); 2 ) & "Z" ;
~lf = Char(10) ;
~query = "X-Amz-Algorithm=AWS4-HMAC-SHA256"
& "&X-Amz-Credential=" & ~accessKey & "%2F" & ~date & "%2F" & ~region & "%2F" & "s3" & "%2F" & "aws4_request"
& "&X-Amz-Date=" & ~timestamp
& "&X-Amz-Expires=" & ~expireSeconds
& If ( Length ( ~optionsToken ) ; "&X-Amz-Security-Token=" & ~optionsToken )
& "&X-Amz-SignedHeaders=host" ;
~canonical_request = ~method & ~lf
& ~file & ~lf
& ~query & ~lf
& "host:" & ~host & ~lf
& ~lf
& "host" & ~lf
& "UNSIGNED-PAYLOAD" ;
~canonical_hashed = Lower ( HexEncode ( CryptDigest ( ~canonical_request ; "SHA256" ) ) ) ;
~string_to_sign = "AWS4-HMAC-SHA256" & ~lf
& ~timestamp & ~lf
& ~date & "/" & ~region & "/s3/aws4_request" & ~lf
& ~canonical_hashed ;
~kDate = CryptAuthCode ( ~date ; "SHA256" ; "AWS4" & ~secretKey ) ;
~kRegion = CryptAuthCode ( ~region ; "SHA256" ; ~kDate ) ;
~kService = CryptAuthCode ( "s3" ; "SHA256" ; ~kRegion ) ;
~kSigning = CryptAuthCode ( "aws4_request" ; "SHA256" ; ~kService ) ;
~signature = Lower( HexEncode ( CryptAuthCode ( ~string_to_sign ; "SHA256" ; ~kSigning ) ) );
~query = Substitute ( ~query ; ["%2F" ; "/" ]) //must be percent encoded to calculate the signature, but it is more compatible with WebDirect if the URL is converted back to unencoded form.
];
"https://" & ~host & ~file & "?" & ~query & "&X-Amz-Signature=" & ~signature
)