Skip to content

Commit

Permalink
Support self editor profile matching by using External Auth ID (vivo-…
Browse files Browse the repository at this point in the history
…project#444)

* refactored PolicyHelper, use UserAccount instead of IdentifierBundle

* Support user profile matching by External Auth ID

* fixes for review

* fixed typo
  • Loading branch information
litvinovg authored Apr 17, 2024
1 parent 0f18627 commit f484c98
Show file tree
Hide file tree
Showing 29 changed files with 358 additions and 231 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

package edu.cornell.mannlib.vedit.beans;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

Expand Down Expand Up @@ -100,9 +99,7 @@ public static UserAccount getCurrentUser(HttpSession session) {
if (!getBean(session).isLoggedIn()) {
return null;
}

ServletContext ctx = session.getServletContext();
WebappDaoFactory wadf = ModelAccess.on(ctx).getWebappDaoFactory();
WebappDaoFactory wadf = ModelAccess.getInstance().getWebappDaoFactory();
UserAccountsDao userAccountsDao = wadf.getUserAccountsDao();
if (userAccountsDao == null) {
log.error("No UserAccountsDao");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import edu.cornell.mannlib.vitro.webapp.auth.attributes.AttributeValueSet;
import edu.cornell.mannlib.vitro.webapp.auth.objects.AccessObject;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Expand All @@ -26,47 +27,77 @@
import org.apache.jena.rdf.model.RDFNode;

public class SparqlSelectQueryResultsChecker {
private static final String PROFILE_URI = "profileUri";
private static final String EXTERNAL_AUTH_ID = "externalAuthId";
private static final String MATCHING_PROPERTY_URI = "matchingPropertyUri";
private static final String OBJECT_URI = "objectUri";
private static final Log log = LogFactory.getLog(SparqlSelectQueryResultsChecker.class);

public static boolean sparqlSelectQueryResultsContain(Check check, AuthorizationRequest ar, String[] inputValues) {
String queryTemplate = check.getConfiguration();
if (StringUtils.isBlank(queryTemplate)) {
queryTemplate = check.getValues().getSingleValue();
}
if (StringUtils.isBlank(queryTemplate)) {
log.error("SparqlQueryContains template is empty");
return false;
String query = check.getConfiguration();
if (StringUtils.isBlank(query)) {
query = check.getValues().getSingleValue();
if (StringUtils.isBlank(query)) {
log.error("Sparql query is empty.");
return false;
}
}

AccessObject ao = ar.getAccessObject();
Model m = ao.getModel();
if (m == null) {
log.debug("SparqlQueryContains model is not provided");
return false;
}
Set<String> profileUris = new HashSet<String>(ar.getEditorUris());
if (profileUris.isEmpty()) {
if (queryTemplate.contains("?profileUri")) {
log.debug("Subject has no person URIs");
return false;
} else {
profileUris.add("");
}
}

Set<String> comparedValues = new HashSet<>();

if (isQueryNotProvidedInConfiguration(check)) {
addRelatedUrisToComparedValues(ao, comparedValues);
} else {
addValuesToComparedValues(check.getValues(), comparedValues);
}

if (isProfileUriRelatedQuery(query)) {
return makeProfileUriMatchQuery(ar, query, m, comparedValues);
}

if (query.contains("?" + EXTERNAL_AUTH_ID) && externalAuthIdIsNotAvailable(ar)) {
logVariableNotAvailable(EXTERNAL_AUTH_ID);
return false;
}

if (query.contains("?" + MATCHING_PROPERTY_URI) && matchingPropertyUriIsNotAvailable()) {
logVariableNotAvailable(MATCHING_PROPERTY_URI);
return false;
}

Set<String> sparqlResults = getSparqlSelectResults(m, "", query, ar);
sparqlResults.retainAll(comparedValues);
if (!sparqlResults.isEmpty()) {
return true;
}

return false;
}

private static boolean makeProfileUriMatchQuery(AuthorizationRequest ar, String queryTemplate, Model m,
Set<String> comparedValues) {
boolean result = false;
Set<String> profileUris = new HashSet<String>(ar.getEditorUris());
if (profileUris.isEmpty()) {
log.debug("Subject has no person Uri, nothing to substitute.");
result = false;
}
for (String profileUri : profileUris) {
Set<String> sparqlSelectResults = getSparqlSelectResults(m, profileUri, queryTemplate, ar);
Set<String> sparqlResults = getSparqlSelectResults(m, profileUri, queryTemplate, ar);
// Return true if intersection is not empty
comparedValues.retainAll(sparqlSelectResults);
if (!comparedValues.isEmpty()) {
return true;
sparqlResults.retainAll(comparedValues);
if (!sparqlResults.isEmpty()) {
result = true;
}
}
return false;
return result;
}

private static void addValuesToComparedValues(AttributeValueSet values, Set<String> comparedValues) {
Expand Down Expand Up @@ -115,11 +146,19 @@ private static Set<String> getSparqlSelectResults(Model model, String profileUri
}

private static void setVariables(String profileUri, AuthorizationRequest ar, ParameterizedSparqlString pss) {
pss.setIri("profileUri", profileUri);
pss.setIri(PROFILE_URI, profileUri);
AccessObject object = ar.getAccessObject();
Optional<String> uri = object.getUri();
if (uri.isPresent()) {
pss.setIri("objectUri", uri.get());
pss.setIri(OBJECT_URI, uri.get());
}
String externalAuthId = ar.getExternalAuthId();
if (!StringUtils.isBlank(externalAuthId)) {
pss.setLiteral(EXTERNAL_AUTH_ID, externalAuthId);
}
String matchingPropertyUri = SelfEditingConfiguration.getInstance().getMatchingPropertyUri();
if (!StringUtils.isBlank(matchingPropertyUri)) {
pss.setIri(MATCHING_PROPERTY_URI, matchingPropertyUri);
}
}

Expand All @@ -144,7 +183,7 @@ private static void debug(String queryText) {

private static String createQueryMapKey(String profileUri, String queryTemplate, AuthorizationRequest ar) {
String mapKey = queryTemplate + "." + profileUri;
if (queryTemplate.contains("?objectUri")) {
if (queryTemplate.contains("?" + OBJECT_URI)) {
AccessObject object = ar.getAccessObject();
Optional<String> uri = object.getUri();
if (uri.isPresent()) {
Expand All @@ -154,4 +193,22 @@ private static String createQueryMapKey(String profileUri, String queryTemplate,
return mapKey;
}

private static void logVariableNotAvailable(String variable) {
if (log.isDebugEnabled()) {
log.debug(String.format("Query contains ?%s, but authorization request doesn't provide it.", variable));
}
}

private static boolean externalAuthIdIsNotAvailable(AuthorizationRequest ar) {
return StringUtils.isBlank(ar.getExternalAuthId());
}

private static boolean matchingPropertyUriIsNotAvailable() {
return StringUtils.isBlank(SelfEditingConfiguration.getInstance().getMatchingPropertyUri());
}

private static boolean isProfileUriRelatedQuery(String queryTemplate) {
return queryTemplate.contains("?" + PROFILE_URI);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

package edu.cornell.mannlib.vitro.webapp.auth.checks;

import java.util.ArrayList;
import java.util.List;

import edu.cornell.mannlib.vitro.webapp.auth.attributes.Attribute;
import edu.cornell.mannlib.vitro.webapp.auth.attributes.AttributeValueSet;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

Expand All @@ -20,7 +22,10 @@ public SubjectRoleCheck(String uri, AttributeValueSet values) {

@Override
public boolean check(AuthorizationRequest ar) {
final List<String> inputValues = ar.getRoleUris();
List<String> inputValues = new ArrayList<String>(ar.getRoleUris());
if (inputValues.isEmpty()) {
inputValues.add(VitroVocabulary.ROLE_PUBLIC_URI);
}
if (AttributeValueChecker.test(this, ar, inputValues.toArray(new String[0]))) {
log.debug("Attribute match requested '" + inputValues + "'");
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

import edu.cornell.mannlib.vitro.webapp.auth.attributes.Attribute;
import edu.cornell.mannlib.vitro.webapp.auth.attributes.AttributeValueSet;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Expand All @@ -20,8 +18,7 @@ public SubjectTypeCheck(String uri, AttributeValueSet values) {

@Override
public boolean check(AuthorizationRequest ar) {
IdentifierBundle ac_subject = ar.getIds();
String inputValue = IsRootUser.isRootUser(ac_subject) ? "ROOT_USER" : "";
String inputValue = ar.isRootUser() ? "ROOT_USER" : "";
if (AttributeValueChecker.test(this, ar, inputValue)) {
log.debug("Attribute subject type match requested object type '");
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
import java.util.regex.Pattern;

import edu.cornell.mannlib.vitro.webapp.auth.attributes.AccessOperation;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.objects.AccessObject;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.Policy;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyDecision;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
import edu.cornell.mannlib.vitro.webapp.utils.developer.Key;
import org.apache.commons.logging.Log;
Expand All @@ -33,7 +33,7 @@ public class PolicyDecisionLogger {
private final DeveloperSettings settings;
private final AccessObject object;
private final AccessOperation operation;
private final IdentifierBundle whoToAuth;
private final UserAccount whoToAuth;

private final boolean enabled;

Expand All @@ -43,7 +43,7 @@ public class PolicyDecisionLogger {

public PolicyDecisionLogger(AuthorizationRequest ar) {
this.settings = DeveloperSettings.getInstance();
this.whoToAuth = ar.getIds();
this.whoToAuth = ar.getUserAccount();
this.object = ar.getAccessObject();
this.operation = ar.getAccessOperation();

Expand All @@ -67,7 +67,7 @@ private boolean figureEnabled() {
*/
private boolean passesUserRestriction() {
Pattern userRestriction = compilePatternFromSetting(Key.AUTHORIZATION_LOG_DECISIONS_USER_RESTRICTION);
return userRestriction == null || userRestriction.matcher(String.valueOf(whoToAuth)).find();
return userRestriction == null || userRestriction.matcher(String.valueOf(whoToAuth.getUri())).find();
}

/**
Expand Down Expand Up @@ -126,7 +126,7 @@ public void log(Policy policy, PolicyDecision pd) {
if (passesRestrictions(String.valueOf(policy), pd)) {
if (this.includeIdentifiers) {
log.info(String.format("Decision on %s %s by %s was %s; user is %s", this.operation, this.object,
policy.getShortUri(), pd, this.whoToAuth));
policy.getShortUri(), pd, getUser()));
} else {
log.info(String.format("Decision on %s %s by %s was %s", this.operation, this.object,
policy.getShortUri(), pd));
Expand Down Expand Up @@ -156,10 +156,14 @@ private boolean isInconclusive(PolicyDecision pd) {
public void logNoDecision(PolicyDecision pd) {
if (enabled) {
if (this.includeIdentifiers) {
log.info(pd.getMessage() + "; user is " + this.whoToAuth);
log.info(pd.getMessage() + "; user is " + getUser());
} else {
log.info(pd.getMessage());
}
}
}

private String getUser() {
return whoToAuth.getLastName() + ", " + whoToAuth.getFirstName() + " uri:'" + whoToAuth.getUri() + "'";
}
}
Loading

0 comments on commit f484c98

Please sign in to comment.