1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.restapi.v1;
20
21 import java.io.IOException;
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
27
28 import org.apache.logging.log4j.LogManager;
29 import org.mycore.access.MCRAccessInterface;
30 import org.mycore.access.MCRAccessManager;
31 import org.mycore.access.MCRRuleAccessInterface;
32 import org.mycore.access.mcrimpl.MCRAccessControlSystem;
33 import org.mycore.frontend.jersey.access.MCRRequestScopeACL;
34 import org.mycore.restapi.converter.MCRDetailLevel;
35 import org.mycore.restapi.v1.errors.MCRRestAPIError;
36 import org.mycore.restapi.v1.errors.MCRRestAPIException;
37 import org.mycore.restapi.v1.errors.MCRRestAPIExceptionMapper;
38
39 import jakarta.annotation.Priority;
40 import jakarta.ws.rs.HttpMethod;
41 import jakarta.ws.rs.Path;
42 import jakarta.ws.rs.Priorities;
43 import jakarta.ws.rs.container.ContainerRequestContext;
44 import jakarta.ws.rs.container.ContainerRequestFilter;
45 import jakarta.ws.rs.container.ResourceInfo;
46 import jakarta.ws.rs.core.Context;
47 import jakarta.ws.rs.core.MultivaluedMap;
48 import jakarta.ws.rs.core.Response;
49
50 @Priority(Priorities.AUTHORIZATION)
51 public class MCRRestAuthorizationFilter implements ContainerRequestFilter {
52
53 public static final String PARAM_CLASSID = "classid";
54
55 public static final String PARAM_MCRID = "mcrid";
56
57 public static final String PARAM_DERID = "derid";
58
59 public static final String PARAM_DER_PATH = "path";
60
61 @Context
62 ResourceInfo resourceInfo;
63
64
65
66
67
68
69
70
71 private void checkRestAPIAccess(ContainerRequestContext requestContext, MCRRestAPIACLPermission permission,
72 String path)
73 throws MCRRestAPIException {
74 MCRRequestScopeACL aclProvider = MCRRequestScopeACL.getInstance(requestContext);
75 LogManager.getLogger().warn(path + ": Checking API access: " + permission);
76 String thePath = path.startsWith("/") ? path : "/" + path;
77
78 MCRAccessInterface acl = MCRAccessManager.getAccessImpl();
79 String permStr = permission.toString();
80 boolean hasAPIAccess = aclProvider.checkPermission("restapi:/", permStr);
81 if (hasAPIAccess) {
82 String objId = "restapi:" + thePath;
83 boolean isRuleInterface = acl instanceof MCRRuleAccessInterface;
84 if (!isRuleInterface || ((MCRRuleAccessInterface) acl).hasRule(objId, permStr)) {
85 if (aclProvider.checkPermission(objId, permStr)) {
86 return;
87 }
88 } else {
89 return;
90 }
91 }
92 throw new MCRRestAPIException(Response.Status.FORBIDDEN,
93 new MCRRestAPIError(MCRRestAPIError.CODE_ACCESS_DENIED, "REST-API action is not allowed.",
94 "Check access right '" + permission + "' on ACLs 'restapi:/' and 'restapi:" + path + "'!"));
95 }
96
97 private void checkBaseAccess(ContainerRequestContext requestContext, MCRRestAPIACLPermission permission,
98 String objectId, String derId, String path)
99 throws MCRRestAPIException {
100 LogManager.getLogger().debug("Permission: {}, Object: {}, Derivate: {}, Path: {}", permission, objectId, derId,
101 path);
102 Optional<String> checkable = Optional.ofNullable(derId)
103 .filter(d -> path != null)
104 .map(Optional::of)
105 .orElseGet(() -> Optional.ofNullable(objectId));
106 checkable.ifPresent(id -> LogManager.getLogger().info("Checking " + permission + " access on " + id));
107 MCRRequestScopeACL aclProvider = MCRRequestScopeACL.getInstance(requestContext);
108 boolean allowed = checkable
109 .map(id -> aclProvider.checkPermission(id, permission.toString()))
110 .orElse(true);
111 if (allowed) {
112 return;
113 }
114 throw new MCRRestAPIException(Response.Status.FORBIDDEN,
115 new MCRRestAPIError(MCRRestAPIError.CODE_ACCESS_DENIED, "REST-API action is not allowed.",
116 "Check access right '" + permission + "' on '" + checkable.orElse(null) + "'!"));
117 }
118
119 private void checkDetailLevel(ContainerRequestContext requestContext, String... detail) throws MCRRestAPIException {
120 MCRRequestScopeACL aclProvider = MCRRequestScopeACL.getInstance(requestContext);
121 List<String> missedPermissions = Stream.of(detail)
122 .map(d -> "rest-detail-" + d)
123 .filter(d -> MCRAccessManager.hasRule(MCRAccessControlSystem.POOL_PRIVILEGE_ID, d))
124 .filter(d -> !aclProvider.checkPermission(d))
125 .collect(Collectors.toList());
126 if (!missedPermissions.isEmpty()) {
127 throw new MCRRestAPIException(Response.Status.FORBIDDEN,
128 new MCRRestAPIError(MCRRestAPIError.CODE_ACCESS_DENIED, "REST-API action is not allowed.",
129 "Check permission(s) " + missedPermissions + "!"));
130 }
131 }
132
133 @Override
134 public void filter(ContainerRequestContext requestContext) throws IOException {
135 MCRRestAPIACLPermission permission;
136 switch (requestContext.getMethod()) {
137 case HttpMethod.OPTIONS:
138 return;
139 case HttpMethod.GET:
140 case HttpMethod.HEAD:
141 permission = MCRRestAPIACLPermission.READ;
142 break;
143 case HttpMethod.DELETE:
144 permission = MCRRestAPIACLPermission.DELETE;
145 break;
146 default:
147 permission = MCRRestAPIACLPermission.WRITE;
148 }
149 Optional.ofNullable(resourceInfo.getResourceClass().getAnnotation(Path.class))
150 .map(Path::value)
151 .ifPresent(path -> {
152 try {
153 checkRestAPIAccess(requestContext, permission, path);
154 MultivaluedMap<String, String> pathParameters = requestContext.getUriInfo().getPathParameters();
155 checkBaseAccess(requestContext, permission, pathParameters.getFirst(PARAM_MCRID),
156 pathParameters.getFirst(PARAM_DERID), pathParameters.getFirst(PARAM_DER_PATH));
157 } catch (MCRRestAPIException e) {
158 LogManager.getLogger().warn("API Access denied!");
159 requestContext.abortWith(new MCRRestAPIExceptionMapper().toResponse(e));
160 }
161 });
162 try {
163 checkDetailLevel(requestContext,
164 requestContext.getAcceptableMediaTypes()
165 .stream()
166 .map(m -> m.getParameters().get(MCRDetailLevel.MEDIA_TYPE_PARAMETER))
167 .filter(Objects::nonNull)
168 .toArray(String[]::new));
169 } catch (MCRRestAPIException e) {
170 LogManager.getLogger().warn("API Access denied!");
171 requestContext.abortWith(new MCRRestAPIExceptionMapper().toResponse(e));
172 }
173
174 }
175
176
177
178
179 public enum MCRRestAPIACLPermission {
180 READ {
181 public String toString() {
182 return MCRAccessManager.PERMISSION_READ;
183 }
184 },
185
186 WRITE {
187 public String toString() {
188 return MCRAccessManager.PERMISSION_WRITE;
189 }
190 },
191
192 DELETE {
193 public String toString() {
194 return MCRAccessManager.PERMISSION_DELETE;
195 }
196 }
197 }
198 }