Skip to content

Commit

Permalink
Samler pip-klienter i egen rest-modul (#1406)
Browse files Browse the repository at this point in the history
* Samler pip-klienter i egen rest-modul

* Finpuss
  • Loading branch information
jolarsen authored Dec 15, 2024
1 parent f5c58a1 commit cc4be4f
Show file tree
Hide file tree
Showing 13 changed files with 633 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import no.nav.vedtak.felles.integrasjon.rest.RestRequest;

// Extend og annoter med endpoint+default og scopes/default + tokenConfig = AzureAD_CC
//@RestClientConfig(tokenConfig = TokenFlow.AZUREAD_CC, endpointProperty = "skjermet.person.rs.url", endpointDefault = "https://skjermede-personer-pip.intern.nav.no/skjermet",
//@RestClientConfig(tokenConfig = TokenFlow.AZUREAD_CC, endpointProperty = "skjermet.person.rs.url", endpointDefault = "https://skjermede-personer-pip.intern.nav.no",
// scopesProperty = "skjermet.person.rs.azure.scope", scopesDefault = "api://prod-gcp.nom.skjermede-personer-pip/.default")
public abstract class AbstractSkjermetPersonGCPKlient implements Skjerming {

Expand Down
13 changes: 13 additions & 0 deletions integrasjon/pip-klient/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# pip-klient

Samling av pip-klienter som brukes til å henter informasjon for tilgangskontroll og ruting av saker

## Hensikten

Tilby data til applikasjoner som implementerer tilgangskontrollvurderinger

## Integrasjoner

* PDL-PIP-API
* SKJERMET PERSON
* Muligens FP-sak
23 changes: 23 additions & 0 deletions integrasjon/pip-klient/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>no.nav.foreldrepenger.felles.integrasjon</groupId>
<artifactId>felles-integrasjon-pom</artifactId>
<version>0.0.0-SNAPSHOT</version>
</parent>
<artifactId>pip-klient</artifactId>
<packaging>jar</packaging>
<name>Felles :: Integrasjonsendepunkt - pip-klient</name>

<dependencies>
<dependency>
<groupId>no.nav.foreldrepenger.felles.integrasjon</groupId>
<artifactId>felles-integrasjon-rest-klient</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package no.nav.vedtak.felles.integrasjon.fpsakpip;

import java.net.URI;
import java.util.List;
import java.util.Objects;

import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.core.UriBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import no.nav.vedtak.felles.integrasjon.rest.RestClient;
import no.nav.vedtak.felles.integrasjon.rest.RestConfig;
import no.nav.vedtak.felles.integrasjon.rest.RestRequest;

/*
* Informasjon fra skjermingsløsningen til bruk for tilgangskontroll. Registrer klientapps hos nom
*/

// Extend og annoter med @RestClientConfig(tokenConfig = TokenFlow.AZUREAD_CC, application = FpApplication.FPSAK)
public abstract class AbstractForeldrepengerPipKlient implements ForeldrepengerPip {

private static final Logger LOG = LoggerFactory.getLogger(AbstractForeldrepengerPipKlient.class);

private static final String SAK_AKTØR_PATH = "/api/pip/aktoer-for-sak";

private final RestClient restClient;
private final RestConfig restConfig;

private final URI sakAktørerEndpoint;

protected AbstractForeldrepengerPipKlient() {
this(RestClient.client());
}

protected AbstractForeldrepengerPipKlient(RestClient restClient) {
this.restClient = restClient;
this.restConfig = RestConfig.forClient(this.getClass());
this.sakAktørerEndpoint = UriBuilder.fromUri(restConfig.fpContextPath()).path(SAK_AKTØR_PATH).build();
if (!restConfig.tokenConfig().isAzureAD()) {
throw new IllegalArgumentException("Utviklerfeil: klient må annoteres med Azure CC");
}
}


@Override
public List<ForeldrepengerPipAktørId> personerForSak(String saksnummer) {
if (saksnummer == null) {
return List.of();
}

var request = RestRequest.newPOSTJson(new SaksnummerDto(saksnummer), sakAktørerEndpoint, restConfig);

try {
return restClient.sendReturnList(request, ForeldrepengerPipAktørId.class);
} catch (Exception e) {
LOG.info("ForeldrepengerPip fikk feil", e);
}
return restClient.sendReturnList(request, ForeldrepengerPipAktørId.class);
}

public record SaksnummerDto(@NotNull String saksnummer) {
public SaksnummerDto {
Objects.requireNonNull(saksnummer, "saksnummer");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package no.nav.vedtak.felles.integrasjon.fpsakpip;

import java.util.List;

public interface ForeldrepengerPip {

List<ForeldrepengerPipAktørId> personerForSak(String fnr);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package no.nav.vedtak.felles.integrasjon.fpsakpip;

import java.io.Serializable;
import java.util.Objects;
import java.util.regex.Pattern;

import jakarta.validation.constraints.NotNull;

import com.fasterxml.jackson.annotation.JsonValue;

/**
* Id som genereres fra NAV Aktørregister.
* Denne iden benyttes til interne forhold i Nav og vil ikke endres f.eks. dersom bruker går fra DNR til FNR i Folkeregisteret.
*/
public class ForeldrepengerPipAktørId implements Serializable, Comparable<ForeldrepengerPipAktørId> {
private static final String VALID_REGEXP = "^\\d{13}$";

private static final Pattern VALID = Pattern.compile(VALID_REGEXP, Pattern.CASE_INSENSITIVE);

@JsonValue
@NotNull
@jakarta.validation.constraints.Pattern(regexp = VALID_REGEXP, message = "aktørId ${validatedValue} har ikke gyldig verdi (pattern '{regexp}')")
private String aktørId;

public ForeldrepengerPipAktørId(Long aktørId) {
Objects.requireNonNull(aktørId, "aktørId");
this.aktørId = validateAktørId(aktørId.toString());
}

public ForeldrepengerPipAktørId(String aktørId) {
this.aktørId = validateAktørId(aktørId);
}

private String validateAktørId(String aktørId) {
Objects.requireNonNull(aktørId, "aktørId");
if (!VALID.matcher(aktørId).matches()) {
// skal ikke skje, funksjonelle feilmeldinger håndteres ikke her.
throw new IllegalArgumentException("Ugyldig aktørId '" + aktørId + "', tillatt pattern: " + VALID_REGEXP);
}
return aktørId;
}

public String getVerdi() {
return aktørId;
}

@Override
public boolean equals(Object obj) {
return obj instanceof ForeldrepengerPipAktørId annen && Objects.equals(aktørId, annen.aktørId);
}

@Override
public int hashCode() {
return Objects.hash(aktørId);
}

@Override
public String toString() {
return getClass().getSimpleName() + "<maskert>";
}

@Override
public int compareTo(ForeldrepengerPipAktørId o) {
// TODO: Burde ikke finnes
return aktørId.compareTo(o.aktørId);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package no.nav.vedtak.felles.integrasjon.pdlpip;

import java.net.URI;
import java.time.Duration;
import java.util.List;
import java.util.Map;

import jakarta.ws.rs.core.UriBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import no.nav.vedtak.felles.integrasjon.rest.RestClient;
import no.nav.vedtak.felles.integrasjon.rest.RestConfig;
import no.nav.vedtak.felles.integrasjon.rest.RestRequest;

/*
* Informasjon fra PDL til bruk kun for tilgangskontroll. Registrer klientapps hos pdl
*
* PROD: SD innenfor FSS ellers https pdl-pip-api.intern.nav.no (scope: prod-fss:pdl:pdl-pip-api)
* DEV: SD innenfor FSS ellers https pdl-pip-api.dev.intern.nav.no (scope: dev-fss:pdl:pdl-pip-api)
*/

// Extend og annoter med endpoint+default og scopes/default + tokenConfig = AzureAD_CC
//@RestClientConfig(tokenConfig = TokenFlow.AZUREAD_CC, endpointProperty = "pdl.pip.base.url",
// endpointDefault = "http://pdl-pip-api.pdl/api/v1", // For FSS - apps i GCP bruker https://pdl-pip-api.intern.nav.no/api/v1
// scopesProperty = "pdl.pip.scope", scopesDefault = "api://prod-fss:pdl:pdl-pip-api/.default")
public abstract class AbstractPersondataPipKlient implements PersondataPip {

private static final Logger LOG = LoggerFactory.getLogger(AbstractPersondataPipKlient.class);
private static final String PERSON_PATH = "person";
private static final String PERSON_BOLK_PATH = "personBolk";

private final RestClient client;
private final RestConfig restConfig;
private final URI pipPersonBolkEndpoint;
private final URI pipPersonEndpoint;


protected AbstractPersondataPipKlient() {
this(RestClient.client());
}

protected AbstractPersondataPipKlient(RestClient restClient) {
this.client = restClient;
this.restConfig = RestConfig.forClient(this.getClass());
this.pipPersonEndpoint = UriBuilder.fromUri(restConfig.endpoint()).path(PERSON_PATH).build();
this.pipPersonBolkEndpoint = UriBuilder.fromUri(restConfig.endpoint()).path(PERSON_BOLK_PATH).build();
if (!restConfig.tokenConfig().isAzureAD()) {
throw new IllegalArgumentException("Utviklerfeil: klient må annoteres med Azure CC");
}
}

@Override
public PersondataPipDto hentTilgangPersondata(String ident) {
var request = RestRequest.newGET(pipPersonEndpoint, restConfig)
.header("ident", ident)
.timeout(Duration.ofSeconds(5));
try {
return client.send(request, PersondataPipDto.class);
} catch (Exception e) {
LOG.info("PdlPip fikk feil", e);
}
return client.send(request, PersondataPipDto.class);
}

@Override
public Map<String, PersondataPipDto> hentTilgangPersondataBolk(List<String> identer) {
var request = RestRequest.newPOSTJson(identer, pipPersonBolkEndpoint, restConfig)
.timeout(Duration.ofSeconds(5));
try {
return client.sendReturnMap(request, PersondataPipDto.class);
} catch (Exception e) {
LOG.info("PdlPip fikk feil", e);
}
return client.sendReturnMap(request, PersondataPipDto.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package no.nav.vedtak.felles.integrasjon.pdlpip;

import java.util.List;
import java.util.Map;

public interface PersondataPip {

PersondataPipDto hentTilgangPersondata(String ident);

Map<String, PersondataPipDto> hentTilgangPersondataBolk(List<String> identer);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package no.nav.vedtak.felles.integrasjon.pdlpip;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;

public record PersondataPipDto(String aktoerId, Person person, Identer identer, GeografiskTilknytning geografiskTilknytning) {

public record Person(List<Adressebeskyttelse> adressebeskyttelse, List<Fødsel> foedsel,
List<Dødsfall> doedsfall, List<Familierelasjon> familierelasjoner) {
}

public record Adressebeskyttelse(Gradering gradering) { }

public record Fødsel(LocalDate foedselsdato) { }

public record Dødsfall(LocalDate doedsdato) { }

public record Familierelasjon(String relatertPersonsIdent) { } // en personIdent (FNR/DNR)

public record Identer(List<Ident> identer) { }

public record Ident(String ident, Boolean historisk, IdentGruppe gruppe) { }

public record GeografiskTilknytning(GtType gtType, String gtKommune, String gtBydel, String gtLand, String regel) { }

public enum Gradering { STRENGT_FORTROLIG_UTLAND, STRENGT_FORTROLIG, FORTROLIG, @JsonEnumDefaultValue UDEFINERT }

public enum IdentGruppe { AKTORID, FOLKEREGISTERIDENT, NPID, @JsonEnumDefaultValue UDEFINERT }

public enum GtType { KOMMUNE, BYDEL, UTLAND, @JsonEnumDefaultValue UDEFINERT }

public enum Relasjonsrolle { FAR, MOR, MEDMOR, BARN, @JsonEnumDefaultValue UDEFINERT }

public boolean harStrengAdresseBeskyttelse() {
return Optional.ofNullable(person()).map(Person::adressebeskyttelse).orElse(List.of()).stream()
.map(Adressebeskyttelse::gradering)
.anyMatch(g -> Gradering.STRENGT_FORTROLIG.equals(g) || Gradering.STRENGT_FORTROLIG_UTLAND.equals(g));
}

public boolean harAdresseBeskyttelse() {
return Optional.ofNullable(person()).map(Person::adressebeskyttelse).orElse(List.of()).stream()
.map(Adressebeskyttelse::gradering)
.anyMatch(g -> g != null && !Gradering.UDEFINERT.equals(g));
}

public boolean erIkkeMyndig() {
return Optional.ofNullable(person()).map(Person::foedsel).orElse(List.of()).stream()
.map(Fødsel::foedselsdato)
.anyMatch(f -> f == null || f.plusYears(18).isAfter(LocalDate.now()));
}

public String personIdent() {
return Optional.ofNullable(identer()).map(Identer::identer).orElse(List.of()).stream()
.filter(i -> IdentGruppe.FOLKEREGISTERIDENT.equals(i.gruppe()))
.filter(i -> !i.historisk())
.map(Ident::ident)
.findFirst().orElse(null);
}

public List<String> personIdenter() {
return Optional.ofNullable(identer()).map(Identer::identer).orElse(List.of()).stream()
.filter(i -> IdentGruppe.FOLKEREGISTERIDENT.equals(i.gruppe()))
.map(Ident::ident)
.toList();
}

public String aktørId() {
return Optional.ofNullable(identer()).map(Identer::identer).orElse(List.of()).stream()
.filter(i -> IdentGruppe.AKTORID.equals(i.gruppe()))
.filter(i -> !i.historisk())
.map(Ident::ident)
.findFirst().orElse(null);
}

public List<String> aktørIdMedHistoriske() {
return Optional.ofNullable(identer()).map(Identer::identer).orElse(List.of()).stream()
.filter(i -> IdentGruppe.AKTORID.equals(i.gruppe()))
.map(Ident::ident)
.toList();
}

public boolean harNasjonalTilknytning() {
return Optional.ofNullable(geografiskTilknytning()).map(GeografiskTilknytning::gtType)
.filter(gtt -> GtType.KOMMUNE.equals(gtt) || GtType.BYDEL.equals(gtt))
.isPresent();
}

public boolean harIkkeNasjonalTilknytning() {
return Optional.ofNullable(geografiskTilknytning()).map(GeografiskTilknytning::gtType)
.filter(gtt -> GtType.KOMMUNE.equals(gtt) || GtType.BYDEL.equals(gtt))
.isEmpty();
}

}
Loading

0 comments on commit cc4be4f

Please sign in to comment.