diff options
Diffstat (limited to 'connector')
4 files changed, 254 insertions, 16 deletions
| diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSeIDASNodeConstants.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSeIDASNodeConstants.java index 97defade..94c77297 100644 --- a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSeIDASNodeConstants.java +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSeIDASNodeConstants.java @@ -33,7 +33,7 @@ public class MSeIDASNodeConstants {  	//default values  	public static final String POLICY_DEFAULT_ALLOWED_TARGETS =  -			EAAFConstants.URN_PREFIX_CDID.replaceAll(".", "\\.").replaceAll("+", "\\+") + ".*"; +			EAAFConstants.URN_PREFIX_CDID.replaceAll("\\.", "\\\\.").replaceAll("\\+", "\\\\+") + ".*";  	public static final int METADATA_SOCKED_TIMEOUT = 20 * 1000;  	//20 seconds metadata socked timeout  	public static final int DEFAULT_PVP_METADATA_VALIDITY = 24;		//24 hours @@ -42,12 +42,20 @@ public class MSeIDASNodeConstants {  	public static final String ENDPOINT_PVP_POST = "/pvp/post";  	public static final String ENDPOINT_PVP_REDIRECT = "/pvp/redirect"; - +	public static final String ENDPOINT_COUNTRYSELECTION = "/myHomeCountry"; +	  	//paths and templates  	public static final String CLASSPATH_TEMPLATE_DIR = "/templates/";  	public static final String TEMPLATE_HTML_ERROR = "error.html";  	public static final String TEMPLATE_HTML_PVP_POSTBINDING = "pvp2_post_binding.html"; -	 +	public static final String TEMPLATE_HTML_COUNTRYSELECTION = "countrySelection.html"; +	 +	//execution context and generic data +	public static final String REQ_PARAM_SELECTED_COUNTRY = "selectedCountry"; +	public static final String DATA_REQUESTERID = "req_requesterId"; +	public static final String DATA_PROVIDERNAME = "req_providerName"; +	public static final String DATA_REQUESTED_LOA_LIST = "req_requestedLoA"; +	public static final String DATA_REQUESTED_LOA_COMPERISON = "req_requestedLoAComperision";  } diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataProvider.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataProvider.java index 0edc5fcd..57f6e373 100644 --- a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataProvider.java +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataProvider.java @@ -8,6 +8,7 @@ import java.util.List;  import org.apache.commons.httpclient.HttpClient;  import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.lang3.StringUtils;  import org.opensaml.saml2.metadata.provider.MetadataProvider;  import org.opensaml.xml.parse.BasicParserPool;  import org.slf4j.Logger; @@ -18,11 +19,14 @@ import org.springframework.stereotype.Service;  import at.gv.egiz.eaaf.core.api.idp.IConfiguration;  import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration;  import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.core.impl.utils.FileUtils; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2MetadataException;  import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.AbstractChainingMetadataProvider;  import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.MetadataFilterChain;  import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.PVPEntityCategoryFilter;  import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.SchemaValidationFilter;  import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; +import at.gv.egiz.eidas.specific.connector.verification.MetadataSignatureVerificationFilter;  @Service("PVPMetadataProvider")  public class PVPMetadataProvider extends AbstractChainingMetadataProvider{ @@ -47,14 +51,31 @@ public class PVPMetadataProvider extends AbstractChainingMetadataProvider{  			throws EAAFConfigurationException, IOException, CertificateException {  		ISPConfiguration spConfig = basicConfig.getServiceProviderConfiguration(entityId);  		if (spConfig != null) { -			String metadataURL = spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_URL); -			String trustStoreUrl = spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_TRUSTSTORE); -			return createNewSimpleMetadataProvider(metadataURL, 								  -					buildMetadataFilterChain(spConfig, metadataURL, trustStoreUrl),  -					spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_UNIQUEIDENTIFIER), -					getTimer(), -					new BasicParserPool(), -					createHttpClient(metadataURL)); +			try { +				String metadataURL = spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_URL); +				if (StringUtils.isEmpty(metadataURL)) { +					log.debug("Use EntityId: " + entityId + " instead of explicite metadataURL ... "); +					metadataURL = entityId; +					 +				}				 +				String trustStoreUrl = FileUtils.makeAbsoluteURL( +						spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_TRUSTSTORE),  +						authConfig.getConfigurationRootDirectory()); +				String trustStorePassword = spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_TRUSTSTORE_PASSWORD); + +				return createNewSimpleMetadataProvider(metadataURL, 								  +						buildMetadataFilterChain(spConfig, metadataURL, trustStoreUrl, trustStorePassword),  +						spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_UNIQUEIDENTIFIER), +						getTimer(), +						new BasicParserPool(), +						createHttpClient(metadataURL)); +				 +			} catch (PVP2MetadataException e) { +				log.info("Can NOT initialize Metadata signature-verification filter. Reason: " + e.getMessage()); +				throw new EAAFConfigurationException( +						"Can NOT initialize Metadata signature-verification filter. Reason: " + e.getMessage(), e); +				 +			}  		} else  			log.info("No ServiceProvider with entityId: " + entityId + " in configuration."); @@ -77,14 +98,15 @@ public class PVPMetadataProvider extends AbstractChainingMetadataProvider{  	} -	private MetadataFilterChain buildMetadataFilterChain(ISPConfiguration oaParam, String metadataURL, String trustStoreUrl) throws CertificateException{ +	private MetadataFilterChain buildMetadataFilterChain(ISPConfiguration oaParam, String metadataURL, String trustStoreUrl, String trustStorePassword) throws CertificateException, PVP2MetadataException{  		MetadataFilterChain filterChain = new MetadataFilterChain();		  		filterChain.getFilters().add(new SchemaValidationFilter(  				basicConfig.getBasicMOAIDConfigurationBoolean(MSeIDASNodeConstants.PROP_CONFIG_PVP_SCHEME_VALIDATION, true))); +					 +		filterChain.getFilters().add( +				new MetadataSignatureVerificationFilter( +						trustStoreUrl, trustStorePassword, metadataURL)); -		//TODO: add signature validation filter -		 -		  		filterChain.getFilters().add(new PVPEntityCategoryFilter(  				basicConfig.getBasicMOAIDConfigurationBoolean(MSeIDASNodeConstants.PROP_CONFIG_PVP_ENABLE_ENTITYCATEGORIES, true))); diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/AuthnRequestValidator.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/AuthnRequestValidator.java new file mode 100644 index 00000000..1b912ed4 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/AuthnRequestValidator.java @@ -0,0 +1,207 @@ +package at.gv.egiz.eidas.specific.connector.verification; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.opensaml.saml2.core.AuthnContextClassRef; +import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.NameID; +import org.opensaml.saml2.core.NameIDPolicy; +import org.opensaml.saml2.core.RequestedAuthnContext; +import org.opensaml.saml2.core.Scoping; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.opensaml.xml.XMLObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.data.PVPAttributeDefinitions; +import at.gv.egiz.eaaf.core.exceptions.AuthnRequestValidatorException; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.EAAFStorageException; +import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EAAFRequestedAttribute; +import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EAAFRequestedAttributes; +import at.gv.egiz.eaaf.modules.pvp2.api.validation.IAuthnRequestValidator; +import at.gv.egiz.eaaf.modules.pvp2.exception.NameIDFormatNotSupportedException; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; +import at.gv.egiz.eidas.specific.connector.config.ServiceProviderConfiguration; + +public class AuthnRequestValidator implements IAuthnRequestValidator { + +	private static final Logger log = LoggerFactory.getLogger(AuthnRequestValidator.class); +	 +	@Override +	public void validate(HttpServletRequest httpReq, IRequest pendingReq, AuthnRequest authnReq, +			SPSSODescriptor spSSODescriptor) throws AuthnRequestValidatorException { +		try {		 +			//validate NameIDPolicy +			NameIDPolicy nameIDPolicy = authnReq.getNameIDPolicy(); +			if (nameIDPolicy != null) { +				String nameIDFormat = nameIDPolicy.getFormat(); +				if (nameIDFormat != null) { +					if ( !(NameID.TRANSIENT.equals(nameIDFormat) || +							NameID.PERSISTENT.equals(nameIDFormat)) ) { +						  +						throw new NameIDFormatNotSupportedException(nameIDFormat); +						 +					} +					 +				} else +					log.trace("Find NameIDPolicy, but NameIDFormat is 'null'");							 +			} else +				log.trace("AuthnRequest includes no 'NameIDPolicy'"); +	 +	 +			//post-process RequesterId +			String spEntityId = extractScopeRequsterId(authnReq); +			if (StringUtils.isEmpty(spEntityId)) { +				log.info("NO service-provider entityID in Authn. request. Stop authn. process ... "); +				throw new AuthnRequestValidatorException("TODO", null,  +						"NO service-provider entityID in Authn. request", pendingReq); +				 +			} else +				pendingReq.setGenericDataToSession(MSeIDASNodeConstants.DATA_REQUESTERID, spEntityId); +															 +				 +			//post-process ProviderName +			String providerName = authnReq.getProviderName();	 +			if (StringUtils.isEmpty(providerName)) +				log.info("Authn. request contains NO SP friendlyName"); +			else +				pendingReq.setGenericDataToSession(MSeIDASNodeConstants.DATA_PROVIDERNAME, spEntityId); +			 +			//TODO: set to SPConfiguration +			//post-process requested LoA +			List<String> reqLoA = extractLoA(authnReq); +			pendingReq.setGenericDataToSession(MSeIDASNodeConstants.DATA_REQUESTED_LOA_LIST, reqLoA); +			 +			//TODO: set to SPConfiguration +			//post-process requested LoA comparison-level +			String reqLoAComperison = extractComparisonLevel(authnReq); +			pendingReq.setGenericDataToSession(MSeIDASNodeConstants.DATA_REQUESTED_LOA_COMPERISON, reqLoAComperison); +			 +			//validate and process requested attributes +			boolean sectorDetected = false; +			List<XMLObject> requestedAttributes = authnReq.getExtensions().getUnknownXMLObjects(); +			for (XMLObject reqAttrObj : requestedAttributes) { +				if (reqAttrObj instanceof EAAFRequestedAttributes) { +					EAAFRequestedAttributes reqAttr = (EAAFRequestedAttributes)reqAttrObj; +					if (reqAttr.getAttributes() != null && reqAttr.getAttributes().size() != 0 ) { +						for (EAAFRequestedAttribute el : reqAttr.getAttributes()) { +							log.trace("Processing req. attribute '" + el.getName() + "' ... "); +							if (el.getName().equals(PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME)) { +								if (el.getAttributeValues() != null && el.getAttributeValues().size() == 1) { +									String sectorId = el.getAttributeValues().get(0).getDOM().getTextContent(); +									ServiceProviderConfiguration spConfig = pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class); +									 +									try {																								 +										spConfig.setbPKTargetIdentifier(sectorId); +										sectorDetected = true; +										 +									} catch (EAAFException e) { +										log.info("Requested sector: " + sectorId + " DOES NOT match to allowed sectors for SP: " + spConfig.getUniqueIdentifier()); +									} +									 +								} else  +									log.info("Req. attribute '" +  el.getName() + "' contains NO or MORE THEN ONE attribute-values. Ignore full req. attribute"); +								 +							} else +								log.debug("Ignore req. attribute: " + el.getName()); +							 +						} +											 +					} else  +						log.debug("No requested Attributes in Authn. Request"); +					 +				} else +					log.info("Ignore unknown requested attribute: " + reqAttrObj.getElementQName().toString()); +				 +			} +			 +			if (!sectorDetected) { +				log.info("Authn.Req validation FAILED. Reason: Contains NO or NO VALID target-sector information."); +				throw new AuthnRequestValidatorException("TODO", null,  +						"Authn.Req validation FAILED. Reason: Contains NO or NO VALID target-sector information."); +				 +			} +					 +		} catch (EAAFStorageException e) { +			log.info("Can NOT store Authn. Req. data into pendingRequest." , e); +			throw new AuthnRequestValidatorException("TODO", null,  +					"Can NOT store Authn. Req. data into pendingRequest.", e); +			 +		} +				 +	} + +	private String extractComparisonLevel(AuthnRequest authnReq) { +		if (authnReq.getRequestedAuthnContext() != null) { +			RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); +			return authContext.getComparison().toString(); +			 +		} +		 +		return null; +	} + +	private List<String> extractLoA(AuthnRequest authnReq) throws AuthnRequestValidatorException { +		List<String> result = new ArrayList<String>(); +		if (authnReq.getRequestedAuthnContext() != null) { +			RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); +			if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.MINIMUM)) { +				if (authContext.getAuthnContextClassRefs().isEmpty())  { +					log.debug("Authn. Req. contains no requested LoA"); +					 +				} else if (authContext.getAuthnContextClassRefs().size() > 1) { +					log.info("Authn. Req. contains MORE THAN ONE requested LoA, but "  +							+ AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one" ); +					throw new AuthnRequestValidatorException("TODO", null,  +							"Authn. Req. contains MORE THAN ONE requested LoA, but "  +									+ AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one"); +					 +				} else +					result.add(authContext.getAuthnContextClassRefs().get(0).getAuthnContextClassRef()); +				 +			} else if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.EXACT)) { +				for (AuthnContextClassRef el : authContext.getAuthnContextClassRefs()) +					result.add(el.getAuthnContextClassRef()); +				 +			} else {  +				log.info("Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '"  +						+ AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"); +				throw new AuthnRequestValidatorException("TODO", null,  +						"Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '"  +								+ AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"); +				 +			} +							 +		} +		 +		return result; +	} + +	private String extractScopeRequsterId(AuthnRequest authnReq) { +		if (authnReq.getScoping() != null) { +			Scoping scoping = authnReq.getScoping(); +			if (scoping.getRequesterIDs() != null &&  +					scoping.getRequesterIDs().size() > 0) { +				if (scoping.getRequesterIDs().size() == 1) +					return scoping.getRequesterIDs().get(0).getRequesterID(); +				 +				else { +					log.info("Authn. request contains more than on RequesterIDs! Only use first one"); +					return scoping.getRequesterIDs().get(0).getRequesterID(); +					 +				}					 +			}			 +		} +		 +		return null; +	} +	 + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java index ed88091b..d7d75f90 100644 --- a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java @@ -61,7 +61,8 @@ public class MetadataSignatureVerificationFilter extends AbstractMetadataSignatu  				} -			} +			} else +				throw new PVP2MetadataException("Can not open trustStore: " + trustStorePath + " for metadata: " + metadataURL, null);  		} catch (KeyStoreException | IOException e) {  			log.warn("Can not open trustStore: " + trustStorePath + " for metadata: " + metadataURL + " Reason: " + e.getMessage(), e); | 
