Skip to content

Commit

Permalink
PRESIDECMS-2708 add more nuanced cache-control headers with asset dow…
Browse files Browse the repository at this point in the history
…nloads.

For restricted assets, we should use cache-control: private to avoid proxies or any
other shared caches from caching the response. Public assets can explicitly be
labelled as such.

In addition, provide configurable timeouts for the caches.
  • Loading branch information
DominicWatson committed Sep 18, 2023
1 parent 8e0bb6e commit 282408c
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 45 deletions.
4 changes: 4 additions & 0 deletions system/config/Config.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,10 @@ component {
, trash = ( settings.env[ "assetmanager.storage.trash" ] ?: settings.uploads_directory & "/.trash" )
, publicUrl = ( settings.env[ "assetmanager.storage.publicUrl" ] ?: "" )
}
, cacheExpiry = {
public = Val( settings.env.ASSET_CACHE_EXPIRY_PUBLIC ?: 31536000 ) // one year
, private = Val( settings.env.ASSET_CACHE_EXPIRY_PRIVATE ?: 86400 ) // one day
}
};
settings.assetManager.allowedExtensions = _typesToExtensions( settings.assetManager.types );
settings.assetManager.types.document.append( { tiff = { serveAsAttachment = true, mimeType="image/tiff" } } );
Expand Down
98 changes: 53 additions & 45 deletions system/handlers/core/AssetDownload.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ component {
property name="websiteUserActionService" inject="websiteUserActionService";
property name="rulesEngineWebRequestService" inject="rulesEngineWebRequestService";
property name="queueMaxWaitAttempts" inject="coldbox:setting:assetManager.queue.downloadWaitSeconds";
property name="publicCacheAge" inject="coldbox:setting:assetManager.cacheExpiry.public";
property name="privateCacheAge" inject="coldbox:setting:assetManager.cacheExpiry.private";

public function asset( event, rc, prc ) output=false {
public function asset( event, rc, prc ) {
announceInterception( "preDownloadAsset" );

_checkDownloadPermissions( argumentCollection=arguments );
var permissionSettings = _getPermissionSettings( argumentCollection=arguments );
if ( permissionSettings.restricted ) {
_checkDownloadPermissions( argumentCollection=arguments, permissionSettings=permissionSettings );
}

var assetId = rc.assetId ?: "";
var versionId = rc.versionId ?: "";
Expand Down Expand Up @@ -138,7 +143,11 @@ component {
}

header name="etag" value=etag;
header name="cache-control" value="max-age=31536000";
if ( permissionSettings.restricted ) {
header name="cache-control" value="private, max-age=#privateCacheAge#";
} else {
header name="cache-control" value="public, max-age=#publicCacheAge#";
}

if ( IsBinary( assetFilePathOrBinary ) ) {
content
Expand Down Expand Up @@ -167,7 +176,7 @@ component {
}

// private helpers
private string function _doBrowserEtagLookup( required string etag ) output=false {
private string function _doBrowserEtagLookup( required string etag ) {
if ( ( cgi.http_if_none_match ?: "" ) == arguments.etag ) {
announceInterception( "onReturnAsset304", { etag = arguments.etag } );
content reset=true;header statuscode=304 statustext="Not Modified";abort;
Expand All @@ -178,62 +187,61 @@ component {
return ReReplace( arguments.assetTitle, "\.#arguments.extension#$", "" ) & "." & arguments.extension;
}

private void function _checkDownloadPermissions( event, rc, prc ) output=false {
private struct function _getPermissionSettings( event, rc, prc ) {
var assetId = rc.assetId ?: "";
var derivativeName = rc.derivativeId ?: "";

if ( Len( Trim( derivativeName ) ) && assetManagerService.isDerivativePubliclyAccessible( derivativeName ) ) {
return;
return { restricted=false };
}

var permissionSettings = assetManagerService.getAssetPermissioningSettings( assetId );

if ( !event.isAdminUser() ) {
if ( permissionSettings.restricted ) {
if ( Len( Trim( permissionSettings.conditionId ) ) ) {
var conditionIsTrue = rulesEngineWebRequestService.evaluateCondition( permissionSettings.conditionId );

if ( !conditionIsTrue ) {
if ( !isLoggedIn() || ( permissionSettings.fullLoginRequired && isAutoLoggedIn() ) ) {
event.accessDenied( reason="LOGIN_REQUIRED", postLoginUrl=( cgi.http_referer ?: "" ) );
} else {
event.accessDenied( reason="INSUFFICIENT_PRIVILEGES" );
}
}
return;
}
var hasPerm = event.isAdminUser() && hasCmsPermission(
permissionKey = "assetmanager.assets.download"
, context = "assetmanagerfolder"
, contextKeys = permissionSettings.contextTree
, forceGrantByDefault = IsBoolean( permissionSettings.grantAcessToAllLoggedInUsers ) && permissionSettings.grantAcessToAllLoggedInUsers
);
if ( hasPerm ) { return; }

if ( !isLoggedIn() || ( permissionSettings.fullLoginRequired && isAutoLoggedIn() ) ) {
event.accessDenied( reason="LOGIN_REQUIRED", postLoginUrl=( cgi.http_referer ?: "" ) );
}
return assetManagerService.getAssetPermissioningSettings( assetId );
}

hasPerm = hasWebsitePermission(
permissionKey = "assets.access"
, context = "asset"
, contextKeys = permissionSettings.contextTree
, forceGrantByDefault = IsBoolean( permissionSettings.grantAcessToAllLoggedInUsers ) && permissionSettings.grantAcessToAllLoggedInUsers
)
if ( !hasPerm ) {
event.accessDenied( reason="INSUFFICIENT_PRIVILEGES" );
}
}
} else {
private void function _checkDownloadPermissions( event, rc, prc, permissionSettings ) {
var assetId = rc.assetId ?: "";
var derivativeName = rc.derivativeId ?: "";
var hasPerm = false;

if ( event.isAdminUser() ) {
hasPerm = hasCmsPermission(
permissionKey = "assetmanager.assets.download"
, context = "assetmanagerfolder"
, contextKeys = permissionSettings.contextTree ?: []
, contextKeys = arguments.permissionSettings.contextTree ?: []
);
if ( !hasPerm ) {
event.accessDenied( reason="INSUFFICIENT_PRIVILEGES" );
}
return;
}

if ( Len( Trim( arguments.permissionSettings.conditionId ) ) ) {
var conditionIsTrue = rulesEngineWebRequestService.evaluateCondition( arguments.permissionSettings.conditionId );

if ( conditionIsTrue ) {
return;
}

if ( !isLoggedIn() || ( arguments.permissionSettings.fullLoginRequired && isAutoLoggedIn() ) ) {
event.accessDenied( reason="LOGIN_REQUIRED", postLoginUrl=( cgi.http_referer ?: "" ) );
} else {
event.accessDenied( reason="INSUFFICIENT_PRIVILEGES" );
}
}

if ( !isLoggedIn() || ( arguments.permissionSettings.fullLoginRequired && isAutoLoggedIn() ) ) {
event.accessDenied( reason="LOGIN_REQUIRED", postLoginUrl=( cgi.http_referer ?: "" ) );
}

hasPerm = hasWebsitePermission(
permissionKey = "assets.access"
, context = "asset"
, contextKeys = arguments.permissionSettings.contextTree
, forceGrantByDefault = IsBoolean( arguments.permissionSettings.grantAcessToAllLoggedInUsers ) && arguments.permissionSettings.grantAcessToAllLoggedInUsers
);

if ( !hasPerm ) {
event.accessDenied( reason="INSUFFICIENT_PRIVILEGES" );
}
}
}

0 comments on commit 282408c

Please sign in to comment.