1
2
3
4
5
6 package org.tailormap.api.controller;
7
8 import jakarta.servlet.http.HttpServletRequest;
9 import org.springframework.beans.factory.annotation.Value;
10 import org.springframework.http.HttpStatus;
11 import org.springframework.http.MediaType;
12 import org.springframework.http.ResponseEntity;
13 import org.springframework.web.bind.WebDataBinder;
14 import org.springframework.web.bind.annotation.ExceptionHandler;
15 import org.springframework.web.bind.annotation.InitBinder;
16 import org.springframework.web.bind.annotation.ModelAttribute;
17 import org.springframework.web.bind.annotation.PathVariable;
18 import org.springframework.web.bind.annotation.RequestParam;
19 import org.springframework.web.bind.annotation.RestControllerAdvice;
20 import org.springframework.web.server.ResponseStatusException;
21 import org.tailormap.api.annotation.AppRestController;
22 import org.tailormap.api.persistence.Application;
23 import org.tailormap.api.persistence.GeoService;
24 import org.tailormap.api.persistence.helper.ApplicationHelper;
25 import org.tailormap.api.persistence.json.AppTreeLayerNode;
26 import org.tailormap.api.persistence.json.GeoServiceLayer;
27 import org.tailormap.api.repository.ApplicationRepository;
28 import org.tailormap.api.repository.GeoServiceRepository;
29 import org.tailormap.api.security.AuthorizationService;
30 import org.tailormap.api.viewer.model.ErrorResponse;
31 import org.tailormap.api.viewer.model.RedirectResponse;
32 import org.tailormap.api.viewer.model.ViewerResponse;
33
34 @RestControllerAdvice(annotations = AppRestController.class)
35 public class AppRestControllerAdvice {
36 private final ApplicationRepository applicationRepository;
37 private final GeoServiceRepository geoServiceRepository;
38 private final ApplicationHelper applicationHelper;
39 private final AuthorizationService authorizationService;
40
41 @Value("${tailormap-api.base-path}")
42 private String basePath;
43
44 public AppRestControllerAdvice(
45 ApplicationRepository applicationRepository,
46 GeoServiceRepository geoServiceRepository,
47 ApplicationHelper applicationHelper,
48 AuthorizationService authorizationService) {
49 this.applicationRepository = applicationRepository;
50 this.geoServiceRepository = geoServiceRepository;
51 this.applicationHelper = applicationHelper;
52 this.authorizationService = authorizationService;
53 }
54
55 @InitBinder
56 protected void initBinder(WebDataBinder binder) {
57
58
59 binder.setAllowedFields("viewerName", "appLayerId", "base", "projection");
60 }
61
62 @ExceptionHandler(ResponseStatusException.class)
63 protected ResponseEntity<?> handleResponseStatusException(ResponseStatusException ex) {
64 if (HttpStatus.UNAUTHORIZED.equals(ex.getStatusCode())) {
65 return ResponseEntity.status(ex.getStatusCode())
66 .contentType(MediaType.APPLICATION_JSON)
67 .body(new RedirectResponse());
68 }
69 return ResponseEntity.status(ex.getStatusCode())
70 .contentType(MediaType.APPLICATION_JSON)
71 .body(new ErrorResponse()
72 .message(
73 ex.getReason() != null
74 ? ex.getReason()
75 : ex.getBody().getTitle())
76 .code(ex.getStatusCode().value()));
77 }
78
79 @ModelAttribute
80 public ViewerResponse.KindEnum populateViewerKind(HttpServletRequest request) {
81 if (request.getServletPath().startsWith(basePath + "/app/")) {
82 return ViewerResponse.KindEnum.APP;
83 } else if (request.getServletPath().startsWith(basePath + "/service/")) {
84 return ViewerResponse.KindEnum.SERVICE;
85 } else {
86 return null;
87 }
88 }
89
90 @ModelAttribute
91 public Application populateApplication(
92 @ModelAttribute ViewerResponse.KindEnum viewerKind,
93 @PathVariable(required = false) String viewerName,
94 @RequestParam(required = false) String base,
95 @RequestParam(required = false) String projection) {
96 if (viewerKind == null || viewerName == null) {
97
98 return null;
99 }
100
101 Application app;
102 if (viewerKind == ViewerResponse.KindEnum.APP) {
103 app = applicationRepository.findByName(viewerName);
104 if (app == null) {
105 throw new ResponseStatusException(HttpStatus.NOT_FOUND);
106 }
107 } else if (viewerKind == ViewerResponse.KindEnum.SERVICE) {
108 GeoService service = geoServiceRepository.findById(viewerName).orElse(null);
109
110 if (service == null) {
111 throw new ResponseStatusException(HttpStatus.NOT_FOUND);
112 }
113
114 if (!authorizationService.mayUserRead(service)) {
115 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
116 }
117
118
119 if (!service.isPublished()) {
120 throw new ResponseStatusException(HttpStatus.NOT_FOUND);
121 }
122 app = applicationHelper.getServiceApplication(base, projection, service);
123 } else {
124 throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
125 }
126
127 if (!this.authorizationService.mayUserRead(app)) {
128 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
129 }
130 return app;
131 }
132
133 @ModelAttribute
134 public AppTreeLayerNode populateAppTreeLayerNode(
135 @ModelAttribute Application app, @PathVariable(required = false) String appLayerId) {
136 if (app == null || appLayerId == null) {
137
138 return null;
139 }
140
141 final AppTreeLayerNode layerNode = app.getAllAppTreeLayerNode()
142 .filter(r -> r.getId().equals(appLayerId))
143 .findFirst()
144 .orElse(null);
145 if (layerNode == null) {
146 throw new ResponseStatusException(
147 HttpStatus.NOT_FOUND, "Application layer with id " + appLayerId + " not found");
148 }
149
150
151
152
153
154 return layerNode;
155 }
156
157 @ModelAttribute
158 public GeoService populateGeoService(
159 @ModelAttribute Application app, @ModelAttribute AppTreeLayerNode appTreeLayerNode) {
160 if (appTreeLayerNode == null) {
161
162 return null;
163 }
164 if (appTreeLayerNode.getServiceId() == null) {
165 return null;
166 }
167 GeoService service =
168 geoServiceRepository.findById(appTreeLayerNode.getServiceId()).orElse(null);
169 if (service != null && !authorizationService.mayUserRead(service)) {
170 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
171 }
172
173 if (service != null && authorizationService.mustDenyAccessForSecuredProxy(app, service)) {
174 throw new ResponseStatusException(HttpStatus.FORBIDDEN);
175 }
176
177 return service;
178 }
179
180 @ModelAttribute
181 public GeoServiceLayer populateGeoServiceLayer(
182 @ModelAttribute AppTreeLayerNode appTreeLayerNode, @ModelAttribute GeoService service) {
183 if (service == null) {
184
185 return null;
186 }
187 GeoServiceLayer layer = service.getLayers().stream()
188 .filter(l -> appTreeLayerNode.getLayerName().equals(l.getName()))
189 .findFirst()
190 .orElse(null);
191
192 if (layer != null && !authorizationService.mayUserRead(service, layer)) {
193 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
194 }
195
196 return layer;
197 }
198 }