VIVO-660 Add an Authorization tabs to the developer panel

This commit is contained in:
j2blake 2014-01-07 15:08:30 -05:00
parent 0f0cac5d35
commit 54509b1aee
7 changed files with 285 additions and 60 deletions

View file

@ -0,0 +1,176 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.auth.policy;
import static edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.Authorization.INCONCLUSIVE;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyDecision;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyIface;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.Key;
/**
* If enabled in the developer settings (and log levels), log each
* PolicyDecision (subject to restrictions).
*
* Some restrictions apply to the logger as a whole. Others apply to the
* particular policy or the particular decision.
*/
public class PolicyDecisionLogger {
private static final Log log = LogFactory
.getLog(PolicyDecisionLogger.class);
private static final Pattern NEVER_MATCHES = Pattern.compile("^__NEVER__$");
private static final BasicPolicyDecision NULL_DECISION = new BasicPolicyDecision(
INCONCLUSIVE, "The decision was null.");
private final DeveloperSettings settings;
private final RequestedAction whatToAuth;
private final IdentifierBundle whoToAuth;
private final boolean enabled;
private final Pattern policyRestriction;
private final boolean skipInconclusive;
private final boolean includeIdentifiers;
public PolicyDecisionLogger(IdentifierBundle whoToAuth,
RequestedAction whatToAuth) {
this.settings = DeveloperSettings.getInstance();
this.whoToAuth = whoToAuth;
this.whatToAuth = whatToAuth;
this.enabled = figureEnabled();
this.policyRestriction = figurePolicyRestriction();
this.skipInconclusive = figureSkipInconclusive();
this.includeIdentifiers = figureIncludeIdentifiers();
}
private boolean figureEnabled() {
return log.isInfoEnabled()
&& settings.getBoolean(Key.AUTHORIZATION_LOG_DECISIONS_ENABLE)
&& passesUserRestriction() && passesActionRestriction();
}
/**
* The identifier bundle passes if there is no restriction, or if the
* restriction pattern is found within concatenated string of the identifier
* bundle.
*
* If the restriction is invalid, the action fails.
*/
private boolean passesUserRestriction() {
Pattern userRestriction = compilePatternFromSetting(Key.AUTHORIZATION_LOG_DECISIONS_USER_RESTRICTION);
return userRestriction == null
|| userRestriction.matcher(String.valueOf(whoToAuth)).find();
}
/**
* The requested action passes if there is no restriction, or if the
* restriction pattern is found within the class name of the action.
*
* If the restriction is invalid, the action fails.
*/
private boolean passesActionRestriction() {
Pattern actionRestriction = compilePatternFromSetting(Key.AUTHORIZATION_LOG_DECISIONS_ACTION_RESTRICTION);
return actionRestriction == null
|| actionRestriction.matcher(String.valueOf(whatToAuth)).find();
}
/**
* Only compile the policy restriction pattern once.
*/
private Pattern figurePolicyRestriction() {
return compilePatternFromSetting(Key.AUTHORIZATION_LOG_DECISIONS_POLICY_RESTRICTION);
}
/**
* Do we log inconclusive decisions?
*/
private boolean figureSkipInconclusive() {
return settings
.getBoolean(Key.AUTHORIZATION_LOG_DECISIONS_SKIP_INCONCLUSIVE);
}
/**
* Do we include Identifiers in the log record?
*/
private boolean figureIncludeIdentifiers() {
return settings
.getBoolean(Key.AUTHORIZATION_LOG_DECISIONS_ADD_IDENTIFERS);
}
/**
* If no pattern was provided, return null. If an invalid pattern was
* provided, return a pattern that never matches.
*/
private Pattern compilePatternFromSetting(Key key) {
String setting = settings.getString(key);
if (setting.isEmpty()) {
return null;
} else {
try {
return Pattern.compile(setting);
} catch (Exception e) {
return NEVER_MATCHES;
}
}
}
/**
* If the logger and the policy and the decision all pass the restrictions,
* write to the log. A null decision is treated as inconclusive.
*/
public void log(PolicyIface policy, PolicyDecision pd) {
if (passesRestrictions(String.valueOf(policy), pd)) {
if (this.includeIdentifiers) {
log.info(String.format(
"Decision on %s by %s was %s; user is %s",
this.whatToAuth, policy, pd, this.whoToAuth));
} else {
log.info(String.format("Decision on %s by %s was %s",
this.whatToAuth, policy, pd));
}
}
}
private boolean passesRestrictions(String policyString, PolicyDecision pd) {
if (pd == null) {
pd = NULL_DECISION;
}
return enabled && passesPolicyRestriction(policyString)
&& passesConclusiveRestriction(pd);
}
private boolean passesPolicyRestriction(String policyString) {
return this.policyRestriction == null
|| this.policyRestriction.matcher(policyString).find();
}
private boolean passesConclusiveRestriction(PolicyDecision pd) {
return !(skipInconclusive && isInconclusive(pd));
}
private boolean isInconclusive(PolicyDecision pd) {
return pd == null || pd.getAuthorized() == INCONCLUSIVE;
}
public void logNoDecision(PolicyDecision pd) {
if (enabled) {
if (this.includeIdentifiers) {
log.info(pd.getMessage() + "; user is " + this.whoToAuth);
} else {
log.info(pd.getMessage());
}
}
}
}

View file

@ -103,14 +103,16 @@ public class PolicyList extends ArrayList<PolicyIface> implements PolicyIface{
protected PolicyDecision checkAgainstPolicys( IdentifierBundle whoToAuth, RequestedAction whatToAuth){ protected PolicyDecision checkAgainstPolicys( IdentifierBundle whoToAuth, RequestedAction whatToAuth){
PolicyDecision pd = null; PolicyDecision pd = null;
PolicyDecisionLogger logger = new PolicyDecisionLogger(whoToAuth, whatToAuth);
for(PolicyIface policy : this){ for(PolicyIface policy : this){
try{ try{
pd = policy.isAuthorized(whoToAuth, whatToAuth); pd = policy.isAuthorized(whoToAuth, whatToAuth);
logger.log(policy, pd);
if( pd != null ){ if( pd != null ){
if( pd.getAuthorized() == Authorization.AUTHORIZED ) if( pd.getAuthorized() == Authorization.AUTHORIZED )
break; return pd;
if( pd.getAuthorized() == Authorization.UNAUTHORIZED ) if( pd.getAuthorized() == Authorization.UNAUTHORIZED )
break; return pd;
if( pd.getAuthorized() == Authorization.INCONCLUSIVE ) if( pd.getAuthorized() == Authorization.INCONCLUSIVE )
continue; continue;
} else{ } else{
@ -120,7 +122,10 @@ public class PolicyList extends ArrayList<PolicyIface> implements PolicyIface{
log.error("ignoring exception in policy " + policy.toString(), th ); log.error("ignoring exception in policy " + policy.toString(), th );
} }
} }
log.debug("decision " + pd + " for " + whatToAuth);
pd = new BasicPolicyDecision(Authorization.INCONCLUSIVE,
"No policy returned a conclusive decision on " + whatToAuth);
logger.logNoDecision(pd);
return pd; return pd;
} }

View file

@ -205,6 +205,7 @@ public class DeveloperSettings {
Properties dsProps = new Properties(); Properties dsProps = new Properties();
dsProps.load(reader); dsProps.load(reader);
devSettings.updateFromProperties(dsProps); devSettings.updateFromProperties(dsProps);
log.info(devSettings);
ss.info(this, "Loaded the 'developer.properties' file: " ss.info(this, "Loaded the 'developer.properties' file: "
+ devSettings); + devSettings);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {

View file

@ -24,7 +24,7 @@ public enum Key {
ENABLED("developer.enabled", true), ENABLED("developer.enabled", true),
/** /**
* If the developer panel is enabled, can a non-logged-in user change the * If the developer panel is enabled, may an anonymous user change the
* settings? * settings?
*/ */
PERMIT_ANONYMOUS_CONTROL("developer.permitAnonymousControl", true), PERMIT_ANONYMOUS_CONTROL("developer.permitAnonymousControl", true),
@ -84,7 +84,45 @@ public enum Key {
* Tell the ShortViewLogger to note the use of non-default short views. * Tell the ShortViewLogger to note the use of non-default short views.
*/ */
PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW( PAGE_CONTENTS_LOG_CUSTOM_SHORT_VIEW(
"developer.pageContents.logCustomShortView", true); "developer.pageContents.logCustomShortView", true),
/**
* Enable the PolicyDecisionLogger.
*/
AUTHORIZATION_LOG_DECISIONS_ENABLE(
"developer.authorization.logDecisions.enable", true),
/**
* Enable the PolicyDecisionLogger.
*/
AUTHORIZATION_LOG_DECISIONS_ADD_IDENTIFERS(
"developer.authorization.logDecisions.addIdentifiers", true),
/**
* Enable the PolicyDecisionLogger.
*/
AUTHORIZATION_LOG_DECISIONS_SKIP_INCONCLUSIVE(
"developer.authorization.logDecisions.skipInconclusive", true),
/**
* Don't log policy decisions unless the requested action meets this
* restriction.
*/
AUTHORIZATION_LOG_DECISIONS_ACTION_RESTRICTION(
"developer.authorization.logDecisions.actionRestriction", false),
/**
* Don't log policy decisions unless the identifier bundle meets this
* restriction.
*/
AUTHORIZATION_LOG_DECISIONS_USER_RESTRICTION(
"developer.authorization.logDecisions.userRestriction", false),
/**
* Don't log policy decisions unless the policy meets this restriction.
*/
AUTHORIZATION_LOG_DECISIONS_POLICY_RESTRICTION(
"developer.authorization.logDecisions.policyRestriction", false);
private static final Log log = LogFactory.getLog(Key.class); private static final Log log = LogFactory.getLog(Key.class);
private final String propertyName; private final String propertyName;

View file

@ -10,6 +10,9 @@ div.developer {
div.developer #developerPanelBody { div.developer #developerPanelBody {
display: none; display: none;
}
#developerPanelBody * {
line-height: 1em; line-height: 1em;
font-size: small; font-size: small;
} }

View file

@ -48,11 +48,19 @@ function DeveloperPanel(developerAjaxUrl) {
document.getElementById("developer_i18n_defeatCache").disabled = !developerEnabled; document.getElementById("developer_i18n_defeatCache").disabled = !developerEnabled;
document.getElementById("developer_i18n_logStringRequests").disabled = !developerEnabled; document.getElementById("developer_i18n_logStringRequests").disabled = !developerEnabled;
document.getElementById("developer_loggingRDFService_enable").disabled = !developerEnabled; document.getElementById("developer_loggingRDFService_enable").disabled = !developerEnabled;
document.getElementById("developer_authorization_logDecisions_enable").disabled = !developerEnabled;
var rdfServiceEnabled = developerEnabled && document.getElementById("developer_loggingRDFService_enable").checked; var rdfServiceEnabled = developerEnabled && document.getElementById("developer_loggingRDFService_enable").checked;
document.getElementById("developer_loggingRDFService_stackTrace").disabled = !rdfServiceEnabled; document.getElementById("developer_loggingRDFService_stackTrace").disabled = !rdfServiceEnabled;
document.getElementById("developer_loggingRDFService_queryRestriction").disabled = !rdfServiceEnabled; document.getElementById("developer_loggingRDFService_queryRestriction").disabled = !rdfServiceEnabled;
document.getElementById("developer_loggingRDFService_stackRestriction").disabled = !rdfServiceEnabled; document.getElementById("developer_loggingRDFService_stackRestriction").disabled = !rdfServiceEnabled;
var authLoggingEnabled = developerEnabled && document.getElementById("developer_authorization_logDecisions_enable").checked;
document.getElementById("developer_authorization_logDecisions_skipInconclusive").disabled = !authLoggingEnabled;
document.getElementById("developer_authorization_logDecisions_addIdentifiers").disabled = !authLoggingEnabled;
document.getElementById("developer_authorization_logDecisions_actionRestriction").disabled = !authLoggingEnabled;
document.getElementById("developer_authorization_logDecisions_policyRestriction").disabled = !authLoggingEnabled;
document.getElementById("developer_authorization_logDecisions_userRestriction").disabled = !authLoggingEnabled;
} }
function collectFormData() { function collectFormData() {

View file

@ -1,12 +1,18 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> <#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#macro showCheckbox key> <#macro showCheckbox key, labelText>
<label>
<input type="checkbox" id="${key}" <#if settings[key]>checked</#if>> <input type="checkbox" id="${key}" <#if settings[key]>checked</#if>>
${labelText}
</label>
</#macro> </#macro>
<#macro showTextbox key> <#macro showTextbox key, labelText>
<label>
${labelText}
<input type="text" id="${key}" size="30" value="${settings[key]}" > <input type="text" id="${key}" size="30" value="${settings[key]}" >
</label>
</#macro> </#macro>
<#if !settings.developer_enabled> <#if !settings.developer_enabled>
@ -21,14 +27,10 @@
</h1> </h1>
<div id="developerPanelBody"> <div id="developerPanelBody">
<div> <div>
<label> <@showCheckbox "developer_enabled",
<@showCheckbox "developer_enabled" /> "Enable developer mode" />
Enable developer mode <@showCheckbox "developer_permitAnonymousControl",
</label> "Allow anonymous user to see and modify developer settings" />
<label>
<@showCheckbox "developer_permitAnonymousControl" />
Allow anonymous user to see and modify developer settings
</label>
</div> </div>
<div id="developerTabs"> <div id="developerTabs">
@ -41,26 +43,18 @@
<div class="devright"> <div class="devright">
<div class="container"> <div class="container">
Page configuration Page configuration
<label> <@showCheckbox "developer_pageContents_logCustomListView",
<@showCheckbox "developer_pageContents_logCustomListView" /> "Log the use of custom list view XML files." />
Log the use of custom list view XML files. <@showCheckbox "developer_pageContents_logCustomShortView" ,
</label> "Log the use of custom short views in search, index and browse pages."/>
<label>
<@showCheckbox "developer_pageContents_logCustomShortView" />
Log the use of custom short views in search, index and browse pages.
</label>
</div> </div>
<div class="container"> <div class="container">
Language support Language support
<label> <@showCheckbox "developer_i18n_defeatCache",
<@showCheckbox "developer_i18n_defeatCache" /> "Defeat the cache of language property files" />
Defeat the cache of language property files <@showCheckbox "developer_i18n_logStringRequests",
</label> "Log the retrieval of language strings" />
<label>
<@showCheckbox "developer_i18n_logStringRequests" />
Log the retrieval of language strings
</label>
</div> </div>
<div class="container"> <div class="container">
@ -77,43 +71,43 @@
<div class="devleft"> <div class="devleft">
<div class="container"> <div class="container">
Freemarker templates Freemarker templates
<label> <@showCheckbox "developer_defeatFreemarkerCache",
<@showCheckbox "developer_defeatFreemarkerCache" /> "Defeat the template cache" />
Defeat the template cache <@showCheckbox "developer_insertFreemarkerDelimiters",
</label> "Insert HTML comments at start and end of templates" />
<label>
<@showCheckbox "developer_insertFreemarkerDelimiters" />
Insert HTML comments at start and end of templates
</label>
</div> </div>
<div class="container"> <div class="container">
SPARQL Queries SPARQL Queries
<label> <@showCheckbox "developer_loggingRDFService_enable",
<@showCheckbox "developer_loggingRDFService_enable" /> "Log each query" />
Log each query
</label>
<div class="within"> <div class="within">
<label> <@showCheckbox "developer_loggingRDFService_stackTrace",
<@showCheckbox "developer_loggingRDFService_stackTrace" /> "Add stack trace" />
Add stack trace <@showTextbox "developer_loggingRDFService_queryRestriction",
</label> "Restrict by query string" />
<label> <@showTextbox "developer_loggingRDFService_stackRestriction",
Restrict by query string "Restrict by calling stack" />
<@showTextbox "developer_loggingRDFService_queryRestriction" />
</label>
<label>
Restrict by calling stack
<@showTextbox "developer_loggingRDFService_stackRestriction" />
</label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div id="developerTabAuthorization"> <div id="developerTabAuthorization">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. <@showCheckbox "developer_authorization_logDecisions_enable",
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. "Write policy decisions to the log" />
<div class="within">
<@showCheckbox "developer_authorization_logDecisions_skipInconclusive",
"Skip inconclusive decisions" />
<@showCheckbox "developer_authorization_logDecisions_addIdentifiers",
"Include the user identifiers in the log record" />
<@showTextbox "developer_authorization_logDecisions_actionRestriction",
"Restrict by requested action" />
<@showTextbox "developer_authorization_logDecisions_policyRestriction",
"Restrict by policy name" />
<@showTextbox "developer_authorization_logDecisions_userRestriction",
"Restrict by user identifiers" />
</div>
</div> </div>
</div> </div>