From a2baf085fcd3a1940585beb3f4a8acb2e4e5a461 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 8 Feb 2022 11:40:01 +0100 Subject: refactor(matching): move AddresssucheController into eIDAS module to reuse data-model for tasks --- .../eidas/v2/controller/AdresssucheController.java | 258 +++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/controller/AdresssucheController.java (limited to 'eidas_modules/authmodule-eIDAS-v2') diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/controller/AdresssucheController.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/controller/AdresssucheController.java new file mode 100644 index 00000000..8505f5d5 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/controller/AdresssucheController.java @@ -0,0 +1,258 @@ +/* + * Copyright 2018 A-SIT Plus GmbH + * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, + * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "License"); + * You may not use this work except in compliance with the License. + * You may obtain a copy of the License at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * 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. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + +package at.asitplus.eidas.specific.modules.auth.eidas.v2.controller; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.CompareToBuilder; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; +import at.asitplus.eidas.specific.connector.gui.StaticGuiBuilderConfiguration; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr.ZmrAddressSoapClient; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException; +import at.gv.bmi.namespace.zmr_su.zrm._20040201_.address.Adressdaten; +import at.gv.e_government.reference.namespace.persondata.de._20040201.PostAdresseTyp; +import at.gv.e_government.reference.namespace.persondata.de._20040201.ZustelladresseTyp; +import at.gv.egiz.eaaf.core.api.data.EaafConstants; +import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.GuiBuildException; +import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +/** + * Default process-engine signaling controller. + * + * @author tlenz + */ +@Controller +@Slf4j +public class AdresssucheController { + + public static final String PARAM_POSTLEITZAHL = "postleitzahl"; + public static final String PARAM_MUNIPICALITY = "municipality"; + public static final String PARAM_VILLAGE = "village"; + public static final String PARAM_STREET = "street"; + public static final String PARAM_NUMBER = "number"; + + @Autowired + private ISpringMvcGuiFormBuilder guiBuilder; + + @Autowired + private IConfiguration basicConfig; + + @Autowired + private ResourceLoader resourceLoader; + + @Autowired + private ZmrAddressSoapClient client; + + @Autowired + private IPendingRequestIdGenerationStrategy pendingReqGeneration; + + /** + * Show the "residency.html" directly. + * TODO Remove this after testing. + */ + @RequestMapping(value = {"/test"}, method = {RequestMethod.GET}) + public void test(HttpServletRequest request, HttpServletResponse response) throws GuiBuildException, EaafException { + final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration( + basicConfig, + "http://localhost:8080/ms_connector/", + basicConfig.getBasicConfiguration(//TODO + MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_TEMPLATES_RESIDENCY, + MsEidasNodeConstants.TEMPLATE_HTML_RESIDENCY), + MsEidasNodeConstants.ENDPOINT_RESIDENCY_INPUT, + resourceLoader); + config.putCustomParameter(null, "pendingid", pendingReqGeneration.generateExternalPendingRequestId()); + guiBuilder.build(request, response, config, "Query Austrian residency"); + } + + /** + * Show the "other_login_method.html" directly. + * TODO Remove this after testing. + */ + @RequestMapping(value = {"/olm"}, method = {RequestMethod.GET}) + public void otherloginmethod(HttpServletRequest request, HttpServletResponse response) throws GuiBuildException, + EaafException { + final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration( + basicConfig, + "http://localhost:8080/ms_connector/", + basicConfig.getBasicConfiguration(//TODO + MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_TEMPLATES_OTHER_LOGIN_METHOD_SELECTION, + MsEidasNodeConstants.TEMPLATE_HTML_OTHERLOGINMETHODS), + MsEidasNodeConstants.ENDPOINT_OTHER_LOGIN_METHOD_SELECTION, + resourceLoader); + config.putCustomParameter(null, "pendingid", pendingReqGeneration.generateExternalPendingRequestId()); + guiBuilder.build(request, response, config, "Other Login Method"); + } + + /** + * Show the "country_selection.html" directly. + * TODO Remove this after testing. + */ + @RequestMapping(value = {"/country"}, method = {RequestMethod.GET}) + public void countryselection(HttpServletRequest request, HttpServletResponse response) throws GuiBuildException, + EaafException { + final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration( + basicConfig, + "http://localhost:8080/ms_connector/", + basicConfig.getBasicConfiguration(//TODO + MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_TEMPLATES_CCSELECTION, + MsEidasNodeConstants.TEMPLATE_HTML_COUNTRYSELECTION), + MsEidasNodeConstants.ENDPOINT_COUNTRYSELECTION, + resourceLoader); + config.putCustomParameter(null, "pendingid", pendingReqGeneration.generateExternalPendingRequestId()); + guiBuilder.build(request, response, config, "Country Selection"); + } + + /** + * Performs search for addresses in ZMR. + */ + @RequestMapping(value = {"/residency/search"}, method = {RequestMethod.POST}) + public ResponseEntity search( + @RequestParam(PARAM_POSTLEITZAHL) String postleitzahl, + @RequestParam(PARAM_MUNIPICALITY) String municipality, + @RequestParam(PARAM_VILLAGE) String village, + @RequestParam(PARAM_STREET) String street, + @RequestParam(PARAM_NUMBER) String number, + @RequestParam(EaafConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID) String pendingId) { + log.info("Search with '{}', '{}', '{}', '{}', '{}'", + postleitzahl.replaceAll("[\r\n]", ""), + municipality.replaceAll("[\r\n]", ""), + village.replaceAll("[\r\n]", ""), + street.replaceAll("[\r\n]", ""), + number.replaceAll("[\r\n]", "")); + try { + pendingReqGeneration.validateAndGetPendingRequestId(pendingId); + } catch (PendingReqIdValidationException e) { + log.warn("Search with pendingId '{}' is not valid", pendingId.replaceAll("[\r\n]", "")); + return ResponseEntity.badRequest().build(); + } + try { + Adressdaten searchInput = buildSearchInput(postleitzahl, municipality, village, street, number); + ZmrAddressSoapClient.AddressInfo searchOutput = client.searchAddress(searchInput); + AdresssucheResult output = buildResponse(searchOutput); + return ResponseEntity.ok(output); + } catch (EidasSAuthenticationException e) { + log.warn("Search failed", e); + return ResponseEntity.badRequest().build(); + } + } + + private AdresssucheResult buildResponse(ZmrAddressSoapClient.AddressInfo searchOutput) { + if (searchOutput.getPersonResult().isEmpty()) { + log.warn("No result from ZMR"); + return new AdresssucheResult(Collections.emptyList(), 0); + } + log.info("Result level is {}", searchOutput.getLevel()); + Set result = searchOutput.getPersonResult().stream() + .map(Adressdaten::getPostAdresse) + .map(it -> new AdresssucheOutput(it.getPostleitzahl(), it.getGemeinde(), it.getOrtschaft(), + it.getZustelladresse().getStrassenname(), it.getZustelladresse().getOrientierungsnummer())) + .collect(Collectors.toSet()); + // TODO Add configuration option for the limit of 30 + List sorted = result.stream().sorted().limit(30).collect(Collectors.toList()); + return new AdresssucheResult(sorted, result.size()); + } + + private Adressdaten buildSearchInput(String postleitzahl, + String municipality, + String village, + String street, + String number) { + PostAdresseTyp postAdresse = new PostAdresseTyp(); + if (StringUtils.isNotBlank(postleitzahl)) { + postAdresse.setPostleitzahl(postleitzahl); + } + if (StringUtils.isNotBlank(municipality)) { + postAdresse.setGemeinde(municipality); + } + if (StringUtils.isNotBlank(village)) { + postAdresse.setOrtschaft(village); + } + if (StringUtils.isNotBlank(street) || StringUtils.isNotBlank(number)) { + ZustelladresseTyp zustelladresse = new ZustelladresseTyp(); + if (StringUtils.isNotBlank(street)) { + zustelladresse.setStrassenname(street); + } + if (StringUtils.isNotBlank(number)) { + zustelladresse.setOrientierungsnummer(number); + } + postAdresse.setZustelladresse(zustelladresse); + } + Adressdaten searchInput = new Adressdaten(); + searchInput.setPostAdresse(postAdresse); + return searchInput; + } + + @Data + @AllArgsConstructor + public static class AdresssucheResult { + private final Collection results; + private final int resultCount; + } + + @Data + @AllArgsConstructor + public static class AdresssucheOutput implements Comparable { + private final String postleitzahl; + private final String municipality; + private final String village; + private final String street; + private final String number; + + @Override + public int compareTo(@NotNull AdresssucheOutput o) { + return new CompareToBuilder() + .append(this.postleitzahl, o.postleitzahl) + .append(this.municipality, o.municipality) + .append(this.village, o.village) + .append(this.street, o.street) + .append(this.number, o.number) + .toComparison(); + } + } + +} -- cgit v1.2.3