Skip to content

Commit 49a4a61

Browse files
dragonpooludomikula
authored andcommitted
Add authorization check to Extension Endpoint
1 parent dd36801 commit 49a4a61

File tree

2 files changed

+75
-39
lines changed

2 files changed

+75
-39
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
package org.lowcoder.api.framework.configuration;
22

3-
import java.util.ArrayList;
4-
53
import org.lowcoder.api.framework.plugin.LowcoderPluginManager;
64
import org.lowcoder.api.framework.plugin.endpoint.PluginEndpointHandler;
7-
// Falk: eventually not needed
8-
import org.lowcoder.api.framework.plugin.security.PluginAuthorizationManager;
9-
import org.lowcoder.plugin.api.EndpointExtension;
10-
import org.springframework.aop.Advisor;
11-
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
12-
import org.springframework.beans.factory.config.BeanDefinition;
135
import org.springframework.context.annotation.Bean;
146
import org.springframework.context.annotation.Configuration;
157
import org.springframework.context.annotation.DependsOn;
16-
import org.springframework.context.annotation.Role;
17-
import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
18-
import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
198
import org.springframework.web.reactive.function.server.RequestPredicates;
209
import org.springframework.web.reactive.function.server.RouterFunction;
2110
import org.springframework.web.reactive.function.server.RouterFunctions;
2211
import org.springframework.web.reactive.function.server.ServerResponse;
23-
2412
import reactor.core.publisher.Mono;
2513

14+
import java.util.ArrayList;
15+
2616
@Configuration
2717
public class PluginConfiguration
2818
{
@@ -43,15 +33,4 @@ RouterFunction<?> pluginEndpoints(LowcoderPluginManager pluginManager, PluginEnd
4333

4434
return (endpoints == null) ? pluginsList : pluginsList.andOther(endpoints);
4535
}
46-
47-
// Falk: eventually not needed
48-
@Bean
49-
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
50-
Advisor protectPluginEndpoints(PluginAuthorizationManager pluginAauthManager)
51-
{
52-
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(EndpointExtension.class, true);
53-
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(pointcut, pluginAauthManager);
54-
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder() -1);
55-
return interceptor;
56-
}
5736
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/endpoint/PluginEndpointHandlerImpl.java

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.lowcoder.api.framework.plugin.endpoint;
22

3+
import static org.lowcoder.sdk.exception.BizError.NOT_AUTHORIZED;
34
import static org.springframework.web.reactive.function.server.RequestPredicates.DELETE;
45
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
56
import static org.springframework.web.reactive.function.server.RequestPredicates.OPTIONS;
@@ -8,30 +9,42 @@
89
import static org.springframework.web.reactive.function.server.RequestPredicates.PUT;
910
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
1011

12+
import java.lang.reflect.AccessibleObject;
1113
import java.lang.reflect.InvocationTargetException;
1214
import java.lang.reflect.Method;
1315
import java.util.ArrayList;
16+
import java.util.Collections;
1417
import java.util.List;
1518

19+
import org.aopalliance.intercept.MethodInvocation;
1620
import org.apache.commons.collections4.CollectionUtils;
1721
import org.apache.commons.lang3.StringUtils;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.jetbrains.annotations.Nullable;
1824
import org.lowcoder.api.framework.plugin.data.PluginServerRequest;
25+
import org.lowcoder.api.framework.plugin.security.PluginAuthorizationManager;
1926
import org.lowcoder.api.framework.plugin.security.SecuredEndpoint;
2027
import org.lowcoder.plugin.api.EndpointExtension;
2128
import org.lowcoder.plugin.api.PluginEndpoint;
2229
import org.lowcoder.plugin.api.data.EndpointRequest;
2330
import org.lowcoder.plugin.api.data.EndpointResponse;
2431
import org.lowcoder.sdk.exception.BaseException;
32+
import org.lowcoder.sdk.exception.BizException;
2533
import org.springframework.aop.TargetSource;
2634
import org.springframework.aop.framework.ProxyFactoryBean;
35+
import org.springframework.aop.framework.ReflectiveMethodInvocation;
2736
import org.springframework.aop.target.SimpleBeanTargetSource;
2837
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
2938
import org.springframework.context.ApplicationContext;
3039
import org.springframework.context.support.GenericApplicationContext;
3140
import org.springframework.core.ResolvableType;
3241
import org.springframework.http.ResponseCookie;
3342
import org.springframework.security.access.prepost.PreAuthorize;
43+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
44+
import org.springframework.security.authorization.AuthorizationDecision;
45+
import org.springframework.security.core.Authentication;
3446
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
47+
import org.springframework.security.core.context.SecurityContext;
3548
import org.springframework.stereotype.Component;
3649
import org.springframework.web.reactive.function.server.RequestPredicate;
3750
import org.springframework.web.reactive.function.server.RouterFunction;
@@ -52,6 +65,7 @@ public class PluginEndpointHandlerImpl implements PluginEndpointHandler
5265

5366
private final ApplicationContext applicationContext;
5467
private final DefaultListableBeanFactory beanFactory;
68+
private final PluginAuthorizationManager pluginAuthorizationManager;
5569

5670
@Override
5771
public void registerEndpoints(String pluginUrlPrefix, List<PluginEndpoint> endpoints)
@@ -101,26 +115,69 @@ private void registerEndpointHandler(String urlPrefix, PluginEndpoint endpoint,
101115

102116
log.info("Registered endpoint: {} -> {}: {}", endpoint.getClass().getSimpleName(), endpointMeta.method(), urlPrefix + endpointMeta.uri());
103117
}
104-
105-
@SecuredEndpoint
118+
106119
public Mono<ServerResponse> runPluginEndpointMethod(PluginEndpoint endpoint, EndpointExtension endpointMeta, Method handler, ServerRequest request)
107120
{
108-
Mono<ServerResponse> result = null;
109-
try
110-
{
111-
log.info("Running plugin endpoint method {}\nRequest: {}", handler.getName(), request);
121+
log.info("Running plugin endpoint method {}\nRequest: {}", handler.getName(), request);
112122

113-
EndpointResponse response = (EndpointResponse)handler.invoke(endpoint, PluginServerRequest.fromServerRequest(request));
114-
result = createServerResponse(response);
115-
}
116-
catch (IllegalAccessException | InvocationTargetException cause)
117-
{
118-
throw new BaseException("Error running handler for [ " + endpointMeta.method() + ": " + endpointMeta.uri() + "] !");
119-
}
120-
return result;
123+
Mono<Authentication> monoAuthentication = ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).cache();
124+
Mono<AuthorizationDecision> decisionMono = monoAuthentication.flatMap(authentication -> {
125+
MethodInvocation methodInvocation = null;
126+
try {
127+
methodInvocation = getMethodInvocation(endpointMeta, authentication);
128+
} catch (NoSuchMethodException e) {
129+
return Mono.error(new RuntimeException(e));
130+
}
131+
return pluginAuthorizationManager.check(monoAuthentication, methodInvocation);
132+
});
133+
134+
return decisionMono.<EndpointResponse>handle((authorizationDecision, sink) -> {
135+
if(!authorizationDecision.isGranted()) sink.error(new BizException(NOT_AUTHORIZED, "NOT_AUTHORIZED"));
136+
try {
137+
sink.next((EndpointResponse) handler.invoke(endpoint, PluginServerRequest.fromServerRequest(request)));
138+
} catch (IllegalAccessException | InvocationTargetException e) {
139+
sink.error(new RuntimeException(e));
140+
}
141+
}).flatMap(this::createServerResponse);
121142
}
122-
123-
143+
144+
private static @NotNull MethodInvocation getMethodInvocation(EndpointExtension endpointMeta, Authentication authentication) throws NoSuchMethodException {
145+
Method method = Authentication.class.getMethod("isAuthenticated");
146+
Object[] arguments = new Object[]{"someString", endpointMeta};
147+
return new MethodInvocation() {
148+
@NotNull
149+
@Override
150+
public Method getMethod() {
151+
return method;
152+
}
153+
154+
@NotNull
155+
@Override
156+
public Object[] getArguments() {
157+
return arguments;
158+
}
159+
160+
@Nullable
161+
@Override
162+
public Object proceed() throws Throwable {
163+
return null;
164+
}
165+
166+
@Nullable
167+
@Override
168+
public Object getThis() {
169+
return authentication;
170+
}
171+
172+
@NotNull
173+
@Override
174+
public AccessibleObject getStaticPart() {
175+
return null;
176+
}
177+
};
178+
}
179+
180+
124181
private void registerRouterFunctionMapping(String endpointName, RouterFunction<ServerResponse> routerFunction)
125182
{
126183
String beanName = "pluginEndpoint_" + endpointName + "_" + System.currentTimeMillis();

0 commit comments

Comments
 (0)