Skip to content

Commit

Permalink
Merge pull request #41 from djcard/addVirtualStyle
Browse files Browse the repository at this point in the history
Added virtual URL style support and working tests.
  • Loading branch information
jclausen authored Sep 22, 2023
2 parents 70c9320 + 5cf2ce3 commit 6d52a53
Show file tree
Hide file tree
Showing 17 changed files with 864 additions and 98 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ modules/**

# log files
logs/**
.idea/
6 changes: 5 additions & 1 deletion ModuleConfig.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ component {
defaultBlockPublicAcls : false,
defaultIgnorePublicAcls : false,
defaultBlockPublicPolicy : false,
defaultRestrictPublicBuckets : false
defaultRestrictPublicBuckets : false,
urlStyle : "path"
};
}

Expand Down Expand Up @@ -90,6 +91,9 @@ component {
.initArg(
name = "defaultRestrictPublicBuckets",
value = variables.settings.defaultRestrictPublicBuckets
).initArg(
name = "urlStyle",
value = variables.settings.urlStyle
);
binder.map( "Sv4Util@s3sdk" ).to( "#moduleMapping#.models.AmazonS3" );

Expand Down
75 changes: 53 additions & 22 deletions models/AmazonS3.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* s3_accessKey : The Amazon access key
* s3_secretKey : The Amazon secret key
* s3_encryption_charset : encryptyion charset (Optional, defaults to utf-8)
* s3_ssl : Whether to use ssl on all cals or not (Optional, defaults to false)
* s3_ssl : Whether to use ssl on all calls or not (Optional, defaults to false)
*/
component accessors="true" singleton {

Expand Down Expand Up @@ -59,7 +59,7 @@ component accessors="true" singleton {
property name="defaultIgnorePublicAcls";
property name="defaultBlockPublicPolicy";
property name="defaultRestrictPublicBuckets";

property name="urlStyle";

// STATIC Contsants
this.ACL_PRIVATE = "private";
Expand Down Expand Up @@ -109,6 +109,7 @@ component accessors="true" singleton {
* @defaultIgnorePublicAcls Specifies whether Amazon S3 should block public bucket policies for this bucket. Setting this element to TRUE causes Amazon S3 to reject calls to PUT Bucket policy if the specified bucket policy allows public access.
* @defaultBlockPublicPolicy Specifies whether Amazon S3 should ignore public ACLs for this bucket and objects in this bucket. Setting this element to TRUE causes Amazon S3 to ignore all public ACLs on this bucket and objects in this bucket.
* @defaultRestrictPublicBuckets Specifies whether Amazon S3 should restrict public bucket policies for this bucket. Setting this element to TRUE restricts access to this bucket to only AWS service principals and authorized users within this account if the bucket has a public policy.
* @urlStyle Specifies the format of the URL whether it is the `path` format or `virtual` format. Defaults to path. For more information see https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html
*
* @return An AmazonS3 instance.
*/
Expand Down Expand Up @@ -139,7 +140,8 @@ component accessors="true" singleton {
boolean defaultBlockPublicAcls = false,
boolean defaultIgnorePublicAcls = false,
boolean defaultBlockPublicPolicy = false,
boolean defaultRestrictPublicBuckets = false
boolean defaultRestrictPublicBuckets = false,
string urlStyle = "path"
){
if ( arguments.awsDomain == "amazonaws.com" && arguments.awsRegion == "" ) {
arguments.awsRegion = "us-east-1";
Expand Down Expand Up @@ -176,6 +178,7 @@ component accessors="true" singleton {
variables.defaultIgnorePublicAcls = arguments.defaultIgnorePublicAcls;
variables.defaultBlockPublicPolicy = arguments.defaultBlockPublicPolicy;
variables.defaultRestrictPublicBuckets = arguments.defaultRestrictPublicBuckets;
variables.urlStyle = arguments.urlStyle;

// Construct the SSL Domain
setSSL( arguments.ssl );
Expand Down Expand Up @@ -261,21 +264,34 @@ component accessors="true" singleton {
/**
* This function builds variables.UrlEndpoint and variables.URLEndpointHostname according to credentials and ssl configuration, usually called after init() for you automatically.
*/
AmazonS3 function buildUrlEndpoint(){
AmazonS3 function buildUrlEndpoint( string bucketName ){
// Build accordingly
var URLEndPointProtocol = ( variables.ssl ) ? "https://" : "http://";

var hostnameComponents = [];
if ( variables.awsDomain contains "amazonaws.com" ) {
hostnameComponents.append( "s3" );
}
if ( len( variables.awsRegion ) ) {
hostnameComponents.append( variables.awsRegion );
if ( variables.urlStyle == "path" ) {
if ( variables.awsDomain contains "amazonaws.com" ) {
hostnameComponents.append( "s3" );
}
if ( len( variables.awsRegion ) ) {
hostnameComponents.append( variables.awsRegion );
}
} else if ( variables.urlStyle == "virtual" ) {
if ( variables.awsDomain contains "amazonaws.com" ) {
if ( !isNull( arguments.bucketName ) ) {
hostnameComponents.append( arguments.bucketName );
}

hostnameComponents.append( "s3" );

if ( len( variables.awsRegion ) ) {
hostnameComponents.append( variables.awsRegion );
}
}
}
hostnameComponents.append( variables.awsDomain );
variables.URLEndpointHostname = arrayToList( hostnameComponents, "." );
variables.URLEndpoint = URLEndpointProtocol & variables.URLEndpointHostname;

return this;
}

Expand Down Expand Up @@ -325,7 +341,7 @@ component accessors="true" singleton {
if ( results.error ) {
throw( message = "Error making Amazon REST Call", detail = results.message );
}

// Should this return whatever comes from AWS? It seems like hardcoding a potentially wrong answer is not a good idea.
if ( len( results.response.LocationConstraint.XMLText ) ) {
return results.response.LocationConstraint.XMLText;
}
Expand Down Expand Up @@ -858,7 +874,7 @@ component accessors="true" singleton {

var finalized = s3Request(
method = "POST",
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
timeout = arguments.HTTPTimeout,
parameters = { "uploadId" : uploadId },
body = finalizeBody
Expand All @@ -875,7 +891,7 @@ component accessors="true" singleton {
} catch ( any e ) {
s3Request(
method = "DELETE",
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
timeout = arguments.HTTPTimeout,
parameters = { "uploadId" : uploadId }
);
Expand Down Expand Up @@ -1034,7 +1050,7 @@ component accessors="true" singleton {

var results = s3Request(
method = "PUT",
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
body = arguments.data,
timeout = arguments.HTTPTimeout,
headers = headers
Expand Down Expand Up @@ -1065,7 +1081,7 @@ component accessors="true" singleton {
var headers = applyEncryptionHeaders( {}, arguments );
var results = s3Request(
method = "HEAD",
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
headers = headers
);

Expand Down Expand Up @@ -1096,7 +1112,7 @@ component accessors="true" singleton {
requireBucketName( arguments.bucketName );
var results = s3Request(
method = "GET",
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
parameters = { "acl" : "" },
throwOnError = throwOnError
);
Expand Down Expand Up @@ -1139,7 +1155,7 @@ component accessors="true" singleton {
requireBucketName( arguments.bucketName );
var results = s3Request(
method = "HEAD",
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
throwOnError = false
);
var status_code = results.responseHeader.status_code ?: 0;
Expand Down Expand Up @@ -1292,7 +1308,7 @@ component accessors="true" singleton {

return s3Request(
method = "POST",
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
timeout = arguments.HTTPTimeout,
headers = headers,
parameters = { "uploads" : true },
Expand Down Expand Up @@ -1320,14 +1336,15 @@ component accessors="true" singleton {
required string uri,
string encryptionKey = variables.defaultEncryptionKey
){
buildUrlEndpoint( arguments.bucketName );
requireBucketName( arguments.bucketName );

var headers = applyEncryptionHeaders( {}, arguments );

var results = s3Request(
method = "GET",
headers = headers,
resource = arguments.bucketName & "/" & arguments.uri
resource = buildKeyName( arguments.uri, arguments.bucketName )
);
return results;
}
Expand Down Expand Up @@ -1365,7 +1382,7 @@ component accessors="true" singleton {
var results = s3Request(
method = "GET",
headers = headers,
resource = arguments.bucketName & "/" & arguments.uri,
resource = buildKeyName( arguments.uri, arguments.bucketName ),
filename = arguments.filepath,
timeout = arguments.HTTPTimeout,
getAsBinary = arguments.getAsBinary,
Expand Down Expand Up @@ -1393,7 +1410,10 @@ component accessors="true" singleton {
boolean function deleteObject( required string bucketName = variables.defaultBucketName, required string uri ){
requireBucketName( arguments.bucketName );

var results = s3Request( method = "DELETE", resource = arguments.bucketName & "/" & arguments.uri );
var results = s3Request(
method = "DELETE",
resource = buildKeyName( arguments.uri, arguments.bucketName )
);

return results.responseheader.status_code == 204;
}
Expand Down Expand Up @@ -1861,7 +1881,7 @@ component accessors="true" singleton {
* Determines mime type from the file extension
*
* @filePath The path to the file stored in S3.
*
*
* @return string
*/
string function getFileMimeType( required string filePath ){
Expand All @@ -1883,4 +1903,15 @@ component accessors="true" singleton {
return contentType;
}


/**
* Creates the s3 key name based on the format (path or virtual) from the bucket name and the object key
*
* @url The key for the file in question
* @bucketName The name of the bucket to use. Not needed if the urlStyle is `virtual`
**/
function buildKeyName( required string uri, string bucketName = "" ){
return variables.urlStyle == "path" ? arguments.bucketName & ( arguments.bucketName.len() ? "/" : "" ) & arguments.uri : arguments.uri;
}

}
8 changes: 6 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ This SDK will be installed into a directory called `s3sdk` and then the SDK can
* @debug Used to turn debugging on or off outside of logbox. Defaults to false.
* @defaultEncryptionAlgorithm The default server side encryption algorithm to use. Usually "AES256". Not needed if using custom defaultEncryptionKey
* @defaultEncryptionKey The default base64 encoded AES 356 bit key for server side encryption.
*
* @urlStyle Specifies the format of the URL whether it is the `path` format or `virtual` format. Defaults to path. For more information see https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html
*
* @return An AmazonS3 instance.
*/
public AmazonS3 function init(
Expand All @@ -75,6 +76,7 @@ public AmazonS3 function init(
boolean debug= false,
string defaultEncryptionAlgorithm = "",
string defaultEncryptionKey = "",
string urlStyle = "path"
)
```

Expand Down Expand Up @@ -125,7 +127,9 @@ moduleSettings = {
// SSL mode or not on cfhttp calls and when generating put/get authenticated URLs: Defaults to true
ssl = true,
// Throw exceptions when s3 requests fail, else it swallows them up.
throwOnRequestError : true
throwOnRequestError : true,
// What format of endpoint to use whether path or virtual
urlStyle = "path"
}
};
```
Expand Down
2 changes: 1 addition & 1 deletion [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name":"s3sdk-adobe@2023",
"app":{
"serverHomeDirectory":".engine/adobe2023",
"cfengine":"[email protected].0-beta.1"
"cfengine":"[email protected].4+330500"
},
"web":{
"http":{
Expand Down
Loading

0 comments on commit 6d52a53

Please sign in to comment.