Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb58a742

Browse files
committed
new: plugin endpoint security basics
1 parent20f5bc8 commitb58a742

File tree

9 files changed

+105
-122
lines changed

9 files changed

+105
-122
lines changed

‎server/api-service/PLUGIN.md‎

Lines changed: 3 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#Lowcoder plugin system (WIP)
1+
#Lowcoderbackendplugin system
22

33
This is an ongoing effort to refactor current plugin system based on pf4j library.
44

@@ -50,73 +50,14 @@ Plugin jar can be structured in any way you like. It can be a plain java project
5050
5151
It is composed from several parts:
5252
- class(es) implementing **LowcoderPlugin** interface
53-
- class(es) implementing **LowcoderEndpoint** interface, containing endpoint handler functions marked with **@EndpointExtension** annotation. These functions must obey following format:
53+
- class(es) implementing **PluginEndpoint** interface, containing endpoint handler functions marked with **@EndpointExtension** annotation. These functions must obey following format:
5454
5555
```java
5656
@EndpointExtension(uri = <endpoint uri>, method = <HTTP method>)
57-
publicMono<ServerResponse> <handler name>(ServerRequest request)
57+
publicEndpointResponse <handler name>(EndpointRequest request)
5858
{
5959
... your endpoint logic implementation
6060
}
61-
62-
for example:
63-
64-
@EndpointExtension(uri = "/hello-world", method = Method.GET)
65-
public Mono<ServerResponse> helloWorld(ServerRequest request)
66-
{
67-
return ServerResponse.ok().body(Mono.just(Hello.builder().message("Hello world!").build()), Hello.class);
68-
}
6961
```
7062
- TODO: class(es) impelemting**LowcoderDatasource** interface
7163

72-
###LowcoderPlugin implementations
73-
74-
Methods of interest:
75-
-**pluginId()** - unique plugin ID - if a plugin with such ID is already loaded, subsequent plugins whith this ID will be ignored
76-
-**description()** - short plugin description
77-
-**load(ApplicationContext parentContext)** - is called during plugin startup - this is the place where you should completely initialize your plugin. If initialization fails, return false
78-
-**unload()** - is called during lowcoder API server shutdown - this is the place where you should release all resources
79-
-**endpoints()** - needs to contain all initialized**PluginEndpoints** you want to expose, for example:
80-
81-
```java
82-
@Override
83-
publicList<PluginEndpoint> endpoints()
84-
{
85-
List<PluginEndpoint> endpoints=newArrayList<>();
86-
87-
endpoints.add(newHelloWorldEndpoint());
88-
89-
return endpoints;
90-
}
91-
```
92-
-**pluginInfo()** - should return a record object with additional information about your plugin. It is serialized to JSON as part of the**/plugins** listing (see**"info"** object in this example):
93-
94-
```json
95-
[
96-
{
97-
"id":"example-plugin",
98-
"description":"Example plugin for lowcoder platform",
99-
"info": {}
100-
},
101-
{
102-
"id":"enterprise",
103-
"description":"Lowcoder enterprise plugin",
104-
"info": {
105-
"enabledFeatures": [
106-
"endpointApiUsage"
107-
]
108-
}
109-
}
110-
]
111-
```
112-
113-
##TODOs
114-
115-
1. Implement endpoint security - currently all plugin endpoints are public (probably by adding**security** attribute to**@EndpointExtension** and enforcing it)
116-
117-
118-
##QUESTIONS / CONSIDERATIONS
119-
120-
1. currently the plugin endpoints are prefixed with**/plugin/{pluginId}/** - this is hardcoded, do we want to make it configurable?
121-
122-

‎server/api-service/lowcoder-sdk/pom.xml‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,6 @@
1313

1414
<name>lowcoder-sdk</name>
1515

16-
<properties>
17-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18-
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
19-
20-
<java.version>17</java.version>
21-
</properties>
22-
2316
<dependencies>
2417
<dependency>
2518
<groupId>org.springframework.boot</groupId>
@@ -173,7 +166,17 @@
173166
<artifactId>validation-api</artifactId>
174167
</dependency>
175168
</dependencies>
176-
169+
170+
<properties>
171+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
172+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
173+
174+
<java.version>17</java.version>
175+
176+
<maven.compiler.source>17</maven.compiler.source>
177+
<maven.compiler.target>17</maven.compiler.target>
178+
</properties>
179+
177180
<dependencyManagement>
178181
<dependencies>
179182
<dependency>

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
importorg.lowcoder.domain.application.model.ApplicationStatus;
2828
importorg.lowcoder.domain.application.model.ApplicationType;
2929
importorg.lowcoder.domain.permission.model.ResourceRole;
30-
importorg.lowcoder.infra.event.EventType;
3130
importorg.springframework.web.bind.annotation.PathVariable;
3231
importorg.springframework.web.bind.annotation.RequestBody;
3332
importorg.springframework.web.bind.annotation.RequestParam;
@@ -106,15 +105,15 @@ public Mono<ResponseView<ApplicationView>> getPublishedApplication(@PathVariable
106105
publicMono<ResponseView<ApplicationView>>getPublishedMarketPlaceApplication(@PathVariableStringapplicationId) {
107106
returnapplicationApiService.getPublishedApplication(applicationId,ApplicationRequestType.PUBLIC_TO_MARKETPLACE)
108107
.delayUntil(applicationView ->applicationApiService.updateUserApplicationLastViewTime(applicationId))
109-
.delayUntil(applicationView ->businessEventPublisher.publishApplicationCommonEvent(applicationView,EventType.VIEW))
108+
.delayUntil(applicationView ->businessEventPublisher.publishApplicationCommonEvent(applicationView,APPLICATION_VIEW))
110109
.map(ResponseView::success);
111110
}
112111

113112
@Override
114113
publicMono<ResponseView<ApplicationView>>getAgencyProfileApplication(@PathVariableStringapplicationId) {
115114
returnapplicationApiService.getPublishedApplication(applicationId,ApplicationRequestType.AGENCY_PROFILE)
116115
.delayUntil(applicationView ->applicationApiService.updateUserApplicationLastViewTime(applicationId))
117-
.delayUntil(applicationView ->businessEventPublisher.publishApplicationCommonEvent(applicationView,EventType.VIEW))
116+
.delayUntil(applicationView ->businessEventPublisher.publishApplicationCommonEvent(applicationView,APPLICATION_VIEW))
118117
.map(ResponseView::success);
119118
}
120119

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/PluginClassLoader.java‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
packageorg.lowcoder.api.framework.plugin;
22

33
importjava.io.IOException;
4-
importjava.io.InputStream;
54
importjava.net.MalformedURLException;
65
importjava.net.URL;
76
importjava.net.URLClassLoader;
@@ -20,6 +19,11 @@ public class PluginClassLoader extends URLClassLoader
2019
privatestaticfinalClassLoaderbaseClassLoader =ClassLoader.getPlatformClassLoader();
2120
privatefinalClassLoaderappClassLoader =Thread.currentThread().getContextClassLoader();
2221

22+
privatestaticfinalString[]excludedPaths =newString[] {
23+
"org.lowcoder.plugin.api.",
24+
"org/lowcoder/plugin/api/"
25+
};
26+
2327
publicPluginClassLoader(Stringname,PathpluginPath)
2428
{
2529
super(name,pathToURLs(pluginPath),baseClassLoader);
@@ -34,7 +38,7 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
3438
returnclazz;
3539
}
3640

37-
if (name.startsWith("org.lowcoder.plugin.api."))
41+
if (StringUtils.startsWithAny(name,excludedPaths))
3842
{
3943
try
4044
{
@@ -67,7 +71,7 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
6771
@Override
6872
publicURLgetResource(Stringname) {
6973
Objects.requireNonNull(name);
70-
if (StringUtils.startsWithAny(name,"org/lowcoder/plugin/api/","org.lowcoder.plugin.api."))
74+
if (StringUtils.startsWithAny(name,excludedPaths))
7175
{
7276
returnappClassLoader.getResource(name);
7377
}
@@ -79,7 +83,7 @@ public URL getResource(String name) {
7983
publicEnumeration<URL>getResources(Stringname)throwsIOException
8084
{
8185
Objects.requireNonNull(name);
82-
if (StringUtils.startsWithAny(name,"org/lowcoder/plugin/api/","org.lowcoder.plugin.api."))
86+
if (StringUtils.startsWithAny(name,excludedPaths))
8387
{
8488
returnappClassLoader.getResources(name);
8589
}

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

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,23 @@
1616
importorg.apache.commons.collections4.CollectionUtils;
1717
importorg.apache.commons.lang3.StringUtils;
1818
importorg.lowcoder.api.framework.plugin.data.PluginServerRequest;
19+
importorg.lowcoder.api.framework.plugin.security.SecuredEndpoint;
1920
importorg.lowcoder.plugin.api.EndpointExtension;
2021
importorg.lowcoder.plugin.api.PluginEndpoint;
2122
importorg.lowcoder.plugin.api.data.EndpointRequest;
2223
importorg.lowcoder.plugin.api.data.EndpointResponse;
2324
importorg.lowcoder.sdk.exception.BaseException;
25+
importorg.springframework.aop.TargetSource;
26+
importorg.springframework.aop.framework.ProxyFactoryBean;
27+
importorg.springframework.aop.target.SimpleBeanTargetSource;
2428
importorg.springframework.beans.factory.support.DefaultListableBeanFactory;
2529
importorg.springframework.context.ApplicationContext;
2630
importorg.springframework.context.support.GenericApplicationContext;
2731
importorg.springframework.core.ResolvableType;
2832
importorg.springframework.http.ResponseCookie;
2933
importorg.springframework.security.access.prepost.PreAuthorize;
34+
importorg.springframework.security.core.context.ReactiveSecurityContextHolder;
3035
importorg.springframework.stereotype.Component;
31-
importorg.springframework.web.reactive.function.server.HandlerFunction;
3236
importorg.springframework.web.reactive.function.server.RequestPredicate;
3337
importorg.springframework.web.reactive.function.server.RouterFunction;
3438
importorg.springframework.web.reactive.function.server.ServerRequest;
@@ -80,48 +84,47 @@ public List<RouterFunction<ServerResponse>> registeredEndpoints()
8084

8185
privatevoidregisterEndpointHandler(StringurlPrefix,PluginEndpointendpoint,Methodhandler)
8286
{
83-
if (handler.isAnnotationPresent(EndpointExtension.class))
87+
if (!handler.isAnnotationPresent(EndpointExtension.class) || !checkHandlerMethod(handler))
8488
{
85-
if (checkHandlerMethod(handler))
89+
if (handler.isAnnotationPresent(EndpointExtension.class))
8690
{
87-
88-
EndpointExtensionendpointMeta =handler.getAnnotation(EndpointExtension.class);
89-
StringendpointName =endpoint.getClass().getSimpleName() +"_" +handler.getName();
90-
91-
RouterFunction<ServerResponse>routerFunction =route(createRequestPredicate(urlPrefix,endpointMeta),req ->
92-
{
93-
Mono<ServerResponse>result =null;
94-
try
95-
{
96-
EndpointResponseresponse = (EndpointResponse)handler.invoke(endpoint,PluginServerRequest.fromServerRequest(req));
97-
result =createServerResponse(response);
98-
}
99-
catch (IllegalAccessException |InvocationTargetExceptioncause)
100-
{
101-
thrownewBaseException("Error running handler for [ " +endpointMeta.method() +": " +endpointMeta.uri() +"] !");
102-
}
103-
returnresult;
104-
});
105-
routes.add(routerFunction);
106-
registerRouterFunctionMapping(endpointName,routerFunction);
107-
108-
log.info("Registered endpoint: {} -> {}: {}",endpoint.getClass().getSimpleName(),endpointMeta.method(),urlPrefix +endpointMeta.uri());
109-
}
110-
else
111-
{
112-
log.error("Cannot register plugin endpoint: {} -> {}! Handler method must be defined as: public Mono<ServerResponse> {}(ServerRequest request)",endpoint.getClass().getSimpleName(),handler.getName(),handler.getName());
91+
log.debug("Not registering plugin endpoint method: {} -> {}! Handler method must be defined as: public EndpointResponse methodName(EndpointRequest request)",endpoint.getClass().getSimpleName(),handler.getName(),handler.getName());
11392
}
93+
return;
11494
}
95+
96+
EndpointExtensionendpointMeta =handler.getAnnotation(EndpointExtension.class);
97+
StringendpointName =endpoint.getClass().getSimpleName() +"_" +handler.getName();
98+
RouterFunction<ServerResponse>routerFunction =route(createRequestPredicate(urlPrefix,endpointMeta),req ->runPluginEndpointMethod(endpoint,endpointMeta,handler,req));
99+
routes.add(routerFunction);
100+
registerRouterFunctionMapping(endpointName,routerFunction);
101+
102+
log.info("Registered endpoint: {} -> {}: {}",endpoint.getClass().getSimpleName(),endpointMeta.method(),urlPrefix +endpointMeta.uri());
115103
}
116104

105+
@SecuredEndpoint
106+
publicMono<ServerResponse>runPluginEndpointMethod(PluginEndpointendpoint,EndpointExtensionendpointMeta,Methodhandler,ServerRequestrequest)
107+
{
108+
Mono<ServerResponse>result =null;
109+
try
110+
{
111+
log.info("Running plugin endpoint method {}\nRequest: {}",handler.getName(),request);
112+
113+
EndpointResponseresponse = (EndpointResponse)handler.invoke(endpoint,PluginServerRequest.fromServerRequest(request));
114+
result =createServerResponse(response);
115+
}
116+
catch (IllegalAccessException |InvocationTargetExceptioncause)
117+
{
118+
thrownewBaseException("Error running handler for [ " +endpointMeta.method() +": " +endpointMeta.uri() +"] !");
119+
}
120+
returnresult;
121+
}
122+
123+
117124
privatevoidregisterRouterFunctionMapping(StringendpointName,RouterFunction<ServerResponse>routerFunction)
118125
{
119126
StringbeanName ="pluginEndpoint_" +endpointName +"_" +System.currentTimeMillis();
120-
121-
((GenericApplicationContext)applicationContext).registerBean(beanName,RouterFunction.class, () -> {
122-
returnrouterFunction;
123-
});
124-
127+
((GenericApplicationContext)applicationContext).registerBean(beanName,RouterFunction.class, () ->routerFunction );
125128
log.debug("Registering RouterFunction bean definition: {}",beanName);
126129
}
127130

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
packageorg.lowcoder.api.framework.plugin.security;
2+
3+
importjava.util.function.Supplier;
4+
5+
importorg.aopalliance.intercept.MethodInvocation;
6+
importorg.springframework.security.authorization.AuthorizationDecision;
7+
importorg.springframework.security.authorization.AuthorizationManager;
8+
importorg.springframework.security.core.Authentication;
9+
10+
importlombok.extern.slf4j.Slf4j;
11+
12+
@Slf4j
13+
publicclassEndpointAuthorizationManagerimplementsAuthorizationManager<MethodInvocation>
14+
{
15+
16+
@Override
17+
publicAuthorizationDecisioncheck(Supplier<Authentication>authentication,MethodInvocationinvocation)
18+
{
19+
log.info("Checking plugin endpoint invocation security for {}",invocation.getMethod().getName());
20+
21+
returnnewAuthorizationDecision(true);
22+
}
23+
24+
}

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/security/PluginAuthorizationManager.java‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
importorg.aopalliance.intercept.MethodInvocation;
66
importorg.apache.commons.lang3.StringUtils;
77
importorg.lowcoder.plugin.api.EndpointExtension;
8-
importorg.springframework.core.annotation.AnnotationUtils;
98
importorg.springframework.expression.EvaluationContext;
109
importorg.springframework.expression.EvaluationException;
1110
importorg.springframework.expression.Expression;
@@ -21,7 +20,7 @@
2120
importreactor.core.publisher.Mono;
2221

2322
@Slf4j
24-
@Component
23+
//@Component
2524
publicclassPluginAuthorizationManagerimplementsReactiveAuthorizationManager<MethodInvocation>
2625
{
2726
privatefinalMethodSecurityExpressionHandlerexpressionHandler;
@@ -34,10 +33,9 @@ public PluginAuthorizationManager()
3433
@Override
3534
publicMono<AuthorizationDecision>check(Mono<Authentication>authentication,MethodInvocationinvocation)
3635
{
37-
log.info(" invocation::{}",invocation.getMethod());
36+
log.info("Checking plugin reactive endpoint invocationsecurity for{}",invocation.getMethod().getName());
3837

39-
Methodmethod =invocation.getMethod();
40-
EndpointExtensionendpointExtension =AnnotationUtils.findAnnotation(method,EndpointExtension.class);
38+
EndpointExtensionendpointExtension = (EndpointExtension)invocation.getArguments()[1];
4139
if (endpointExtension ==null ||StringUtils.isBlank(endpointExtension.authorize()))
4240
{
4341
returnMono.empty();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
packageorg.lowcoder.api.framework.plugin.security;
2+
3+
importjava.lang.annotation.Documented;
4+
importjava.lang.annotation.ElementType;
5+
importjava.lang.annotation.Inherited;
6+
importjava.lang.annotation.Retention;
7+
importjava.lang.annotation.RetentionPolicy;
8+
importjava.lang.annotation.Target;
9+
10+
@Target({ElementType.METHOD,ElementType.TYPE })
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@Inherited
13+
@Documented
14+
public @interfaceSecuredEndpoint {
15+
16+
}

‎server/api-service/pom.xml‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,12 @@
1212

1313

1414
<properties>
15-
<revision>2.3.0-SNAPSHOT</revision>
15+
<revision>2.4.0</revision>
1616
<java.version>17</java.version>
1717
<javadoc.disabled>true</javadoc.disabled>
1818
<deploy.disabled>true</deploy.disabled>
1919
<source.disabled>true</source.disabled>
20-
<project.groupId>org.lowcoder</project.groupId>
21-
<project.version>1.0-SNAPSHOT</project.version>
2220
<skipDockerBuild>true</skipDockerBuild>
23-
<log4j2.version>2.17.0</log4j2.version>
24-
<maven.compiler.source>17</maven.compiler.source>
25-
<maven.compiler.target>17</maven.compiler.target>
2621
</properties>
2722

2823
<repositories>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp