Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NP-2927] Add DAO decorator to warn about (or redirect) system context #4398

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions src/foam/nanos/auth/PreventSystemDAO.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
foam.CLASS({
package: 'foam.nanos.auth',
name: 'PreventSystemDAO',
extends: 'foam.dao.ProxyDAO',
flags: ['java'],
documentation: `
Prevents, or warns about, a system context making changes to a DAO intended
for use within a session context. This is particularly useful when other DAO
decorators expect a meaningful subject in the context.
`,

implements: [
'foam.nanos.boot.NSpecAware'
],

javaImports: [
'foam.dao.DAO',
'foam.nanos.alarming.Alarm',
'foam.nanos.alarming.AlarmReason',
'foam.nanos.logger.LogFields',
'foam.log.LogLevel',
'foam.nanos.logger.Logger'
],

enums: [
{
name: 'PreventionMode',
values: [
{ name: 'ABORT_REQUEST' },
{ name: 'REDIRECT_REQUEST' },
{ name: 'WARN_ONLY' }
]
}
],

// TODO: remove if InnerEnum supported in Java
constants: [
{ name: 'ABORT_REQUEST', type: 'Integer', value: 0 },
{ name: 'REDIRECT_REQUEST', type: 'Integer', value: 1 },
{ name: 'WARN_ONLY', type: 'Integer', value: 2 },
{
name: 'LOG_MESSAGE',
type: 'String',
value: 'Invalid DAO access from a system context'
}
],

properties: [
{
name: 'preventionMode',
class: 'Int', // TODO: update if InnerEnum supported in Java
value: 0,
documentation: `
Change this to false to allow a system context DAO access, but still
warn about this behaviouor.
`
},
{
name: 'localDAOKey',
class: 'String',
documentation: `
When preventionMode is set to REDIRECT_REQUEST, this is the nspec name
of the DAO the system context should be using - usually localMyDAO for
a served DAO named myDAO.
`
},
{
class: 'Enum',
of: 'foam.log.LogLevel',
name: 'severity',
value: 'ERROR'
},
{
name: 'nSpec',
class: 'FObjectProperty',
type: 'foam.nanos.boot.NSpec'
},
{
name: 'putName',
class: 'String',
javaFactory: 'return getNSpec().getName() + ":put";',
visibility: 'RO'
},
{
name: 'findName',
class: 'String',
javaFactory: 'return getNSpec().getName() + ":find";',
visibility: 'RO'
},
{
name: 'selectName',
class: 'String',
javaFactory: 'return getNSpec().getName() + ":select";',
visibility: 'RO'
},
{
name: 'removeName',
class: 'String',
javaFactory: 'return getNSpec().getName() + ":remove";',
visibility: 'RO'
},
{
name: 'removeAllName',
class: 'String',
javaFactory: 'return getNSpec().getName() + ":removeAll";',
visibility: 'RO'
},
],

methods: [
{
name: 'put_',
javaCode: `
DAO delegate = checkSystem(x, getPutName());
if ( delegate != null ) {
return delegate.put_(x, obj);
}
return super.put_(x, obj);
`
},
{
name: 'find_',
javaCode: `
DAO delegate = checkSystem(x, getFindName());
if ( delegate != null ) {
return delegate.find_(x, id);
}
return super.find_(x, id);
`
},
{
name: 'select_',
javaCode: `
DAO delegate = checkSystem(x, getSelectName());
if ( delegate != null ) {
return delegate.select_(x, sink, skip, limit, order, predicate);
}
return super.select_(x, sink, skip, limit, order, predicate);
`
},
{
name: 'remove_',
javaCode: `
DAO delegate = checkSystem(x, getRemoveName());
if ( delegate != null ) {
return delegate.remove_(x, obj);
}
return super.remove_(x, obj);
`
},
{
name: 'removeAll_',
javaCode: `
DAO delegate = checkSystem(x, getRemoveAllName());
if ( delegate != null ) {
delegate.removeAll_(x, skip, limit, order, predicate);
return;
}
super.removeAll_(x, skip, limit, order, predicate);
`
},
{
name: 'checkSystem',
type: 'foam.dao.DAO',
args: [
{ name: 'x', type: 'Context' },
{ name: 'op', type: 'String' }
],
javaCode: `
var logger = (Logger) x.get("logger");
var user = ((Subject) x.get("subject")).getUser();
if ( user.SYSTEM_USER_ID != user.getId() ) {
return getDelegate();
}

var fields = new LogFields();
fields.put("op", op);

if ( getPreventionMode() == REDIRECT_REQUEST ) {
logger.warning(
LOG_MESSAGE
+ "; redirected to: " + getLocalDAOKey(),
fields
);
return (DAO) x.get(getLocalDAOKey());
}

if ( getPreventionMode() == WARN_ONLY ) {
logger.warning(LOG_MESSAGE, fields);
return getDelegate();
}

// ABORT_REQUEST
logger.error(LOG_MESSAGE, fields);
Alarm alarm = new Alarm("Unexpected system context",
AlarmReason.CONFIGURATION);
alarm.setSeverity(LogLevel.ERROR);
alarm.setNote(
"PreventSystemDAO aborted the following operation: "
+ op);
((DAO) x.get("alarmDAO")).put(alarm);
throw new RuntimeException(
"Access to DAO from invalid context: " +
op
);
`
}
]
});
5 changes: 5 additions & 0 deletions src/foam/nanos/crunch/services.jrl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ p({
"serviceScript":"""
dec = new foam.dao.NullDAO(x, foam.nanos.crunch.Capability.getOwnClassInfo());
dec = new foam.nanos.crunch.CapabilityAvailabilityDAO(x, dec);
dec = new foam.nanos.auth.PreventSystemDAO.Builder(x)
.setDelegate(dec)
.setPreventionMode(1)
.setLocalDAOKey("localCapabilityDAO")
.build();
return new foam.dao.EasyDAO.Builder(x)
.setInnerDAO(x.get("localCapabilityDAO"))
.setDecorator(dec)
Expand Down
11 changes: 11 additions & 0 deletions src/foam/nanos/logger/LogFields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
foam.CLASS({
package: 'foam.nanos.logger',
name: 'LogFields',
javaExtends: 'java.util.HashMap<String,String>',
documentation: `
Wraps a Map<String,String> to indicate that the logger should treat these
values as log fields. Multiple arguments of type LogFields will be combined
into a single map that is displayed at the end of the log message, and also
saved onto the LogMessage object.
`
});
2 changes: 2 additions & 0 deletions src/foam/nanos/nanos.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ FOAM_FILES([
{ name: 'foam/nanos/client/ClientBuilder' },
{ name: 'foam/nanos/controller/AppStyles', flags: ['web'] },
{ name: "foam/nanos/logger/LogMessage" },
{ name: "foam/nanos/logger/LogFields" },
{ name: "foam/nanos/logger/LogMessageDAO" },
{ name: "foam/nanos/logger/AbstractLogger" },
{ name: "foam/nanos/logger/ProxyLogger" },
Expand Down Expand Up @@ -114,6 +115,7 @@ FOAM_FILES([
{ name: "foam/nanos/auth/test/PasswordPolicyTest" },
{ name: "foam/nanos/auth/LastModifiedByAwareDAO" },
{ name: "foam/nanos/auth/PermissionedPropertyDAO" },
{ name: "foam/nanos/auth/PreventSystemDAO" },
{ name: "foam/nanos/auth/ProfilePictureView", flags: ['web'] },
{ name: "foam/nanos/auth/twofactor/OTPAuthService" },
{ name: "foam/nanos/auth/twofactor/AbstractOTPAuthService" },
Expand Down
2 changes: 2 additions & 0 deletions tools/classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ var classes = [
'foam.nanos.auth.resetPassword.ResetPasswordTokenService',
'foam.nanos.auth.PreventDuplicateEmailDAO',
'foam.nanos.auth.PermissionedPropertyDAO',
'foam.nanos.auth.PreventSystemDAO',
'foam.nanos.auth.SystemAuthService',
'foam.nanos.auth.HidePropertiesSink',
'foam.nanos.auth.ServiceProvider',
Expand Down Expand Up @@ -332,6 +333,7 @@ var classes = [
'foam.nanos.logger.DAOLogger',
'foam.nanos.logger.FileLogger',
'foam.nanos.logger.Logger',
'foam.nanos.logger.LogFields',
'foam.nanos.logger.NotificationLogMessageDAO',
'foam.nanos.logger.RepeatLogMessageDAO',
'foam.nanos.logger.ProxyLogger',
Expand Down