Skip to content

Commit

Permalink
Add support for checking user access to specific Altinn resources
Browse files Browse the repository at this point in the history
#deploy-altinn3-tilgang-service #deploy-altinn3-tilgang-service-prod

Introduced functionality to verify if a user has access to a specific Altinn resource (e.g., Dolly) via a newly added API endpoint. Refactored existing Altinn-related services, renamed classes for clarity, and integrated the new "authorized parties" API. Updated configurations and added new DTOs and services to support the changes.
  • Loading branch information
krharum committed Dec 13, 2024
1 parent 7ef08c7 commit 241d635
Show file tree
Hide file tree
Showing 17 changed files with 189 additions and 35 deletions.
1 change: 1 addition & 0 deletions apps/altinn3-tilgang-service/config.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ spec:
consumes:
- name: altinn:resourceregistry/accesslist.read
- name: altinn:resourceregistry/accesslist.write
- name: altinn:accessmanagement/authorizedparties.resourceowner
accessPolicy:
inbound:
rules:
Expand Down
1 change: 1 addition & 0 deletions apps/altinn3-tilgang-service/config.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ spec:
consumes:
- name: altinn:resourceregistry/accesslist.read
- name: altinn:resourceregistry/accesslist.write
- name: altinn:accessmanagement/authorizedparties.resourceowner
accessPolicy:
inbound:
rules:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.command.CreateAccessListeMemberCommand;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.command.DeleteAccessListMemberCommand;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.command.GetAccessListMembersCommand;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.command.GetAuthorizedPartiesCommand;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.command.GetExchangeTokenCommand;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnAccessListResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnAuthorizedPartiesRequestDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AuthorizedPartyDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.BrregResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.OrganisasjonCreateDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.OrganisasjonDeleteDTO;
Expand Down Expand Up @@ -76,7 +79,7 @@ public Flux<Organisasjon> delete(String organisasjonsnummer) {

return Flux.from(getAccessListMembers()
.flatMapMany(value -> Flux.fromIterable(value.getData()))
.map(AltinnResponseDTO.AccessListMembershipDTO::getIdentifiers)
.map(AltinnAccessListResponseDTO.AccessListMembershipDTO::getIdentifiers)
.collectList()
.map(data -> getIdentifier(data, organisasjonsnummer))
.map(identifier ->
Expand Down Expand Up @@ -106,16 +109,16 @@ public Flux<Organisasjon> create(String organisasjonsnummer) {
new OrganisasjonCreateDTO(organisasjonsnummer),
altinnConfig).call())
.flatMapMany(response ->
isBlank(response.getFeilmelding()) ?
Flux.fromIterable(response.getData())
.map(this::getOrgnummer)
.filter(organisasjonsnummer::equals)
.flatMap(brregConsumer::getEnheter) :
Mono.just(BrregResponseDTO.builder()
.organisasjonsnummer(organisasjonsnummer)
.feilmelding(response.getFeilmelding())
.status(response.getStatus())
.build()))
isBlank(response.getFeilmelding()) ?
Flux.fromIterable(response.getData())
.map(this::getOrgnummer)
.filter(organisasjonsnummer::equals)
.flatMap(brregConsumer::getEnheter) :
Mono.just(BrregResponseDTO.builder()
.organisasjonsnummer(organisasjonsnummer)
.feilmelding(response.getFeilmelding())
.status(response.getStatus())
.build()))
.map(response -> mapperFacade.map(response, Organisasjon.class));
}

Expand All @@ -125,7 +128,16 @@ public Flux<Organisasjon> getOrganisasjoner() {
.flatMapMany(this::convertToOrganisasjon);
}

private Mono<AltinnResponseDTO> getAccessListMembers() {
public Flux<AuthorizedPartyDTO> getAuthorizedParties(String ident) {

return maskinportenConsumer.getAccessToken()
.flatMap(this::exchangeToken)
.flatMapMany(exchangeToken -> new GetAuthorizedPartiesCommand(webClient,
new AltinnAuthorizedPartiesRequestDTO(ident),
exchangeToken).call());
}

private Mono<AltinnAccessListResponseDTO> getAccessListMembers() {

return maskinportenConsumer.getAccessToken()
.flatMap(this::exchangeToken)
Expand All @@ -135,7 +147,7 @@ private Mono<AltinnResponseDTO> getAccessListMembers() {
altinnConfig).call());
}

private Flux<Organisasjon> convertToOrganisasjon(AltinnResponseDTO altInnResponse) {
private Flux<Organisasjon> convertToOrganisasjon(AltinnAccessListResponseDTO altInnResponse) {

return Flux.fromIterable(altInnResponse.getData())
.map(this::getOrgnummer)
Expand All @@ -155,7 +167,7 @@ private OrganisasjonDeleteDTO getIdentifier(List<JsonNode> data, String organisa
}

@SneakyThrows
private String getOrgnummer(AltinnResponseDTO.AccessListMembershipDTO data) {
private String getOrgnummer(AltinnAccessListResponseDTO.AccessListMembershipDTO data) {

return data.getIdentifiers()
.get(ORGANISASJON_ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.altinn3tilgangservice.config.AltinnConfig;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnAccessListResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.OrganisasjonCreateDTO;
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
import org.springframework.http.HttpHeaders;
Expand All @@ -16,7 +16,7 @@

@Slf4j
@RequiredArgsConstructor
public class CreateAccessListeMemberCommand implements Callable<Mono<AltinnResponseDTO>> {
public class CreateAccessListeMemberCommand implements Callable<Mono<AltinnAccessListResponseDTO>> {

private static final String ALTINN_URL = "/resourceregistry/api/v1/access-lists/{owner}/{identifier}/members";

Expand All @@ -27,7 +27,7 @@ public class CreateAccessListeMemberCommand implements Callable<Mono<AltinnRespo


@Override
public Mono<AltinnResponseDTO> call() {
public Mono<AltinnAccessListResponseDTO> call() {

return webClient
.post()
Expand All @@ -37,14 +37,14 @@ public Mono<AltinnResponseDTO> call() {
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(AltinnResponseDTO.class)
.bodyToMono(AltinnAccessListResponseDTO.class)
.doOnError(WebClientFilter::logErrorMessage)
.doOnSuccess(value -> log.info("Altinn organisasjontilgang opprettet for {}",
organisasjon.getData().stream()
.map(data -> data.split(":"))
.map(data -> data[data.length-1])
.collect(Collectors.joining())))
.onErrorResume(throwable -> Mono.just(AltinnResponseDTO.builder()
.onErrorResume(throwable -> Mono.just(AltinnAccessListResponseDTO.builder()
.status(WebClientFilter.getStatus(throwable))
.feilmelding(WebClientFilter.getMessage(throwable))
.build()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.altinn3tilgangservice.config.AltinnConfig;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnAccessListResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.OrganisasjonDeleteDTO;
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
import org.springframework.http.HttpHeaders;
Expand All @@ -19,7 +19,7 @@

@Slf4j
@RequiredArgsConstructor
public class DeleteAccessListMemberCommand implements Callable<Mono<AltinnResponseDTO>> {
public class DeleteAccessListMemberCommand implements Callable<Mono<AltinnAccessListResponseDTO>> {

private static final String ALTINN_URL = "/resourceregistry/api/v1/access-lists/{owner}/{identifier}/members";

Expand All @@ -30,7 +30,7 @@ public class DeleteAccessListMemberCommand implements Callable<Mono<AltinnRespon


@Override
public Mono<AltinnResponseDTO> call() {
public Mono<AltinnAccessListResponseDTO> call() {

return webClient
.method(HttpMethod.DELETE)
Expand All @@ -41,7 +41,7 @@ public Mono<AltinnResponseDTO> call() {
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.bodyValue(identifiers)
.retrieve()
.bodyToMono(AltinnResponseDTO.class)
.bodyToMono(AltinnAccessListResponseDTO.class)
.doOnSuccess(value -> log.info("Altinn organisasjontilgang slettet for {}",
identifiers.getData().stream()
.filter(data -> data.contains(ORGANISASJON_ID))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.altinn3tilgangservice.config.AltinnConfig;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnResponseDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnAccessListResponseDTO;
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
Expand All @@ -14,7 +14,7 @@

@Slf4j
@RequiredArgsConstructor
public class GetAccessListMembersCommand implements Callable<Mono<AltinnResponseDTO>> {
public class GetAccessListMembersCommand implements Callable<Mono<AltinnAccessListResponseDTO>> {

private static final String ALTINN_URL = "/resourceregistry/api/v1/access-lists/{owner}/{identifier}/members";

Expand All @@ -23,7 +23,7 @@ public class GetAccessListMembersCommand implements Callable<Mono<AltinnResponse
private final AltinnConfig altinnConfig;

@Override
public Mono<AltinnResponseDTO> call() {
public Mono<AltinnAccessListResponseDTO> call() {

return webClient
.get()
Expand All @@ -32,7 +32,7 @@ public Mono<AltinnResponseDTO> call() {
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(AltinnResponseDTO.class)
.bodyToMono(AltinnAccessListResponseDTO.class)
.doOnError(WebClientFilter::logErrorMessage)
.doOnSuccess(value -> log.info("Altinn-tilgang hentet"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package no.nav.testnav.altinn3tilgangservice.consumer.altinn.command;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AltinnAuthorizedPartiesRequestDTO;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto.AuthorizedPartyDTO;
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

import java.util.concurrent.Callable;

@Slf4j
@RequiredArgsConstructor
public class GetAuthorizedPartiesCommand implements Callable<Flux<AuthorizedPartyDTO>> {

private static final String ALTINN_URL = "/resourceregistry/accessmanagement/api/v1/resourceowner/authorizedparties";

private final WebClient webClient;
private final AltinnAuthorizedPartiesRequestDTO request;
private final String token;

@Override
public Flux<AuthorizedPartyDTO> call() {

log.info("Spøøring om bruker {}", request);
return webClient
.post()
.uri(builder -> builder.path(ALTINN_URL).build())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.bodyValue(request)
.retrieve()
.bodyToFlux(AuthorizedPartyDTO.class)
.doOnError(WebClientFilter::logErrorMessage);
}
}
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AltinnResponseDTO {
public class AltinnAccessListResponseDTO {

private List<AccessListMembershipDTO> data;
private String feilmelding;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto;

import lombok.Data;

@Data
public class AltinnAuthorizedPartiesRequestDTO {

private static final String IDENT_IDENTIFIKATOR = "urn:altinn:person:identifier-no";

private String type;
private String value;

public AltinnAuthorizedPartiesRequestDTO(String ident) {

this.type = IDENT_IDENTIFIKATOR;
this.value = ident;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package no.nav.testnav.altinn3tilgangservice.consumer.altinn.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthorizedPartyDTO {

private String personId;
private String organizationNumber;
private List<String> authorizedResources;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package no.nav.testnav.altinn3tilgangservice.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PersonRequest {

private String ident;
private String orgnummer;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package no.nav.testnav.altinn3tilgangservice.provider;

import lombok.RequiredArgsConstructor;
import no.nav.testnav.altinn3tilgangservice.domain.PersonRequest;
import no.nav.testnav.altinn3tilgangservice.service.AltinnBrukerTilgangService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/api/v1/brukertilgang")
@RequiredArgsConstructor
public class AltinnBrukerTilgangController {

private final AltinnBrukerTilgangService brukerTilgangService;

@PostMapping
public Mono<Boolean> harDollyTilgang(@RequestBody PersonRequest personRequest) {

return brukerTilgangService.harDollyTilgang(personRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.altinn3tilgangservice.domain.OrganisasjonResponse;
import no.nav.testnav.altinn3tilgangservice.service.AltinnTilgangService;
import no.nav.testnav.altinn3tilgangservice.service.AltinnOrganisasjonTilgangService;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -20,9 +20,9 @@
@RestController
@RequestMapping("/api/v1/organisasjoner")
@RequiredArgsConstructor
public class AltinnTilgangController {
public class AltinnOrganisasjonTilgangController {

private final AltinnTilgangService altinnTilgangService;
private final AltinnOrganisasjonTilgangService altinnTilgangService;

@GetMapping
@Operation(description = "Henter alle organisasjoner med Altinn-tilgang")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package no.nav.testnav.altinn3tilgangservice.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.altinn3tilgangservice.consumer.altinn.AltinnConsumer;
import no.nav.testnav.altinn3tilgangservice.domain.PersonRequest;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

@Slf4j
@Service
@RequiredArgsConstructor
public class AltinnBrukerTilgangService {

private final AltinnConsumer altinnConsumer;

public Mono<Boolean> harDollyTilgang(PersonRequest personRequest) {

return altinnConsumer.getAuthorizedParties(personRequest.getIdent())
.doOnNext(party -> log.info("AuthorizedParty {}", party))
.filter(party -> party.getOrganizationNumber().equals(personRequest.getOrgnummer()))
.filter(part -> part.getAuthorizedResources().contains("dolly"))
.reduce(Boolean.FALSE, (a, b) -> Boolean.TRUE);
}
}
Loading

0 comments on commit 241d635

Please sign in to comment.