Skip to content

Commit

Permalink
Add authorization checks
Browse files Browse the repository at this point in the history
  • Loading branch information
matthijsln committed Jun 19, 2024
1 parent ea62476 commit 6f88a6e
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,18 @@ private String sqlQuestionMarks(int count) {
return String.join(", ", qs);
}

public Set<Planregistratie> getPlanregistraties() {
public Set<Planregistratie> getPlanregistratiesForProvincie() {
return jdbcClient.sql("select * from planregistratie").query(planregistratieRowMapper).set();
}

public Set<Planregistratie> getPlanregistratiesForGemeente(String gemeente) {
return jdbcClient
.sql("select * from planregistratie where gemeente = ?")
.param(gemeente)
.query(planregistratieRowMapper)
.set();
}

@Transactional
public void deletePlanregistratie(String id) {
jdbcClient.sql("delete from planregistratie where id = ?").param(1, id, Types.OTHER).update();
Expand Down Expand Up @@ -185,13 +193,16 @@ insert into detailplanning(id, plancategorie_id, creator, created_at, editor, ed
}
}

public boolean planregistratieExists(String id) {
return !this.jdbcClient
.sql("select 1 from planregistratie where id = ?")
.param(1, id, Types.OTHER)
.query()
.singleColumn()
.isEmpty();
public String getPlanregistratieGemeente(String id) {
return (String)
this.jdbcClient
.sql("select gemeente from planregistratie where id = ?")
.param(1, id, Types.OTHER)
.query()
.singleColumn()
.stream()
.findFirst()
.orElse(null);
}

public Set<Plancategorie> getPlancategorieen(String planregistratieId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public PopulateTestData(PlanmonitorWonenDatabaseService pmwDb, JdbcClient jdbcCl

@PostConstruct
public void init() throws ParseException {
if (!pmwDb.getPlanregistraties().isEmpty()) {
if (!pmwDb.getPlanregistratiesForProvincie().isEmpty()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@

package nl.b3p.planmonitorwonen.api.controller;

import static nl.b3p.planmonitorwonen.api.model.auth.PlanmonitorAuthentication.getFromSecurityContext;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;

import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.Set;
import nl.b3p.planmonitorwonen.api.PlanmonitorWonenDatabaseService;
import nl.b3p.planmonitorwonen.api.model.Planregistratie;
import nl.b3p.planmonitorwonen.api.model.PlanregistratieComplete;
import nl.b3p.planmonitorwonen.api.model.auth.PlanmonitorAuthentication;
import org.locationtech.jts.io.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand All @@ -29,6 +35,9 @@
@RestController
@Profile("!test")
public class PlanregistratieController {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

private final PlanmonitorWonenDatabaseService pmwDb;

public PlanregistratieController(
Expand All @@ -38,14 +47,33 @@ public PlanregistratieController(

@GetMapping(path = "${planmonitor-wonen-api.base-path}/planregistraties")
public Set<Planregistratie> planregistraties() {
return pmwDb.getPlanregistraties();
PlanmonitorAuthentication auth = getFromSecurityContext();
if (auth.isProvincie()) {
return pmwDb.getPlanregistratiesForProvincie();
} else {
return pmwDb.getPlanregistratiesForGemeente(auth.getGemeente());
}
}

@GetMapping(path = "${planmonitor-wonen-api.base-path}/planregistratie/{id}/details")
public Map<String, Object> details(@PathVariable("id") String id) {
if (!pmwDb.planregistratieExists(id)) {
PlanmonitorAuthentication auth = getFromSecurityContext();

String gemeente = pmwDb.getPlanregistratieGemeente(id);
if (gemeente == null) {
throw new ResponseStatusException(NOT_FOUND);
}

if (!auth.isProvincie() && !gemeente.equals(auth.getGemeente())) {
logger.warn(
"Gemeente user \"{}\" with authorization for gemeente {} tried to access plan id {} of gemeente {}",
auth.getTmApiAuthentication().getName(),
auth.getGemeente(),
id,
gemeente);
throw new ResponseStatusException(NOT_FOUND);
}

return Map.of(
"plancategorieen",
pmwDb.getPlancategorieen(id),
Expand All @@ -57,9 +85,38 @@ public Map<String, Object> details(@PathVariable("id") String id) {
public ResponseEntity<?> put(
@PathVariable("id") String id, @RequestBody PlanregistratieComplete planregistratieComplete)
throws ParseException {

if (id == null || !id.equals(planregistratieComplete.planregistratie().getId())) {
throw new ResponseStatusException(BAD_REQUEST);
}

PlanmonitorAuthentication auth = getFromSecurityContext();

if (!auth.isProvincie()
&& !planregistratieComplete.planregistratie().getGemeente().equals(auth.getGemeente())) {
logger.warn(
"Gemeente user \"{}\" with authorization for gemeente {} tried to save plan id {}, name \"{}\" with gemeente value {}",
auth.getTmApiAuthentication().getName(),
auth.getGemeente(),
id,
planregistratieComplete.planregistratie().getPlanNaam(),
planregistratieComplete.planregistratie().getGemeente());
throw new ResponseStatusException(FORBIDDEN);
}

String gemeente = pmwDb.getPlanregistratieGemeente(id);

if (!auth.isProvincie() && !gemeente.equals(auth.getGemeente())) {
logger.warn(
"Gemeente user \"{}\" with authorization for gemeente {} tried to update plan id {}, name \"{}\" of gemeente {}",
auth.getTmApiAuthentication().getName(),
auth.getGemeente(),
id,
planregistratieComplete.planregistratie().getPlanNaam(),
gemeente);
throw new ResponseStatusException(FORBIDDEN);
}

pmwDb.insertPlanregistratie(
planregistratieComplete.planregistratie(),
planregistratieComplete.plancategorieen(),
Expand All @@ -70,12 +127,28 @@ public ResponseEntity<?> put(
@DeleteMapping(path = "${planmonitor-wonen-api.base-path}/planregistratie/{id}")
public ResponseEntity<?> delete(@PathVariable("id") String id) {
if (id == null) {
return ResponseEntity.status(BAD_REQUEST).build();
} else if (!pmwDb.planregistratieExists(id)) {
return ResponseEntity.status(NOT_FOUND).build();
} else {
pmwDb.deletePlanregistratie(id);
return ResponseEntity.status(OK).build();
throw new ResponseStatusException(BAD_REQUEST);
}

PlanmonitorAuthentication auth = getFromSecurityContext();

String gemeente = pmwDb.getPlanregistratieGemeente(id);

if (gemeente == null) {
throw new ResponseStatusException(NOT_FOUND);
}

if (!auth.isProvincie() && !gemeente.equals(auth.getGemeente())) {
logger.warn(
"Gemeente user \"{}\" with authorization for gemeente {} tried to delete plan id {} of gemeente {}",
auth.getTmApiAuthentication().getName(),
auth.getGemeente(),
id,
gemeente);
throw new ResponseStatusException(FORBIDDEN);
}

pmwDb.deletePlanregistratie(id);
return ResponseEntity.status(OK).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2024 Provincie Zeeland
*
* SPDX-License-Identifier: MIT
*/

package nl.b3p.planmonitorwonen.api.model.auth;

import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;

import java.util.HashMap;
import java.util.Map;
import nl.b3p.planmonitorwonen.api.security.TMAPIAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.server.ResponseStatusException;

public class PlanmonitorAuthentication {
private TMAPIAuthenticationToken tmApiAuthentication;
private boolean isProvincie;
private String gemeente;

public TMAPIAuthenticationToken getTmApiAuthentication() {
return tmApiAuthentication;
}

public boolean isProvincie() {
return isProvincie;
}

public String getGemeente() {
return gemeente;
}

public static PlanmonitorAuthentication getFromSecurityContext() throws ResponseStatusException {
final TMAPIAuthenticationToken authentication =
(TMAPIAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new ResponseStatusException(UNAUTHORIZED);
}

Map<String, String> groupProperties = new HashMap<>();
authentication
.getAuthResponse()
.get("groupProperties")
.elements()
.forEachRemaining(
n -> groupProperties.put(n.get("key").textValue(), n.get("value").textValue()));

PlanmonitorAuthentication result = new PlanmonitorAuthentication();
result.tmApiAuthentication = authentication;
result.isProvincie = "provincie".equals(groupProperties.get("typeGebruiker"));
result.gemeente = groupProperties.get("gemeente");

if (!result.isProvincie && result.gemeente == null) {
throw new ResponseStatusException(FORBIDDEN);
}

return result;
}
}
4 changes: 2 additions & 2 deletions src/main/resources/db/migration/V1__schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ create table planregistratie
edited_at timestamp with time zone,
plan_naam varchar not null unique,
provincie varchar,
gemeente varchar references gemeente (naam),
gemeente varchar not null references gemeente (naam),
regio varchar,
plaatsnaam varchar,
vertrouwelijkheid pmw_vertrouwelijkheid,
Expand All @@ -106,7 +106,7 @@ create table planregistratie
knelpunten_meerkeuze pmw_knelpunten_meerkeuze,
beoogd_woonmilieu_abf13 pmw_woonmilieu_abf13,
aantal_studentenwoningen integer,
sleutelproject boolean not null
sleutelproject boolean not null
);

create type pmw_nieuwbouw as enum (
Expand Down

0 comments on commit 6f88a6e

Please sign in to comment.