package at.gv.egovernment.moa.id.auth.modules.ehvd.service; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; import javax.xml.ws.soap.SOAPFaultException; import org.apache.commons.lang3.StringUtils; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transports.http.configuration.ProxyServerType; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.collect.Sets; import at.gv.egiz.eaaf.core.api.data.PVPAttributeDefinitions; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; import at.gv.egiz.eaaf.core.exceptions.EAAFBuilderException; import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.idp.auth.builder.BPKBuilder; import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.modules.ehvd.ConfigurationProperties; import at.gv.egovernment.moa.id.auth.modules.ehvd.client.wsdl.EHVD; import at.gv.egovernment.moa.id.auth.modules.ehvd.client.wsdl.EHVDService; import at.gv.egovernment.moa.id.auth.modules.ehvd.client.wsdl.GdaDescriptor; import at.gv.egovernment.moa.id.auth.modules.ehvd.client.wsdl.GdaIndexResponse; import at.gv.egovernment.moa.id.auth.modules.ehvd.client.wsdl.GetGdaDescriptors; import at.gv.egovernment.moa.id.auth.modules.ehvd.client.wsdl.InstanceIdentifier; import at.gv.egovernment.moa.id.auth.modules.ehvd.exception.EhvdException; import at.gv.egovernment.moa.logging.Logger; import at.gv.util.LoggingHandler; /** * Implement interaction with EHVD service to get GDA information. * * @author tlenz * */ public class EhvdCommunicationService implements IEhvdCommunication { private static final String GDA_RESP_STATUS_ACTIVE = "Aktiv"; private static final String ERROR_EHVD_00 = "ehvd.00"; private static final String ERROR_EHVD_01 = "ehvd.01"; private static final String ERROR_EHVD_02 = "ehvd.02"; private static final String ERROR_EHVD_03 = "ehvd.03"; private static final String ERROR_EHVD_04 = "ehvd.04"; private static final String ERROR_CONFIG_05 = "config.05"; private static final Set SERVICE_ERRORS_LOG_INFO = Sets.newHashSet("6002"); @Autowired IConfiguration config; private String ehvdBpkTarget; private EHVD ehvdClient; private Pattern ehvdRolePattern; private List ehvhPvpRoleList; /** * Get user's GDA roles from EHVD Service. * * @param identityLink IdentityLink of the user * @return {@link List} of Roles that are received from EHVD * @throws AuthenticationException In case of an EHVD communication error * @throws EAAFBuilderException In case of a bPK generation error */ @Override @Nonnull public EhvdResponseHolder getRoles(IIdentityLink identityLink) throws AuthenticationException, EAAFBuilderException { // get bPK for EHVD request final Pair ehvdBpk = BPKBuilder.generateAreaSpecificPersonIdentifier( identityLink.getIdentificationValue(), identityLink.getIdentificationType(), ehvdBpkTarget); // request EHVD and handle errors final GdaIndexResponse gdaResp = requestingGda(ehvdBpk.getFirst()); // parse roles from response return EhvdResponseHolder.getInstance(gdaResp.getGda(), parseGdaResponse(gdaResp)); } @Nonnull private GdaIndexResponse requestingGda(String bpk) throws EhvdException { try { final GetGdaDescriptors gdaReq = buildGdaRequest(bpk); Logger.debug("Requesting EHVD to get GDA status ... "); final GdaIndexResponse gdaResp = ehvdClient.getGDA(gdaReq); Logger.debug("Receive GDA status. Starting response validation ... "); return gdaResp; } catch (final SOAPFaultException e) { throw handleSoapFaultError(e); } catch (final Exception e) { Logger.error("EHVD communication failed with generic error: " + e.getMessage(), e); throw new EhvdException(ERROR_EHVD_01, new Object[] {}, e); } } private EhvdException handleSoapFaultError(SOAPFaultException e) { // extract reason for this error final String errorMsg = e.getFault() != null ? StringUtils.isNotEmpty(e.getFault().getFaultString()) ? e.getFault().getFaultString() : e.getMessage() : e.getMessage(); if (SERVICE_ERRORS_LOG_INFO.stream() .filter(el -> errorMsg.contains(el)) .findFirst() .isPresent()) { Logger.info("EHVD communication failed with SOAP response: " + errorMsg); return new EhvdException(ERROR_EHVD_03, new Object[] { errorMsg }); } else { Logger.warn("EHVD communication failed with SOAP response: " + errorMsg, e); return new EhvdException(ERROR_EHVD_02, new Object[] { errorMsg }); } } private List parseGdaResponse(GdaIndexResponse ehvdResp) throws EhvdException { if (ehvdResp.getGda() != null) { final GdaDescriptor gdaInfo = ehvdResp.getGda(); if (GDA_RESP_STATUS_ACTIVE.equals(gdaInfo.getStatus().getEhvdstatus())) { Logger.debug("Find #" + gdaInfo.getRoles().getRole().size() + " roles"); // match roles with regex from configuration final Optional validGdaRole = gdaInfo.getRoles().getRole().stream() .filter(el -> matchGdaRole(el)) .findFirst(); if (validGdaRole.isPresent()) { Logger.info("Find valid GDA role: " + validGdaRole.get() + " Set PVP Role: " + StringUtils.join(ehvhPvpRoleList, ",") + " into Session"); // set role into response return ehvhPvpRoleList; } else { Logger.info("No valid GDA role in EHVD response"); throw new EhvdException(ERROR_EHVD_04, null); } } else { Logger.info("GDA is marked as 'inactive'. Stopping process with an error ... "); throw new EhvdException(ERROR_EHVD_00, null); } } else { Logger.info("Receive empty GDA response"); throw new EhvdException(ERROR_EHVD_03, new Object[] {}); } } private boolean matchGdaRole(String role) { final Matcher matcher = ehvdRolePattern.matcher(role); final boolean matches = matcher.matches(); Logger.trace(matches ? "EHVD role: " + role + " matches" : "EHVD role: " + role + " does not matche to pattern: " + matcher.toString()); return matches; } private GetGdaDescriptors buildGdaRequest(String bPK) { final GetGdaDescriptors req = new GetGdaDescriptors(); final InstanceIdentifier gdaIdentifier = new InstanceIdentifier(); gdaIdentifier.setOidIssuingAuthority(PVPAttributeDefinitions.BPK_OID); gdaIdentifier.setId(bPK); req.setHcIdentifier(gdaIdentifier); return req; } @PostConstruct private void initialize() throws EAAFConfigurationException { if (config.getBasicConfigurationBoolean(ConfigurationProperties.PROP_MODULE_ENABLED, false)) { initializeEhvdClient(); // load EHVD bPK target ehvdBpkTarget = config.getBasicConfiguration( ConfigurationProperties.PROP_MODULE_SERVICE_TARGET, ConfigurationProperties.DEFAULT_EHVD_SERVICE_TARGET); Logger.info("Set-up EHVD Client with bPK target: " + ehvdBpkTarget); // load Regex to match EHVD Roles to PVP Roles final String ehvdRoleRegex = config.getBasicConfiguration( ConfigurationProperties.PROP_MODULE_EHVD_ROLE_REGEX); checkConfigPropertyNotNull(ehvdRoleRegex, ConfigurationProperties.PROP_MODULE_EHVD_ROLE_REGEX); ehvdRolePattern = Pattern.compile(ehvdRoleRegex); Logger.info("Set-up EHVD Client with Role regex: " + ehvdRolePattern.toString()); // load PVP Roles for EHVD integration final String ehvdPvpRole = config.getBasicConfiguration( ConfigurationProperties.PROP_MODULE_PVP_ROLE); checkConfigPropertyNotNull(ehvdPvpRole, ConfigurationProperties.PROP_MODULE_PVP_ROLE); ehvhPvpRoleList = KeyValueUtils.getListOfCSVValues(ehvdPvpRole); Logger.info("Set-up EHVD module with PVP Role: " + StringUtils.join(ehvhPvpRoleList, ",")); } else { Logger.info("Skipping EHVD client because it's not active"); } } private void checkConfigPropertyNotNull(String valueToCheck, String configPropName) throws EAAFConfigurationException { if (StringUtils.isEmpty(valueToCheck)) { Logger.error("Missing configuration for EHVD module. " + "(Property: " + configPropName + ")"); throw new EAAFConfigurationException(ERROR_CONFIG_05, new Object[] { configPropName }); } } private void initializeEhvdClient() throws EAAFConfigurationException { Logger.debug("Initializing EHVD client ... "); final URL url = EhvdCommunicationService.class.getResource("/wsdl/eHVD.wsdl"); final EHVDService service = new EHVDService(url); ehvdClient = service.getEHVDPort12(); // load service end-point URL from configuration final String ehvdEndpointUrl = config.getBasicConfiguration( ConfigurationProperties.PROP_MODULE_SERVICE_ENDPOINT); if (StringUtils.isEmpty(ehvdEndpointUrl)) { Logger.error("Missing configuration for EHVD WebService endpoint. " + "(Property: " + ConfigurationProperties.PROP_MODULE_SERVICE_ENDPOINT + ")"); throw new EAAFConfigurationException(ERROR_CONFIG_05, new Object[] { ConfigurationProperties.PROP_MODULE_SERVICE_ENDPOINT }); } // inject service end-point URL final Map requestContext = ((BindingProvider) ehvdClient).getRequestContext(); requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, ehvdEndpointUrl); // inject Logging handler List handlerList = ((BindingProvider) ehvdClient).getBinding().getHandlerChain(); if (handlerList == null) { handlerList = new ArrayList<>(); } handlerList.add(new LoggingHandler()); ((BindingProvider) ehvdClient).getBinding().setHandlerChain(handlerList); Logger.info("Initialize EHVD Client with service end-point: " + ehvdEndpointUrl); // these code is only for local testing final String socksPort = config.getBasicConfiguration( ConfigurationProperties.PROP_MODULE_PROXY_SOCKS_PORT); if (StringUtils.isNotEmpty(socksPort)) { Logger.warn("Injecting SOCKS5 Proxy for service communication!"); final Client client = ClientProxy.getClient(ehvdClient); final HTTPConduit http = (HTTPConduit) client.getConduit(); http.getClient().setProxyServerType(ProxyServerType.SOCKS); http.getClient().setProxyServer("127.0.0.1"); http.getClient().setProxyServerPort(Integer.valueOf(socksPort)); } } public static class EhvdResponseHolder { final List roles; final GdaDescriptor fullGdaResponse; public static EhvdResponseHolder getInstance(GdaDescriptor gdaInfo, List processedRoles) { return new EhvdResponseHolder(gdaInfo, processedRoles); } private EhvdResponseHolder(GdaDescriptor gdaInfo, List processedRoles) { this.roles = processedRoles; this.fullGdaResponse = gdaInfo; } public List getRoles() { return roles; } public GdaDescriptor getFullGdaResponse() { return fullGdaResponse; } } }