Skip to content

Commit 33e9b9a

Browse files
authored
Merge pull request #611 from lowcoder-org/add-auth-handling-to-rest-api-datasource
Add OAuth(Inherit From Login) Handling To Rest Api Datasource
2 parents b4a9873 + e32669c commit 33e9b9a

File tree

7 files changed

+138
-44
lines changed

7 files changed

+138
-44
lines changed

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/query/service/QueryExecutionService.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
package org.lowcoder.domain.query.service;
22

3-
import static org.lowcoder.sdk.exception.BizError.QUERY_EXECUTION_ERROR;
4-
import static org.lowcoder.sdk.exception.PluginCommonError.QUERY_EXECUTION_TIMEOUT;
5-
import static org.lowcoder.sdk.util.ExceptionUtils.ofException;
6-
7-
import java.time.Duration;
8-
import java.util.List;
9-
import java.util.Map;
10-
import java.util.concurrent.TimeoutException;
11-
import java.util.stream.Collectors;
12-
3+
import lombok.extern.slf4j.Slf4j;
134
import org.lowcoder.domain.datasource.model.Datasource;
145
import org.lowcoder.domain.datasource.model.DatasourceConnectionHolder;
156
import org.lowcoder.domain.datasource.service.DatasourceConnectionPool;
@@ -24,10 +15,18 @@
2415
import org.lowcoder.sdk.query.QueryVisitorContext;
2516
import org.springframework.beans.factory.annotation.Autowired;
2617
import org.springframework.stereotype.Service;
27-
28-
import lombok.extern.slf4j.Slf4j;
2918
import reactor.core.publisher.Mono;
3019

20+
import java.time.Duration;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.concurrent.TimeoutException;
24+
import java.util.stream.Collectors;
25+
26+
import static org.lowcoder.sdk.exception.BizError.QUERY_EXECUTION_ERROR;
27+
import static org.lowcoder.sdk.exception.PluginCommonError.QUERY_EXECUTION_TIMEOUT;
28+
import static org.lowcoder.sdk.util.ExceptionUtils.ofException;
29+
3130
@Slf4j
3231
@Service
3332
public class QueryExecutionService {

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/RestApiDatasourceConfig.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ public RestApiAuthType getAuthType() {
108108
return RestApiAuthType.NO_AUTH;
109109
}
110110

111+
public boolean isOauth2InheritFromLogin() {
112+
if (this.authConfig != null) {
113+
return this.authConfig.getType().name().equals(RestApiAuthType.OAUTH2_INHERIT_FROM_LOGIN.name());
114+
}
115+
return false;
116+
}
117+
111118
public Set<String> getForwardCookies() {
112119
return SetUtils.emptyIfNull(forwardCookies);
113120
}

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/AuthConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
@Type(value = BasicAuthConfig.class, name = "DIGEST_AUTH"),
1818
@Type(value = BasicAuthConfig.class, name = "BASIC_AUTH"),
1919
@Type(value = NoneAuthConfig.class, name = "NO_AUTH"),
20-
@Type(value = DefaultAuthConfig.class, name = "OAUTH2_INHERIT_FROM_LOGIN")
20+
@Type(value = OAuthInheritAuthConfig.class, name = "OAUTH2_INHERIT_FROM_LOGIN")
2121
})
2222
public abstract class AuthConfig implements Encrypt {
2323

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.lowcoder.sdk.plugin.restapi.auth;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import lombok.Getter;
5+
6+
import javax.annotation.Nullable;
7+
8+
/**
9+
* oauth(inherit from login) auth config
10+
*/
11+
@Getter
12+
public final class OAuthInheritAuthConfig extends AuthConfig {
13+
14+
private String authId;
15+
16+
@JsonCreator
17+
public OAuthInheritAuthConfig(String authId, RestApiAuthType type) {
18+
super(type);
19+
this.authId = authId;
20+
}
21+
22+
@Override
23+
public AuthConfig mergeWithUpdatedConfig(@Nullable AuthConfig updatedConfig) {
24+
// return new auth config if auth type changed
25+
if (!(updatedConfig instanceof OAuthInheritAuthConfig oAuthInheritAuthConfig)) {
26+
return updatedConfig;
27+
}
28+
// otherwise merge oauth auth config
29+
return new OAuthInheritAuthConfig(oAuthInheritAuthConfig.getAuthId(),
30+
oAuthInheritAuthConfig.getType());
31+
}
32+
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/ApplicationQueryApiService.java

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
package org.lowcoder.api.query;
22

3-
import static org.lowcoder.domain.permission.model.ResourceAction.READ_APPLICATIONS;
4-
import static org.lowcoder.sdk.exception.BizError.DATASOURCE_AND_APP_ORG_NOT_MATCH;
5-
import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER;
6-
import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
7-
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
8-
9-
import java.util.List;
10-
import java.util.stream.Collectors;
11-
12-
import javax.annotation.Nullable;
13-
143
import org.apache.commons.lang.ArrayUtils;
154
import org.apache.commons.lang3.StringUtils;
165
import org.lowcoder.api.home.SessionUserService;
@@ -27,11 +16,15 @@
2716
import org.lowcoder.domain.query.service.LibraryQueryRecordService;
2817
import org.lowcoder.domain.query.service.LibraryQueryService;
2918
import org.lowcoder.domain.query.service.QueryExecutionService;
19+
import org.lowcoder.domain.user.model.Connection;
20+
import org.lowcoder.domain.user.model.User;
3021
import org.lowcoder.infra.util.TupleUtils;
3122
import org.lowcoder.sdk.config.CommonConfig;
3223
import org.lowcoder.sdk.exception.BizError;
3324
import org.lowcoder.sdk.models.Property;
3425
import org.lowcoder.sdk.models.QueryExecutionResult;
26+
import org.lowcoder.sdk.plugin.restapi.RestApiDatasourceConfig;
27+
import org.lowcoder.sdk.plugin.restapi.auth.OAuthInheritAuthConfig;
3528
import org.lowcoder.sdk.query.QueryVisitorContext;
3629
import org.lowcoder.sdk.util.ExceptionUtils;
3730
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,10 +33,21 @@
4033
import org.springframework.stereotype.Service;
4134
import org.springframework.util.MultiValueMap;
4235
import org.springframework.web.server.ServerWebExchange;
43-
4436
import reactor.core.publisher.Mono;
4537
import reactor.core.publisher.Timed;
4638

39+
import javax.annotation.Nullable;
40+
import java.util.Collections;
41+
import java.util.List;
42+
import java.util.Optional;
43+
import java.util.stream.Collectors;
44+
45+
import static org.lowcoder.domain.permission.model.ResourceAction.READ_APPLICATIONS;
46+
import static org.lowcoder.sdk.exception.BizError.DATASOURCE_AND_APP_ORG_NOT_MATCH;
47+
import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER;
48+
import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
49+
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
50+
4751
@Service
4852
public class ApplicationQueryApiService {
4953

@@ -93,12 +97,13 @@ public Mono<QueryExecutionResult> executeApplicationQuery(ServerWebExchange exch
9397
Mono<Datasource> datasourceMono = baseQueryMono.flatMap(query -> datasourceService.getById(query.getDatasourceId())
9498
.switchIfEmpty(deferredError(BizError.DATASOURCE_NOT_FOUND, "DATASOURCE_NOT_FOUND", query.getDatasourceId())))
9599
.cache();
96-
return sessionUserService.getVisitorId()
97-
.delayUntil(userId -> checkExecutePermission(userId, queryExecutionRequest.getPath(), appId,
100+
101+
return sessionUserService.getVisitor()
102+
.delayUntil(user -> checkExecutePermission(user.getId(), queryExecutionRequest.getPath(), appId,
98103
queryExecutionRequest.isViewMode()))
99104
.zipWhen(visitorId -> Mono.zip(appMono, appQueryMono, baseQueryMono, datasourceMono), TupleUtils::merge)
100105
.flatMap(tuple -> {
101-
String userId = tuple.getT1();
106+
String userId = tuple.getT1().getId();
102107
Application app = tuple.getT2();
103108
ApplicationQuery appQuery = tuple.getT3();
104109
BaseQuery baseQuery = tuple.getT4();
@@ -109,8 +114,18 @@ public Mono<QueryExecutionResult> executeApplicationQuery(ServerWebExchange exch
109114
}
110115

111116
MultiValueMap<String, HttpCookie> cookies = exchange.getRequest().getCookies();
112-
QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, app.getOrganizationId(), port, cookies,
113-
getAuthParamsAndHeadersInheritFromLogin(userId, app.getOrganizationId()), commonConfig.getDisallowedHosts());
117+
118+
Mono<List<Property>> paramsAndHeadersInheritFromLogin = Mono.empty();
119+
120+
121+
// Check if oauth inherited from login and save token
122+
if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig
123+
&& restApiDatasourceConfig.isOauth2InheritFromLogin()) {
124+
paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId());
125+
126+
}
127+
128+
QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, app.getOrganizationId(), port, cookies, paramsAndHeadersInheritFromLogin, commonConfig.getDisallowedHosts());
114129
return queryExecutionService.executeQuery(datasource, baseQuery.getQueryConfig(), queryExecutionRequest.paramMap(),
115130
appQuery.getTimeoutStr(), queryVisitorContext
116131
)
@@ -176,8 +191,18 @@ private Mono<BaseQuery> getBaseQueryFromLibraryQuery(ApplicationQuery query) {
176191
.map(LibraryQueryRecord::getQuery);
177192
}
178193

179-
protected Mono<List<Property>> getAuthParamsAndHeadersInheritFromLogin(String userId, String orgId) {
180-
return Mono.empty();
194+
protected Mono<List<Property>> getAuthParamsAndHeadersInheritFromLogin(User user, String authId) {
195+
if(authId == null) {
196+
return Mono.empty();
197+
}
198+
Optional<Connection> activeConnectionOptional = user.getConnections()
199+
.stream()
200+
.filter(connection -> connection.getAuthId().equals(authId))
201+
.findFirst();
202+
if(!activeConnectionOptional.isPresent() || activeConnectionOptional.get().getAuthConnectionAuthToken() == null) {
203+
return Mono.empty();
204+
}
205+
return Mono.just(Collections.singletonList(new Property("Authorization","Bearer " + activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header")));
181206
}
182207

183208
protected void onNextOrError(QueryExecutionRequest queryExecutionRequest, QueryVisitorContext queryVisitorContext,

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
77
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
88

9-
import java.util.HashSet;
10-
import java.util.List;
11-
import java.util.Map;
9+
import java.util.*;
1210

1311
import org.apache.commons.collections4.CollectionUtils;
1412
import org.apache.commons.lang3.StringUtils;
@@ -23,6 +21,7 @@
2321
import org.lowcoder.api.usermanagement.OrgDevChecker;
2422
import org.lowcoder.api.util.BusinessEventPublisher;
2523
import org.lowcoder.api.util.ViewBuilder;
24+
import org.lowcoder.domain.authentication.AuthenticationService;
2625
import org.lowcoder.domain.datasource.model.Datasource;
2726
import org.lowcoder.domain.datasource.service.DatasourceService;
2827
import org.lowcoder.domain.organization.model.OrgMember;
@@ -35,13 +34,16 @@
3534
import org.lowcoder.domain.query.service.LibraryQueryRecordService;
3635
import org.lowcoder.domain.query.service.LibraryQueryService;
3736
import org.lowcoder.domain.query.service.QueryExecutionService;
37+
import org.lowcoder.domain.user.model.Connection;
3838
import org.lowcoder.domain.user.model.User;
3939
import org.lowcoder.domain.user.service.UserService;
4040
import org.lowcoder.sdk.config.CommonConfig;
4141
import org.lowcoder.sdk.exception.BizError;
4242
import org.lowcoder.sdk.exception.PluginCommonError;
4343
import org.lowcoder.sdk.models.Property;
4444
import org.lowcoder.sdk.models.QueryExecutionResult;
45+
import org.lowcoder.sdk.plugin.restapi.RestApiDatasourceConfig;
46+
import org.lowcoder.sdk.plugin.restapi.auth.OAuthInheritAuthConfig;
4547
import org.lowcoder.sdk.query.QueryVisitorContext;
4648
import org.springframework.beans.factory.annotation.Autowired;
4749
import org.springframework.beans.factory.annotation.Value;
@@ -87,6 +89,9 @@ public class LibraryQueryApiService {
8789
@Autowired
8890
private CommonConfig commonConfig;
8991

92+
@Autowired
93+
private AuthenticationService authenticationService;
94+
9095
@Value("${server.port}")
9196
private int port;
9297

@@ -245,15 +250,19 @@ public Mono<QueryExecutionResult> executeLibraryQueryFromJs(ServerWebExchange ex
245250

246251
Mono<OrgMember> visitorOrgMemberCache = sessionUserService.getVisitorOrgMemberCache()
247252
.onErrorReturn(NOT_EXIST);
248-
return Mono.zip(visitorOrgMemberCache, baseQueryMono, datasourceMono)
253+
254+
Mono<User> userMono = sessionUserService.getVisitor();
255+
256+
return Mono.zip(visitorOrgMemberCache, baseQueryMono, datasourceMono, userMono)
249257
.flatMap(tuple -> {
250258
OrgMember orgMember = tuple.getT1();
251259
String orgId = orgMember.getOrgId();
252260
String userId = orgMember.getUserId();
253261
BaseQuery baseQuery = tuple.getT2();
254262
Datasource datasource = tuple.getT3();
263+
User user = tuple.getT4();
255264
Mono<List<Property>> paramsAndHeadersInheritFromLogin = orgMember.isInvalid()
256-
? Mono.empty() : getParamsAndHeadersInheritFromLogin(userId, orgId);
265+
? Mono.empty() : getParamsAndHeadersInheritFromLogin(user, null);
257266

258267
QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, orgId, port,
259268
exchange.getRequest().getCookies(),
@@ -284,17 +293,29 @@ public Mono<QueryExecutionResult> executeLibraryQuery(ServerWebExchange exchange
284293
Mono<Datasource> datasourceMono = baseQueryMono.flatMap(query -> datasourceService.getById(query.getDatasourceId())
285294
.switchIfEmpty(deferredError(BizError.DATASOURCE_NOT_FOUND, "DATASOURCE_NOT_FOUND", query.getDatasourceId()))).cache();
286295

296+
Mono<User> userMono = sessionUserService.getVisitor();
297+
287298
return orgDevChecker.checkCurrentOrgDev()
288299
.then(Mono.zip(sessionUserService.getVisitorOrgMemberCache(),
289-
baseQueryMono, datasourceMono))
300+
baseQueryMono, datasourceMono, userMono))
290301
.flatMap(tuple -> {
291302
OrgMember orgMember = tuple.getT1();
292303
String orgId = orgMember.getOrgId();
293304
String userId = orgMember.getUserId();
294305
BaseQuery baseQuery = tuple.getT2();
295306
Datasource datasource = tuple.getT3();
296-
Mono<List<Property>> paramsAndHeadersInheritFromLogin =
297-
getParamsAndHeadersInheritFromLogin(userId, orgId);
307+
User user = tuple.getT4();
308+
309+
Mono<List<Property>> paramsAndHeadersInheritFromLogin = Mono.empty();
310+
311+
312+
// check if oauth inherited from login and save token
313+
if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig
314+
&& restApiDatasourceConfig.isOauth2InheritFromLogin()) {
315+
paramsAndHeadersInheritFromLogin = getParamsAndHeadersInheritFromLogin
316+
(user, ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId());
317+
}
318+
298319
QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, orgId, port, cookies, paramsAndHeadersInheritFromLogin,
299320
commonConfig.getDisallowedHosts());
300321
Map<String, Object> queryConfig = baseQuery.getQueryConfig();
@@ -322,8 +343,18 @@ private Mono<BaseQuery> getBaseQuery(LibraryQueryCombineId libraryQueryCombineId
322343
.map(LibraryQueryRecord::getQuery);
323344
}
324345

325-
protected Mono<List<Property>> getParamsAndHeadersInheritFromLogin(String userId, String orgId) {
326-
return Mono.empty();
346+
protected Mono<List<Property>> getParamsAndHeadersInheritFromLogin(User user, String authId) {
347+
if(authId == null) {
348+
return Mono.empty();
349+
}
350+
Optional<Connection> activeConnectionOptional = user.getConnections()
351+
.stream()
352+
.filter(connection -> connection.getAuthId().equals(authId))
353+
.findFirst();
354+
if(!activeConnectionOptional.isPresent() || activeConnectionOptional.get().getAuthConnectionAuthToken() == null) {
355+
return Mono.empty();
356+
}
357+
return Mono.just(Collections.singletonList(new Property("Authorization","Bearer " + activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header")));
327358
}
328359

329360
protected void onNextOrError(QueryExecutionRequest queryExecutionRequest, QueryVisitorContext queryVisitorContext, BaseQuery baseQuery,

server/api-service/lowcoder-server/src/main/resources/application-lowcoder.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ spring:
33
mongodb:
44
authentication-database: admin
55
auto-index-creation: false
6-
uri: mongodb://192.168.8.103:27017/lowcoder?authSource=admin
6+
uri: mongodb://localhost:27017/lowcoder?authSource=admin
77
redis:
8-
url: redis://192.168.8.103:6379
8+
url: redis://localhost:6379
99
main:
1010
allow-bean-definition-overriding: true
1111
allow-circular-references: true

0 commit comments

Comments
 (0)