Commit fd711f07 authored by Gradl, Tobias's avatar Gradl, Tobias
Browse files

720: Implement attribute checker

Task-Url: https://minfba.de.dariah.eu/mantisbt/view.php?id=720
parent 0130d5f6
......@@ -17,7 +17,7 @@ public class SAMLAuthenticationProvider extends org.springframework.security.sam
@Override
protected Object getUserDetails(SAMLCredential credential) {
if (this.getAttributeAggregationService()!=null) {
this.getAttributeAggregationService().aggregateIfRequired(credential);
credential = this.getAttributeAggregationService().aggregateIfRequired(credential);
}
return super.getUserDetails(credential);
}
......
package eu.dariah.de.dariahsp.saml.attributequery;
import java.util.ArrayList;
import java.util.List;
import org.opensaml.saml2.core.Attribute;
import org.slf4j.Logger;
......@@ -14,6 +13,7 @@ import eu.dariah.de.dariahsp.saml.attributequery.options.SAMLAttributeGroup;
import eu.dariah.de.dariahsp.saml.attributequery.options.SAMLAttributeQueryExclusionOptions;
import eu.dariah.de.dariahsp.saml.attributequery.options.SAMLAttributeQueryOptions;
import eu.dariah.de.dariahsp.saml.attributequery.options.SAMLRequiredAttributes;
import eu.dariah.de.dariahsp.saml.model.SAMLAggregatedCredential;
public class SAMLAttributeAggregationService {
......@@ -37,10 +37,10 @@ public class SAMLAttributeAggregationService {
}
public void aggregateIfRequired(SAMLCredential credential) {
public SAMLCredential aggregateIfRequired(SAMLCredential credential) {
if (!queryOptions.isPerformAggregation()) {
return;
return credential;
}
boolean queryRequired = true;
......@@ -60,15 +60,16 @@ public class SAMLAttributeAggregationService {
if (queryRequired) {
try {
List<Attribute> queryReturnedAttributes = attributeQuery.queryAttributes(credential, queryOptions);;
List<Attribute> queryReturnedAttributes = attributeQuery.queryAttributes(credential, queryOptions);;
this.logAttributes(queryReturnedAttributes, " attribute(s) resolved by attribute query ");
aggregateAttributes(credential, queryReturnedAttributes);
return new SAMLAggregatedCredential(credential, queryReturnedAttributes);
} catch (Exception e) {
logger.error("An exception occured while attempting to permorm attribute query", e);
}
}
return credential;
}
private void logAttributes(List<Attribute> attributes, String message) {
......@@ -127,30 +128,4 @@ public class SAMLAttributeAggregationService {
}
return true;
}
private void aggregateAttributes(SAMLCredential credential, List<Attribute> queriedAttributes) {
if (queriedAttributes == null || queriedAttributes.size()==0) {
return;
}
// Take care of duplicates
List<Attribute> replacedAttributes = new ArrayList<Attribute>();
for (Attribute queriedA : credential.getAttributes()) {
for (Attribute originalA : credential.getAttributes()) {
if (originalA.getFriendlyName().equalsIgnoreCase(queriedA.getFriendlyName()) ||
originalA.getName().equalsIgnoreCase(queriedA.getName())) {
replacedAttributes.add(originalA);
break;
}
}
}
/*for (Attribute replacedA : replacedAttributes) {
credential.getAttributes().remove(replacedA);
}*/
credential = new SAMLCredential(credential.getNameID(), credential.getAuthenticationAssertion(),
credential.getRemoteEntityID(), credential.getRelayState(), queriedAttributes, credential.getLocalEntityID());
}
}
......@@ -131,7 +131,7 @@ public class SAMLAttributeQueryImpl extends WebSSOProfileConsumerImpl implements
getAttributeQueryResponse(context);
// Unwrap and process response from IDP
List <Attribute> attrs = processResponse(context, query);
List <Attribute> attrs = processResponse(context, query, options);
logger.debug("SAML AttributeQuery resulted in the retrieval of {} attributes", attrs.size());
......@@ -164,7 +164,7 @@ public class SAMLAttributeQueryImpl extends WebSSOProfileConsumerImpl implements
}
private List<Attribute> processResponse(SAMLMessageContext context, AttributeQuery query) throws SAMLException, SecurityException, ValidationException, DecryptionException {
private List<Attribute> processResponse(SAMLMessageContext context, AttributeQuery query, SAMLAttributeQueryOptions options) throws SAMLException, SecurityException, ValidationException, DecryptionException {
SAMLObject message = context.getInboundSAMLMessage();
......@@ -216,6 +216,8 @@ public class SAMLAttributeQueryImpl extends WebSSOProfileConsumerImpl implements
// Verify assertions
List<Assertion> assertionList = response.getAssertions();
List<EncryptedAssertion> encryptedAssertionList = response.getEncryptedAssertions();
for (EncryptedAssertion ea : encryptedAssertionList) {
try {
......@@ -224,7 +226,7 @@ public class SAMLAttributeQueryImpl extends WebSSOProfileConsumerImpl implements
Assertion decryptedAssertion = context.getLocalDecrypter().decrypt(ea);
assertionList.add(decryptedAssertion);
} catch (DecryptionException e) {
logger.debug("Decryption of received assertion failed, assertion will be skipped", e);
logger.warn("Decryption of received assertion failed, assertion will be skipped", e);
}
}
......@@ -232,24 +234,24 @@ public class SAMLAttributeQueryImpl extends WebSSOProfileConsumerImpl implements
for (Assertion a : assertionList) {
try {
// Verify that the assertion is valid
verifyAssertion(a, query, context);
verifyAssertion(a, query, context, options);
} catch (AuthenticationException e) {
logger.debug("Validation of received assertion failed, assertion will be skipped", e);
logger.warn("Validation of received assertion failed, assertion will be skipped", e);
continue;
} catch (SAMLRuntimeException e) {
logger.debug("Validation of received assertion failed, assertion will be skipped", e);
logger.warn("Validation of received assertion failed, assertion will be skipped", e);
continue;
} catch (SAMLException e) {
logger.debug("Validation of received assertion failed, assertion will be skipped", e);
logger.warn("Validation of received assertion failed, assertion will be skipped", e);
continue;
} catch (org.opensaml.xml.security.SecurityException e) {
logger.debug("Validation of received assertion failed, assertion will be skipped", e);
logger.warn("Validation of received assertion failed, assertion will be skipped", e);
continue;
} catch (ValidationException e) {
logger.debug("Validation of received assertion failed, assertion will be skipped", e);
logger.warn("Validation of received assertion failed, assertion will be skipped", e);
continue;
} catch (DecryptionException e) {
logger.debug("Validation of received assertion failed, assertion will be skipped", e);
logger.warn("Validation of received assertion failed, assertion will be skipped", e);
continue;
}
......@@ -274,7 +276,7 @@ public class SAMLAttributeQueryImpl extends WebSSOProfileConsumerImpl implements
return attributes;
}
protected void verifyAssertion(Assertion assertion, AttributeQuery query, SAMLMessageContext context) throws AuthenticationException, SAMLException, org.opensaml.xml.security.SecurityException, ValidationException, DecryptionException {
protected void verifyAssertion(Assertion assertion, AttributeQuery query, SAMLMessageContext context, SAMLAttributeQueryOptions options) throws AuthenticationException, SAMLException, org.opensaml.xml.security.SecurityException, ValidationException, DecryptionException {
// Verify storage time skew
if (!isDateTimeSkewValid(getMaxAssertionTime(), assertion.getIssueInstant())) {
......@@ -287,8 +289,13 @@ public class SAMLAttributeQueryImpl extends WebSSOProfileConsumerImpl implements
// Verify validity of storage
// Advice is ignored, core 574
verifyIssuer(assertion.getIssuer(), context);
verifyAssertionSignature(assertion.getSignature(), context);
// If a signature is presented it must be verified
// otherwise we depend on the option
if (options.isSignedAssertionRequired() || assertion.getSignature()!=null) {
verifyAssertionSignature(assertion.getSignature(), context);
}
// Check subject
if (assertion.getSubject() != null) {
verifySubject(assertion.getSubject(), query, context);
......
package eu.dariah.de.dariahsp.saml.attributequery.options;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import org.opensaml.saml2.core.impl.AttributeImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.dariah.de.dariahsp.saml.SAMLAttribute;
......@@ -23,30 +19,41 @@ public class SAMLAttributeQueryOptions implements Serializable, Cloneable {
private String attributeAuthorityIDP;
private boolean queryByNameID = true;
private SAMLRequiredAttributesList requiredAttributesList;
private boolean performAggregation = true;
private boolean signedAssertionRequired = true;
private SAMLRequiredAttributesList requiredAttributesList;
private List<SAMLAttributeQueryExclusionOptions> exclusionOptions;
private SAMLAttribute queryAttribute;
public SAMLAttribute getQueryAttribute() { return queryAttribute; }
public void setQueryAttribute(SAMLAttribute queryAttribute) { this.queryAttribute = queryAttribute; }
private SAMLAttribute queryAttribute;
public boolean isSignedAssertionRequired() { return signedAssertionRequired; }
public void setSignedAssertionRequired(boolean signedAssertionRequired) { this.signedAssertionRequired = signedAssertionRequired; }
public String getAttributeAuthorityIDP() { return attributeAuthorityIDP; }
public void setAttributeAuthorityIDP(String attributeAuthorityIDP) { this.attributeAuthorityIDP = attributeAuthorityIDP; }
public boolean isQueryByNameID() { return queryByNameID; }
public void setQueryByNameID(boolean queryByNameID) { this.queryByNameID = queryByNameID; }
public SAMLRequiredAttributesList getRequiredAttributesList() { return requiredAttributesList; }
public void setRequiredAttributesList(SAMLRequiredAttributesList requiredAttributesList) { this.requiredAttributesList = requiredAttributesList; }
public boolean isPerformAggregation() { return performAggregation; }
public void setPerformAggregation(boolean performAggregation) { this.performAggregation = performAggregation; }
public List<SAMLAttributeQueryExclusionOptions> getExclusionOptions() { return exclusionOptions; }
public void setExclusionOptions(List<SAMLAttributeQueryExclusionOptions> exclusionOptions) { this.exclusionOptions = exclusionOptions; }
public SAMLAttributeQueryOptions(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public SAMLAttribute getQueryAttribute() {
return queryAttribute;
}
public void setQueryAttribute(SAMLAttribute queryAttribute) {
this.queryAttribute = queryAttribute;
}
public String getQueryAttributeAsJson() {
try {
return queryAttribute==null ? null : objectMapper.writeValueAsString(queryAttribute);
......@@ -65,23 +72,4 @@ public class SAMLAttributeQueryOptions implements Serializable, Cloneable {
logger.error("Failed to deserialize SAML Attribute", e);
}
}
public String getAttributeAuthorityIDP() { return attributeAuthorityIDP; }
public void setAttributeAuthorityIDP(String attributeAuthorityIDP) { this.attributeAuthorityIDP = attributeAuthorityIDP; }
public boolean isQueryByNameID() { return queryByNameID; }
public void setQueryByNameID(boolean queryByNameID) { this.queryByNameID = queryByNameID; }
public SAMLRequiredAttributesList getRequiredAttributesList() { return requiredAttributesList; }
public void setRequiredAttributesList(SAMLRequiredAttributesList requiredAttributesList) { this.requiredAttributesList = requiredAttributesList; }
public boolean isPerformAggregation() { return performAggregation; }
public void setPerformAggregation(boolean performAggregation) { this.performAggregation = performAggregation; }
public List<SAMLAttributeQueryExclusionOptions> getExclusionOptions() { return exclusionOptions; }
public void setExclusionOptions(List<SAMLAttributeQueryExclusionOptions> exclusionOptions) { this.exclusionOptions = exclusionOptions; }
}
\ No newline at end of file
......@@ -34,6 +34,7 @@ public class SampleExceptionController {
public ModelAndView renderErrorPage(HttpServletRequest httpRequest) {
ModelAndView errorPage = new ModelAndView("error");
String errorMsg = "";
int httpErrorCode = getErrorCode(httpRequest);
switch (httpErrorCode) {
......@@ -70,7 +71,7 @@ public class SampleExceptionController {
}
errorPage.addObject("errorMsg", errorMsg);
errorPage.addObject("exception", this.getException(httpRequest));
return errorPage;
......@@ -83,6 +84,13 @@ public class SampleExceptionController {
}
}
private Exception getException(HttpServletRequest httpRequest) {
if (httpRequest.getAttribute("javax.servlet.error.exception")==null) {
return null;
}
return (Exception) httpRequest.getAttribute("javax.servlet.error.exception");
}
private int getErrorCode(HttpServletRequest httpRequest) {
if (httpRequest.getAttribute("javax.servlet.error.status_code")==null) {
return -1;
......
......@@ -29,7 +29,7 @@ auth:
#requireArtifactResolveSigned: false
#requireLogoutRequestSigned: false
#requireLogoutResponseSigned: false
#requireAttributeQuerySigned: true
requireAttributeQuerySigned: false
maxAuthAge: -1 # in seconds
signMetadata : true
#signingAlgorithm : http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
......@@ -49,6 +49,7 @@ auth:
urls: ["https://ldap-dariah-clone.esc.rzg.mpg.de/idp/shibboleth", "https://idp.de.dariah.eu/idp/shibboleth"]
assumeAttributesComplete: false
queryIdp: https://ldap-dariah-clone.esc.rzg.mpg.de/idp/shibboleth
#queryIdp: https://idp.de.dariah.eu/idp/shibboleth
queryByNameID: false
queryAttribute:
friendlyName: eduPersonPrincipalName
......
......@@ -9,7 +9,7 @@
<!-- Enable auto-wiring -->
<context:annotation-config/>
<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled" />
<security:global-method-security access-decision-manager-ref="accessDecisionManager" pre-post-annotations="enabled" secured-annotations="enabled" />
<!-- Unsecured pages and prefixes -->
<security:http security="none" pattern="/favicon.ico"/>
......@@ -18,6 +18,7 @@
<security:http entry-point-ref="securityEntryPoint" use-expressions="false" access-decision-manager-ref="accessDecisionManager" >
<security:intercept-url pattern="/user/**" access="IS_AUTHENTICATED_FULLY"/>
<security:intercept-url pattern="/protected/**" access="IS_AUTHENTICATED_FULLY"/>
<security:intercept-url pattern="/overprotected/**" access="ROLE_UBERROLE"/>
......
......@@ -207,6 +207,7 @@
<bean id="attributeQueryOptions" class="eu.dariah.de.dariahsp.saml.attributequery.options.SAMLAttributeQueryOptions">
<constructor-arg ref="objectMapper" />
<property name="performAggregation" value="${auth.saml.sp.attributeQuery.enabled:false}" />
<property name="signedAssertionRequired" value="${auth.saml.sp.requireAttributeQuerySigned:true}" />
<property name="attributeAuthorityIDP" value="${auth.saml.sp.attributeQuery.queryIdp:#{null}}" />
<property name="queryByNameID" value="${auth.saml.sp.attributeQuery.queryByNameID:false}" />
<property name="queryAttributeAsJson" value="${auth.saml.sp.attributeQuery.queryAttribute:#{null}}" />
......@@ -290,12 +291,12 @@
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl">
<property name="maxAuthenticationAge" value="${auth.maxAuthAge:7200}" />
<property name="maxAuthenticationAge" value="${auth.saml.sp.maxAuthAge:7200}" />
</bean>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl">
<property name="maxAuthenticationAge" value="${auth.maxAuthAge:7200}" />
<property name="maxAuthenticationAge" value="${auth.saml.sp.maxAuthAge:7200}" />
</bean>
<!-- SAML 2.0 Web SSO profile -->
......
......@@ -29,6 +29,14 @@
<p>
${errorMsg}
</p>
<pre>
Failed URL: ${url}
Exception: ${exception.message}
<c:forEach items="${exception.stackTrace}" var="ste"> ${ste}
</c:forEach>
</pre>
</div>
</div>
</div>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment