Skip to content

Instantly share code, notes, and snippets.

@jamiejackson
Last active October 10, 2019 16:58
Show Gist options
  • Save jamiejackson/7f325428e9582b3fca8c5140910c5dee to your computer and use it in GitHub Desktop.
Save jamiejackson/7f325428e9582b3fca8c5140910c5dee to your computer and use it in GitHub Desktop.
<cfscript>
// Don't let saveFlashContext persist complex variables
/* TODO: this is a stopgap until we have FW/1 consolidated across plugins,
at which point, we'll have these in a CFC that extends FW/1 and we'll use
that extended CFC in our plugins. At the moment, we'll include this as
a mixin from plugins' Application.cfc
*/
// default the value to disallow queries
param name="variables.framework.allowQueriesInFlashContext" value=false;
private void function internalFrameworkTraceWrapper()
hint="Some of the fw/1 versions we use don't have this function, so wrap it."
{
// only run the function if it exists
if (isDefined("internalFrameworkTrace")) {
internalFrameworkTrace( argumentCollection = arguments );
}
}
private boolean function isRcElementAllowedInFlashContext(
required any element,
required boolean allowQueriesInFlashContext
)
hint="determines whether an RC element should be allowed in flash context"
{
if ( isSimpleValue(element) ) {
return true;
} else if ( // don't allow mura vars (e.g., $ or PC to get persisted)
isValid('component', element)
&& getMetaData(element).name.listFirst(".") == 'mura'
) {
return false;
} else if ( isValid('query', element) && !allowQueriesInFlashContext ) { // no queries allowed
return false;
} else {
return true;
}
}
private void function logFW1Issue(
required string message,
required string action,
string type = "warning"
)
hint="Log instances of attempted complex variable storage."
{
var logMessage = "saveFlashContext:";
if (structKeyExists(variables.framework, "package")) {
logMessage = "#logMessage# package=#variables.framework.package#;";
}
logMessage = "#logMessage# action=#action#; #message#";
logWrapper(
file="custom_fw_one",
type=type,
text=logMessage
);
}
private void function logWrapper(
required string file,
required string type,
required string text
)
hint="don't log automated tests. this creates a seam to bypass real logging."
{
writeLog(argumentCollection = arguments);
}
private string function saveFlashContext(
string keys,
struct requestScope = request,
struct sessionScope = session
)
hint="Override the stock FW/1 function to prevent saving of complex vars"
{
var curPreserveKey = getNextPreserveKeyAndPurgeOld();
var preserveKeySessionKey = getPreserveKeySessionKey( curPreserveKey );
try {
param name="sessionScope.#preserveKeySessionKey#" default="#{ }#";
if ( keys == 'all' ) {
keys = structKeyList(requestScope.context);
}
var key = 0;
var keyNames = listToArray( keys );
var logMessage = "";
var logType = "information";
var interestingKeys = [];
for ( key in keyNames ) {
key = trim( key );
if ( !structKeyExists( requestScope.context, key ) ) {
internalFrameworkTraceWrapper(
message = 'key "#key#" does not exist in RC,
cannot preserve.',
traceType="WARNING"
);
} else if (
!isRcElementAllowedInFlashContext(
requestScope.context[key],
variables.framework.allowQueriesInFlashContext
)
) {
interestingKeys.append(key);
logMessage = "keys skipped";
} else {
sessionScope[ preserveKeySessionKey ][ key ] = requestScope.context[ key ];
}
}
if (interestingKeys.len() > 0) {
// custom logging
interestingKeys.sort('textnocase');
logFW1Issue(
message="#logMessage#='#interestingKeys.toList()#'",
action = requestScope.action,
type=logType
);
}
} catch ( any ex ) {
if ( ex.message contains 'session scope is not enabled' ) {
// session scope not enabled, do nothing
internalFrameworkTraceWrapper( message = 'sessionManagement not enabled, cannot preserve RC keys.', traceType = 'WARNING' );
} else {
rethrow();
}
}
return curPreserveKey;
}
</cfscript>
// plugins/myplugin/Application.cfc
...
// ignore this variable and what it controls. i just didn't want developers inadvertently
// saving rc-scoped queries *on my project*, which could bloat the session
variables.framework.allowQueriesInFlashContext = true;
include template="/hudexchange_deployment_root/lib/org/framework/_fw1_override_mixin.cfm";
...
// /test/lib/org/framework/LegacyFrameworkOneFixture.cfc
component
// notice that we're extending fw/1
extends="lib.org.framework.one",
hint="Since we're testing a mix-in, we need a component fixture for it. This one uses a legacy version of FW/1."
{
public void function setAllowQueriesInFlashContext(required boolean allow)
hint="allows automated tests to set the value"
{
variables.framework.allowQueriesInFlashContext = allow;
}
// include the mix-in, which overrides fw/1 functions
include template="/hudexchange_deployment_root/lib/org/framework/_fw1_override_mixin.cfm";
}
// /test/lib/org/framework/ModernFrameworkOneFixture.cfc
component
// notice that we're extending fw/1
extends="lib.org.framework.3_1_0.one",
hint="Since we're testing a mix-in, we need a component fixture for it. This one uses a more modern FW/1 version."
{
public void function setAllowQueriesInFlashContext(required boolean allow)
hint="allows automated tests to set the value"
{
variables.framework.allowQueriesInFlashContext = allow;
}
// include the mix-in, which overrides fw/1 functions
include template="/hudexchange_deployment_root/lib/org/framework/_fw1_override_mixin.cfm";
}
// /test/lib/org/framework/OneTest.cfc
component
extends="testbox.system.BaseSpec"
hint="Test FW/1 Overrides"
{
function run( testResults, testBox ){
feature("Framework one overrides and supporting code work properly.", function() {
beforeEach(function (currentSpec){
cut = prepareMock(new ModernFrameworkOneFixture());
cut.$("logWrapper");
});
story("saveFlashContext() override function", function(spec, suite) {
beforeEach(function (currentSpec){
makePublic(cut, 'saveFlashContext');
// mock scopes to help with testing
mockRequestScope = {
"action": "fakeFw1Action"
};
mockSessionScope = {};
});
given(
"I have a mura framework, a pluginConfig, a query, and simpler elements in the RC scope
and queries are not allowed",
function() {
when("the flash context is saved with redirect(..., preserve='all')", function() {
beforeEach(function (currentSpec){
allowedElems = {
'aString': "myString",
'aNumber': 1,
'aStruct': {"aKey": "aValue"},
'anArray': [1,2,3]
};
disallowedElems = {
'$': createMock('mura.muraScope'),
'pluginConfig': createMock('mura.plugin.pluginConfig'),
'aQuery': queryNew('aColumn')
};
expectedLogMessage = "saveFlashContext: action=fakeFw1Action; keys skipped='$,aQuery,pluginConfig'";
// set up the mock request context (combine both allowed
// and disallowed RC scope items together)
mockRequestScope.context = allowedElems.duplicate();
mockRequestScope.context.append(disallowedElems);
cut.saveFlashContext(
keys = "all",
requestScope = mockRequestScope,
sessionScope = mockSessionScope
);
logWrapperLog = cut.$callLog().logWrapper;
// the flash context goes inside a session['__fw1'] key
expectedSession["__fw1"] = allowedElems.duplicate();
});
then("(only) non-mura and query elements are stripped out of the persistent scope.", function() {
expect(mockSessionScope).toBe(expectedSession);
});
then("logging is correct", function() {
expect(logWrapperLog[1].text).toBe(expectedLogMessage);
});
});
}
);
given(
"I have a mura framework, a pluginConfig, a query, and simpler elements in the RC scope
and queries are allowed",
function() {
when("the flash context is saved with redirect(..., preserve='all')", function() {
beforeEach(function (currentSpec){
cut.setAllowQueriesInFlashContext(true);
allowedElems = {
'aString': "myString",
'aNumber': 1,
'aStruct': {"aKey": "aValue"},
'anArray': [1,2,3],
'aQuery': queryNew('aColumn')
};
disallowedElems = {
'$': createMock('mura.muraScope'),
'pluginConfig': createMock('mura.plugin.pluginConfig')
};
expectedLogMessage = "saveFlashContext: action=fakeFw1Action; keys skipped='$,pluginConfig'";
// set up the mock request context (combine both allowed
// and disallowed RC scope items together)
mockRequestScope.context = allowedElems.duplicate();
mockRequestScope.context.append(disallowedElems);
cut.saveFlashContext(
keys = "all",
requestScope = mockRequestScope,
sessionScope = mockSessionScope
);
logWrapperLog = cut.$callLog().logWrapper;
// the flash context goes inside a session['__fw1'] key
expectedSession["__fw1"] = allowedElems.duplicate();
});
then("(only) mura elements are stripped out of the flash context.", function() {
expect(mockSessionScope).toBe(expectedSession);
});
then("logging is correct", function() {
expect(logWrapperLog[1].text).toBe(expectedLogMessage);
});
});
}
);
});
story("internalFrameworkTraceWrapper function works properly", function(spec, suite) {
given("a legacy version of fw/1 is used", function() {
then("no exceptions are thrown", function() {
cut = prepareMock(new LegacyFrameworkOneFixture());
makePublic(cut, 'internalFrameworkTraceWrapper');
expect( function(){
cut.internalFrameworkTraceWrapper(
message = "foo",
traceType = "WARNING"
);
}).notToThrow();
});
});
given("a modern version of fw/1 is used", function() {
then("no exceptions are thrown", function() {
cut = prepareMock(new ModernFrameworkOneFixture());
makePublic(cut, 'internalFrameworkTraceWrapper');
expect( function(){
cut.internalFrameworkTraceWrapper(
message = "foo",
traceType = "WARNING"
);
}).notToThrow();
});
});
});
describe("isRcElementAllowedInFlashContext()", function() {
beforeEach(function (currentSpec){
makePublic(cut, 'isRcElementAllowedInFlashContext');
});
given("queries are allowed", function() {
beforeEach(function (currentSpec){
allowQueriesInFlashContext = true
});
then("a query is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = queryNew('aColumn'),
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
then("a string is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = 'myString',
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
then("a number is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = 1e10,
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
then("a mura component is not allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = createMock('mura.plugin.pluginConfig'),
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeFalse();
});
then("a struct is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = {"aKey": "aValue"},
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
then("an array is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = [1,2,3],
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
});
given("queries are not allowed", function() {
beforeEach(function (currentSpec){
allowQueriesInFlashContext = false
});
then("a query is not allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = queryNew('aColumn'),
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeFalse();
});
then("a string is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = 'myString',
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
then("a number is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = 1e10,
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
then("a mura component is not allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = createMock('mura.plugin.pluginConfig'),
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeFalse();
});
then("a struct is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = {"aKey": "aValue"},
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
then("an array is allowed", function() {
expect(
cut.isRcElementAllowedInFlashContext(
element = [1,2,3],
allowQueriesInFlashContext = allowQueriesInFlashContext
)
).toBeTrue();
});
});
});
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment