1
2
3
4
5
6 package org.tailormap.api.security;
7
8 import java.lang.invoke.MethodHandles;
9 import java.util.ArrayList;
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Optional;
13 import java.util.Set;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16 import org.springframework.security.authentication.AnonymousAuthenticationToken;
17 import org.springframework.security.core.Authentication;
18 import org.springframework.security.core.GrantedAuthority;
19 import org.springframework.security.core.context.SecurityContextHolder;
20 import org.springframework.stereotype.Service;
21 import org.tailormap.api.persistence.Application;
22 import org.tailormap.api.persistence.GeoService;
23 import org.tailormap.api.persistence.Group;
24 import org.tailormap.api.persistence.json.AuthorizationRule;
25 import org.tailormap.api.persistence.json.AuthorizationRuleDecision;
26 import org.tailormap.api.persistence.json.GeoServiceLayer;
27 import org.tailormap.api.persistence.json.GeoServiceLayerSettings;
28
29
30
31
32
33 @Service
34 public class AuthorisationService {
35 private static final Logger logger =
36 LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
37
38 public static final String ACCESS_TYPE_VIEW = "read";
39
40 private Optional<AuthorizationRuleDecision> isAuthorizedByRules(List<AuthorizationRule> rules) {
41 Authentication auth = SecurityContextHolder.getContext().getAuthentication();
42 Set<String> groups;
43
44 if (auth == null || auth instanceof AnonymousAuthenticationToken) {
45 groups = Set.of(Group.ANONYMOUS);
46 } else {
47 groups = new HashSet<>();
48 groups.add(Group.ANONYMOUS);
49 groups.add(Group.AUTHENTICATED);
50
51 for (GrantedAuthority authority : auth.getAuthorities()) {
52 groups.add(authority.getAuthority());
53 }
54 }
55 logger.trace("Groups to check rules against: {}", groups);
56
57
58 if (groups.contains(Group.ADMIN)) {
59 logger.trace(
60 "Returning {} because {} is allowed access to anything.",
61 AuthorizationRuleDecision.ALLOW,
62 Group.ADMIN);
63 return Optional.of(AuthorizationRuleDecision.ALLOW);
64 }
65
66 boolean hasValidRule = false;
67
68 for (AuthorizationRule rule : rules) {
69 if (logger.isTraceEnabled()) {
70 logger.trace("Checking rule: \n{} against groups {}.", rule, groups);
71 }
72
73 boolean matchesGroup = groups.contains(rule.getGroupName());
74 if (!matchesGroup) {
75 continue;
76 }
77
78 hasValidRule = true;
79
80 AuthorizationRuleDecision value = rule.getDecisions().get(AuthorisationService.ACCESS_TYPE_VIEW);
81 if (value == null) {
82 logger.trace(
83 "No decision found for rule: \n{} and access: {}, returning <EMPTY>.",
84 rule,
85 AuthorisationService.ACCESS_TYPE_VIEW);
86 return Optional.empty();
87 }
88
89 if (value.equals(AuthorizationRuleDecision.ALLOW)) {
90 logger.trace(
91 "Returning {} because rule: \n{} allows {} access for access: {}.",
92 value,
93 rule,
94 rule.getGroupName(),
95 AuthorisationService.ACCESS_TYPE_VIEW);
96 return Optional.of(value);
97 }
98 }
99
100 if (hasValidRule) {
101 logger.trace(
102 "Returning {} because no valid rule allowed access for access: {}.",
103 AuthorizationRuleDecision.DENY,
104 AuthorisationService.ACCESS_TYPE_VIEW);
105 return Optional.of(AuthorizationRuleDecision.DENY);
106 }
107
108 logger.trace(
109 "Returning <EMPTY> because no rules matched for access: {}.", AuthorisationService.ACCESS_TYPE_VIEW);
110 return Optional.empty();
111 }
112
113
114
115
116
117
118
119 public boolean userAllowedToViewApplication(Application application) {
120 logger.trace(
121 "Checking if user is allowed to view Application {} ({}).",
122 application.getTitle(),
123 application.getTitle());
124 final boolean allowed = isAuthorizedByRules(application.getAuthorizationRules())
125 .equals(Optional.of(AuthorizationRuleDecision.ALLOW));
126 logger.trace(
127 "User is{} allowed to view application: {} (isAuthorizedByRules={}).",
128 allowed ? "" : " not",
129 application.getName(),
130 allowed);
131 return allowed;
132 }
133
134
135
136
137
138
139
140 public boolean userAllowedToViewGeoService(GeoService geoService) {
141 logger.trace(
142 "Checking if user is allowed to view GeoService {} ({}).", geoService.getId(), geoService.getTitle());
143 final boolean allowed = isAuthorizedByRules(geoService.getAuthorizationRules())
144 .equals(Optional.of(AuthorizationRuleDecision.ALLOW));
145 logger.trace(
146 "User is{} allowed to view GeoService: {} (isAuthorizedByRules={}).",
147 allowed ? "" : " not",
148 geoService.getTitle(),
149 allowed);
150 return allowed;
151 }
152
153
154
155
156
157
158
159
160 public boolean userAllowedToViewGeoServiceLayer(GeoService geoService, GeoServiceLayer layer) {
161 logger.trace(
162 "Checking if user is allowed to view GeoService '{}' and layer {} ({}).",
163 geoService.getTitle(),
164 layer.getName(),
165 layer.getTitle());
166
167 Optional<AuthorizationRuleDecision> geoserviceDecision =
168 isAuthorizedByRules(geoService.getAuthorizationRules());
169 if (geoserviceDecision.equals(Optional.of(AuthorizationRuleDecision.DENY))) {
170 logger.trace("Viewing GeoService {} is denied for user.", geoService.getTitle());
171 return false;
172 }
173
174 GeoServiceLayerSettings layerSettings =
175 geoService.getSettings().getLayerSettings().get(layer.getName());
176 if (layerSettings != null && layerSettings.getAuthorizationRules() != null) {
177 logger.trace(
178 "Checking layer settings rules for GeoService '{}' and layer '{}'. \nRules: {}",
179 geoService.getTitle(),
180 layer.getName(),
181 layerSettings.getAuthorizationRules());
182 List<AuthorizationRule> combinedRules = new ArrayList<>(geoService.getAuthorizationRules());
183 for (AuthorizationRule rule : layerSettings.getAuthorizationRules()) {
184
185
186 combinedRules.removeIf(r -> r.getGroupName().equals(rule.getGroupName()));
187 combinedRules.add(rule);
188 }
189 logger.trace(
190 "Combined rules for GeoService '{}' and layer '{}': \n{}",
191 geoService.getTitle(),
192 layer.getName(),
193 combinedRules);
194
195 Optional<AuthorizationRuleDecision> decision = isAuthorizedByRules(combinedRules);
196
197 if (decision.isPresent() || !layerSettings.getAuthorizationRules().isEmpty()) {
198 boolean allowed = decision.equals(Optional.of(AuthorizationRuleDecision.ALLOW));
199
200 logger.trace(
201 "Viewing GeoService '{}' and layer '{}' ({}) is {} for user.",
202 geoService.getTitle(),
203 layer.getName(),
204 layer.getTitle(),
205 (allowed ? "allowed" : "denied"));
206 return allowed;
207 }
208 }
209
210 boolean allowed = geoserviceDecision.equals(Optional.of(AuthorizationRuleDecision.ALLOW));
211 logger.trace(
212 "Viewing GeoService '{}' and layer '{}' ({}) is {} for user because service access is {3}.",
213 geoService.getTitle(), layer.getName(), layer.getTitle(), (allowed ? "allowed" : "denied"));
214 return allowed;
215 }
216
217
218
219
220
221
222
223
224
225
226 public boolean mustDenyAccessForSecuredProxy(Application application, GeoService geoService) {
227 logger.trace(
228 "Checking if proxy access to GeoService {} must be denied for application {}.",
229 geoService.getTitle(),
230 application.getName());
231 if (!Boolean.TRUE.equals(geoService.getSettings().getUseProxy())) {
232 logger.trace(
233 "Must not deny proxy access to GeoService {}, 'useProxy' not set to true.", geoService.getTitle());
234 return false;
235 }
236 if (geoService.getAuthentication() == null) {
237 logger.trace(
238 "Must not deny proxy access to GeoService {}, authentication is not set.", geoService.getTitle());
239 return false;
240 }
241 boolean mustDeny = application.getAuthorizationRules().stream().anyMatch(rule -> {
242 logger.trace(
243 "Checking application rule: {} for group: {} and access type: {} if proxy access must be denied.",
244 rule,
245 Group.ANONYMOUS,
246 ACCESS_TYPE_VIEW);
247 return Group.ANONYMOUS.equals(rule.getGroupName())
248 && AuthorizationRuleDecision.ALLOW.equals(
249 rule.getDecisions().get(ACCESS_TYPE_VIEW));
250 });
251 logger.trace(
252 "Must {}deny access to GeoService '{}' in application {}, because of rules.",
253 mustDeny ? "not " : "",
254 geoService.getTitle(),
255 application.getName());
256 return mustDeny;
257 }
258 }