Java tutorial
/* * * Copyright 2016 Simon Gfeller * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package ch.simuonline.idh.attribute.resolver.spring.dc.aq.impl; import java.io.FileInputStream; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.List; import javax.annotation.Nonnull; import javax.xml.namespace.QName; import org.opensaml.security.crypto.KeySupport; import org.opensaml.security.x509.X509Support; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; import ch.simuonline.idh.attribute.resolver.dc.aq.AQAttribute; import ch.simuonline.idh.attribute.resolver.dc.aq.AttributeQueryBuilder; import ch.simuonline.idh.attribute.resolver.dc.aq.AttributeQueryDataConnector; import ch.simuonline.idh.attribute.resolver.dc.aq.AttributeQueryKeyManager; import ch.simuonline.idh.attribute.resolver.dc.aq.LDAPTargetDeterminationStrategy; import ch.simuonline.idh.attribute.resolver.dc.aq.MySQLTargetDeterminationStrategy; import net.shibboleth.ext.spring.util.SpringSupport; import net.shibboleth.idp.attribute.resolver.spring.ResolverPluginDependencyParser; import net.shibboleth.idp.attribute.resolver.spring.dc.impl.AbstractDataConnectorParser; import net.shibboleth.utilities.java.support.logic.Constraint; import net.shibboleth.utilities.java.support.primitive.StringSupport; import net.shibboleth.utilities.java.support.xml.AttributeSupport; import net.shibboleth.utilities.java.support.xml.ElementSupport; /** * * Parser for the {@link AttributeQueryDataConnector}. * Visit {@link https://github.com/gfels4/identity_hub} for more informations. * * @author Simon Gfeller * */ public class AttributeQueryDataConnectorParser extends AbstractDataConnectorParser { /** Schema type name. */ @Nonnull public static final QName TYPE_NAME = new QName(AttributeQueryDataConnectorNamespaceHandler.NAMESPACE, "AttributeQuery"); /** Local name of attribute. */ @Nonnull public static final QName ATTRIBUTE_ELEMENT_NAME = new QName( AttributeQueryDataConnectorNamespaceHandler.NAMESPACE, "Attribute"); /** Class logger. */ @Nonnull private final Logger log = LoggerFactory.getLogger(AttributeQueryDataConnectorParser.class); /** {@inheritDoc} */ @Override protected Class<AttributeQueryDataConnector> getNativeBeanClass() { return AttributeQueryDataConnector.class; } /** {@inheritDoc} */ @Override protected void doV2Parse(@Nonnull final Element config, @Nonnull final ParserContext parserContext, @Nonnull final BeanDefinitionBuilder builder) { log.debug("{} Parsing v2 configuration {}", getLogPrefix(), config); final String targetResolvingStrategy = AttributeSupport.getAttributeValue(config, new QName("targetDeterminationStrategy")); Constraint.isNotNull(StringSupport.trimOrNull(targetResolvingStrategy), "The targetDeterminationStrategy can not be null or empty, please adjust entityID from the AQ DataConnector"); if (targetResolvingStrategy.equals("mysql")) { // Constructor is MySQLTargetResolvingStrategy(String url, String username, String password), adding arguments in this order: final BeanDefinitionBuilder mysqlTargetResolvingStrategy = BeanDefinitionBuilder .genericBeanDefinition(MySQLTargetDeterminationStrategy.class); final String dbURL = AttributeSupport.getAttributeValue(config, new QName("dbURL")); Constraint.isNotNull(StringSupport.trimOrNull(dbURL), "The dbURL attribute is required if the targetResolvingStrategy is mysql, please adjust entityID from the AQ DataConnector"); mysqlTargetResolvingStrategy.addConstructorArgValue(dbURL); final String dbUsername = AttributeSupport.getAttributeValue(config, new QName("dbUsername")); Constraint.isNotNull(StringSupport.trimOrNull(dbUsername), "The dbUsername attribute is required if the targetResolvingStrategy is mysql, please adjust entityID from the AQ DataConnector"); mysqlTargetResolvingStrategy.addConstructorArgValue(dbUsername); final String dbPassword = AttributeSupport.getAttributeValue(config, new QName("dbPassword")); Constraint.isNotNull(StringSupport.trimOrNull(dbPassword), "The dbPassword attribute is required if the targetResolvingStrategy is mysql, please adjust entityID from the AQ DataConnector"); mysqlTargetResolvingStrategy.addConstructorArgValue(dbPassword); builder.addPropertyValue("targetResolvingStrategy", mysqlTargetResolvingStrategy.getBeanDefinition()); } else if (targetResolvingStrategy.equals("ldap")) { final BeanDefinitionBuilder ldapTargetResolvingStrategy = BeanDefinitionBuilder .genericBeanDefinition(LDAPTargetDeterminationStrategy.class); final String sourceAttributeID = AttributeSupport.getAttributeValue(config, new QName("sourceAttributeID")); Constraint.isNotNull(StringSupport.trimOrNull(sourceAttributeID), "The sourceAttributeID attribute is required if the targetResolvingStrategy is ldap, please adjust entityID from the AQ DataConnector"); ldapTargetResolvingStrategy.addConstructorArgValue(sourceAttributeID); final List<Element> dependencyElements = ElementSupport.getChildElements(config, ResolverPluginDependencyParser.ELEMENT_NAME); ldapTargetResolvingStrategy.addPropertyValue("dependencies", SpringSupport.parseCustomElements(dependencyElements, parserContext)); final String connectorID = AttributeSupport.getAttributeValue(config, new QName("id")); Constraint.isNotNull(StringSupport.trimOrNull(sourceAttributeID), "The connectorID can not be empty, please adjust it for the AQ DataConnector"); ldapTargetResolvingStrategy.addConstructorArgValue(connectorID); builder.addPropertyValue("targetResolvingStrategy", ldapTargetResolvingStrategy.getBeanDefinition()); } else { log.error("{} Unsupported targetResolvingStrategy: {}. Change it to mysql or ldap! ", getLogPrefix(), targetResolvingStrategy); } final BeanDefinitionBuilder attributeQueryBuilder = BeanDefinitionBuilder .genericBeanDefinition(AttributeQueryBuilder.class); // Parse value of the entityID attribute final String issuer = AttributeSupport.getAttributeValue(config, new QName("entityID")); Constraint.isNotNull(StringSupport.trimOrNull(issuer), "The entityID of the Issuer can not be empty, please adjust entityID from the AQ DataConnector"); attributeQueryBuilder.addConstructorArgValue(issuer); // parsing of the defined AQAttributes for the attribute query final List<Element> children = ElementSupport.getChildElements(config, ATTRIBUTE_ELEMENT_NAME); final List<BeanDefinition> attributes = new ManagedList<>(children.size()); for (final Element child : children) { final String name = AttributeSupport.getAttributeValue(child, new QName("name")); final String friendlyName = AttributeSupport.getAttributeValue(child, new QName("friendlyName")); final BeanDefinitionBuilder attribute = BeanDefinitionBuilder.genericBeanDefinition(AQAttribute.class); attribute.addConstructorArgValue(name); attribute.addConstructorArgValue(friendlyName); log.debug("{} Added one AQAttribute to the resolving List. Friendly Name {}, Name {}", getLogPrefix(), friendlyName, name); attributes.add(attribute.getBeanDefinition()); } attributeQueryBuilder.addConstructorArgValue(attributes); builder.addPropertyValue("attributeQueryBuilder", attributeQueryBuilder.getBeanDefinition()); final BeanDefinitionBuilder keyManager = BeanDefinitionBuilder .genericBeanDefinition(AttributeQueryKeyManager.class); // parse the keyLocaton attribute from the AQ DataCOnnector final String keyLocation = AttributeSupport.getAttributeValue(config, new QName("keyLocation")); Constraint.isNotNull(StringSupport.trimOrNull(keyLocation), "Key location can not be empty, please adjust keyLocation from the AQ DataConnector"); // parse the certLocaton attribute from the AQ DataCOnnector final String certLocation = AttributeSupport.getAttributeValue(config, new QName("certLocation")); Constraint.isNotNull(StringSupport.trimOrNull(certLocation), "Certificate location can not be empty, please adjust certLocation from the AQ DataConnector"); keyManager.addConstructorArgValue(getPrivateKey(keyLocation)); keyManager.addConstructorArgValue(getCertificate(certLocation)); builder.addPropertyValue("attributeQueryKeyManager", keyManager.getBeanDefinition()); // if the asertionSigned attribute is true, set the value to true final String signatureRequired = AttributeSupport.getAttributeValue(config, new QName("assertionSigned")); if (signatureRequired != null && signatureRequired.equals("true")) { builder.addPropertyValue("signatureRequired", Boolean.TRUE); } // if the requestedAttributesRequired attribute is true, set the value to true final String requireMetadataAttributes = AttributeSupport.getAttributeValue(config, new QName("requestedAttributesRequired")); if (requireMetadataAttributes != null && requireMetadataAttributes.equals("true")) { builder.addPropertyValue("requireMetadataAttributes", Boolean.TRUE); } builder.setInitMethodName("initialize"); builder.setDestroyMethodName("destroy"); } /** * * This method reads a certificate file and returns a {@link X509Certificate} * * @param certLocation the location of the certificate file * @return the created {@link X509Certificate} */ private X509Certificate getCertificate(String certLocation) { X509Certificate cert = null; try { FileInputStream fisCertificate = new FileInputStream(certLocation); cert = X509Support .decodeCertificate(StringSupport.inputStreamToString(fisCertificate, null).getBytes()); fisCertificate.close(); } catch (Exception e) { log.debug("{} Couldnt create the X509Certificate: {}", getLogPrefix(), e); return null; } return cert; } /** * * This method reads a private key file and returns a {@link PrivateKey} * * @param privateKeyLocation the location of the private key file * @return the created {@link PrivateKey} */ private PrivateKey getPrivateKey(String privateKeyLocation) { PrivateKey privateKey = null; try { FileInputStream fisPrivateKey = new FileInputStream(privateKeyLocation); privateKey = KeySupport .decodePrivateKey(StringSupport.inputStreamToString(fisPrivateKey, null).getBytes(), null); fisPrivateKey.close(); } catch (Exception e) { log.debug("{} Couldnt create the PrivateKey: {}", getLogPrefix(), e); return null; } return privateKey; } }