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(
72 new ErrorResponse()
73 .message(ex.getReason() != null ? ex.getReason() : ex.getBody().getTitle())
74 .code(ex.getStatusCode().value()));
75 }
76
77 @ModelAttribute
78 public ViewerResponse.KindEnum populateViewerKind(HttpServletRequest request) {
79 if (request.getServletPath().startsWith(basePath + "/app/")) {
80 return ViewerResponse.KindEnum.APP;
81 } else if (request.getServletPath().startsWith(basePath + "/service/")) {
82 return ViewerResponse.KindEnum.SERVICE;
83 } else {
84 return null;
85 }
86 }
87
88 @ModelAttribute
89 public Application populateApplication(
90 @ModelAttribute ViewerResponse.KindEnum viewerKind,
91 @PathVariable(required = false) String viewerName,
92 @RequestParam(required = false) String base,
93 @RequestParam(required = false) String projection) {
94 if (viewerKind == null || viewerName == null) {
95
96 return null;
97 }
98
99 Application app;
100 if (viewerKind == ViewerResponse.KindEnum.APP) {
101 app = applicationRepository.findByName(viewerName);
102 if (app == null) {
103 throw new ResponseStatusException(HttpStatus.NOT_FOUND);
104 }
105 } else if (viewerKind == ViewerResponse.KindEnum.SERVICE) {
106 GeoService service = geoServiceRepository.findById(viewerName).orElse(null);
107
108 if (service == null) {
109 throw new ResponseStatusException(HttpStatus.NOT_FOUND);
110 }
111
112 if (!authorizationService.mayUserRead(service)) {
113 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
114 }
115
116
117 if (!service.isPublished()) {
118 throw new ResponseStatusException(HttpStatus.NOT_FOUND);
119 }
120 app = applicationHelper.getServiceApplication(base, projection, service);
121 } else {
122 throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
123 }
124
125 if (!this.authorizationService.mayUserRead(app)) {
126 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
127 }
128 return app;
129 }
130
131 @ModelAttribute
132 public AppTreeLayerNode populateAppTreeLayerNode(
133 @ModelAttribute Application app, @PathVariable(required = false) String appLayerId) {
134 if (app == null || appLayerId == null) {
135
136 return null;
137 }
138
139 final AppTreeLayerNode layerNode =
140 app.getAllAppTreeLayerNode()
141 .filter(r -> r.getId().equals(appLayerId))
142 .findFirst()
143 .orElse(null);
144 if (layerNode == null) {
145 throw new ResponseStatusException(
146 HttpStatus.NOT_FOUND, "Application layer with id " + appLayerId + " not found");
147 }
148
149
150
151
152
153 return layerNode;
154 }
155
156 @ModelAttribute
157 public GeoService populateGeoService(
158 @ModelAttribute Application app, @ModelAttribute AppTreeLayerNode appTreeLayerNode) {
159 if (appTreeLayerNode == null) {
160
161 return null;
162 }
163 if (appTreeLayerNode.getServiceId() == null) {
164 return null;
165 }
166 GeoService service =
167 geoServiceRepository.findById(appTreeLayerNode.getServiceId()).orElse(null);
168 if (service != null && !authorizationService.mayUserRead(service)) {
169 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
170 }
171
172 if (service != null && authorizationService.mustDenyAccessForSecuredProxy(app, service)) {
173 throw new ResponseStatusException(HttpStatus.FORBIDDEN);
174 }
175
176 return service;
177 }
178
179 @ModelAttribute
180 public GeoServiceLayer populateGeoServiceLayer(
181 @ModelAttribute AppTreeLayerNode appTreeLayerNode, @ModelAttribute GeoService service) {
182 if (service == null) {
183
184 return null;
185 }
186 GeoServiceLayer layer =
187 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 }