diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/query/service/QueryExecutionService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/query/service/QueryExecutionService.java index dc345993c..f58887d38 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/query/service/QueryExecutionService.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/query/service/QueryExecutionService.java @@ -1,15 +1,6 @@ package org.lowcoder.domain.query.service; -import static org.lowcoder.sdk.exception.BizError.QUERY_EXECUTION_ERROR; -import static org.lowcoder.sdk.exception.PluginCommonError.QUERY_EXECUTION_TIMEOUT; -import static org.lowcoder.sdk.util.ExceptionUtils.ofException; - -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - +import lombok.extern.slf4j.Slf4j; import org.lowcoder.domain.datasource.model.Datasource; import org.lowcoder.domain.datasource.model.DatasourceConnectionHolder; import org.lowcoder.domain.datasource.service.DatasourceConnectionPool; @@ -24,10 +15,18 @@ import org.lowcoder.sdk.query.QueryVisitorContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; - -import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Mono; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import static org.lowcoder.sdk.exception.BizError.QUERY_EXECUTION_ERROR; +import static org.lowcoder.sdk.exception.PluginCommonError.QUERY_EXECUTION_TIMEOUT; +import static org.lowcoder.sdk.util.ExceptionUtils.ofException; + @Slf4j @Service public class QueryExecutionService { diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/RestApiDatasourceConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/RestApiDatasourceConfig.java index b4924e985..f60caa9bb 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/RestApiDatasourceConfig.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/RestApiDatasourceConfig.java @@ -108,6 +108,13 @@ public RestApiAuthType getAuthType() { return RestApiAuthType.NO_AUTH; } + public boolean isOauth2InheritFromLogin() { + if (this.authConfig != null) { + return this.authConfig.getType().name().equals(RestApiAuthType.OAUTH2_INHERIT_FROM_LOGIN.name()); + } + return false; + } + public Set getForwardCookies() { return SetUtils.emptyIfNull(forwardCookies); } diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/AuthConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/AuthConfig.java index 16b85fd85..599896a5b 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/AuthConfig.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/AuthConfig.java @@ -17,7 +17,7 @@ @Type(value = BasicAuthConfig.class, name = "DIGEST_AUTH"), @Type(value = BasicAuthConfig.class, name = "BASIC_AUTH"), @Type(value = NoneAuthConfig.class, name = "NO_AUTH"), - @Type(value = DefaultAuthConfig.class, name = "OAUTH2_INHERIT_FROM_LOGIN") + @Type(value = OAuthInheritAuthConfig.class, name = "OAUTH2_INHERIT_FROM_LOGIN") }) public abstract class AuthConfig implements Encrypt { diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/OAuthInheritAuthConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/OAuthInheritAuthConfig.java new file mode 100644 index 000000000..08e56a26c --- /dev/null +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/restapi/auth/OAuthInheritAuthConfig.java @@ -0,0 +1,32 @@ +package org.lowcoder.sdk.plugin.restapi.auth; + +import com.fasterxml.jackson.annotation.JsonCreator; +import lombok.Getter; + +import javax.annotation.Nullable; + +/** + * oauth(inherit from login) auth config + */ +@Getter +public final class OAuthInheritAuthConfig extends AuthConfig { + + private String authId; + + @JsonCreator + public OAuthInheritAuthConfig(String authId, RestApiAuthType type) { + super(type); + this.authId = authId; + } + + @Override + public AuthConfig mergeWithUpdatedConfig(@Nullable AuthConfig updatedConfig) { + // return new auth config if auth type changed + if (!(updatedConfig instanceof OAuthInheritAuthConfig oAuthInheritAuthConfig)) { + return updatedConfig; + } + // otherwise merge oauth auth config + return new OAuthInheritAuthConfig(oAuthInheritAuthConfig.getAuthId(), + oAuthInheritAuthConfig.getType()); + } +} diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/ApplicationQueryApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/ApplicationQueryApiService.java index 2bf1ece3a..669bd501d 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/ApplicationQueryApiService.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/ApplicationQueryApiService.java @@ -1,16 +1,5 @@ package org.lowcoder.api.query; -import static org.lowcoder.domain.permission.model.ResourceAction.READ_APPLICATIONS; -import static org.lowcoder.sdk.exception.BizError.DATASOURCE_AND_APP_ORG_NOT_MATCH; -import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER; -import static org.lowcoder.sdk.util.ExceptionUtils.deferredError; -import static org.lowcoder.sdk.util.ExceptionUtils.ofError; - -import java.util.List; -import java.util.stream.Collectors; - -import javax.annotation.Nullable; - import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.lowcoder.api.home.SessionUserService; @@ -27,11 +16,15 @@ import org.lowcoder.domain.query.service.LibraryQueryRecordService; import org.lowcoder.domain.query.service.LibraryQueryService; import org.lowcoder.domain.query.service.QueryExecutionService; +import org.lowcoder.domain.user.model.Connection; +import org.lowcoder.domain.user.model.User; import org.lowcoder.infra.util.TupleUtils; import org.lowcoder.sdk.config.CommonConfig; import org.lowcoder.sdk.exception.BizError; import org.lowcoder.sdk.models.Property; import org.lowcoder.sdk.models.QueryExecutionResult; +import org.lowcoder.sdk.plugin.restapi.RestApiDatasourceConfig; +import org.lowcoder.sdk.plugin.restapi.auth.OAuthInheritAuthConfig; import org.lowcoder.sdk.query.QueryVisitorContext; import org.lowcoder.sdk.util.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -40,10 +33,21 @@ import org.springframework.stereotype.Service; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; - import reactor.core.publisher.Mono; import reactor.core.publisher.Timed; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.lowcoder.domain.permission.model.ResourceAction.READ_APPLICATIONS; +import static org.lowcoder.sdk.exception.BizError.DATASOURCE_AND_APP_ORG_NOT_MATCH; +import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER; +import static org.lowcoder.sdk.util.ExceptionUtils.deferredError; +import static org.lowcoder.sdk.util.ExceptionUtils.ofError; + @Service public class ApplicationQueryApiService { @@ -93,12 +97,13 @@ public Mono executeApplicationQuery(ServerWebExchange exch Mono datasourceMono = baseQueryMono.flatMap(query -> datasourceService.getById(query.getDatasourceId()) .switchIfEmpty(deferredError(BizError.DATASOURCE_NOT_FOUND, "DATASOURCE_NOT_FOUND", query.getDatasourceId()))) .cache(); - return sessionUserService.getVisitorId() - .delayUntil(userId -> checkExecutePermission(userId, queryExecutionRequest.getPath(), appId, + + return sessionUserService.getVisitor() + .delayUntil(user -> checkExecutePermission(user.getId(), queryExecutionRequest.getPath(), appId, queryExecutionRequest.isViewMode())) .zipWhen(visitorId -> Mono.zip(appMono, appQueryMono, baseQueryMono, datasourceMono), TupleUtils::merge) .flatMap(tuple -> { - String userId = tuple.getT1(); + String userId = tuple.getT1().getId(); Application app = tuple.getT2(); ApplicationQuery appQuery = tuple.getT3(); BaseQuery baseQuery = tuple.getT4(); @@ -109,8 +114,18 @@ public Mono executeApplicationQuery(ServerWebExchange exch } MultiValueMap cookies = exchange.getRequest().getCookies(); - QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, app.getOrganizationId(), port, cookies, - getAuthParamsAndHeadersInheritFromLogin(userId, app.getOrganizationId()), commonConfig.getDisallowedHosts()); + + Mono> paramsAndHeadersInheritFromLogin = Mono.empty(); + + + // Check if oauth inherited from login and save token + if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig + && restApiDatasourceConfig.isOauth2InheritFromLogin()) { + paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId()); + + } + + QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, app.getOrganizationId(), port, cookies, paramsAndHeadersInheritFromLogin, commonConfig.getDisallowedHosts()); return queryExecutionService.executeQuery(datasource, baseQuery.getQueryConfig(), queryExecutionRequest.paramMap(), appQuery.getTimeoutStr(), queryVisitorContext ) @@ -176,8 +191,18 @@ private Mono getBaseQueryFromLibraryQuery(ApplicationQuery query) { .map(LibraryQueryRecord::getQuery); } - protected Mono> getAuthParamsAndHeadersInheritFromLogin(String userId, String orgId) { - return Mono.empty(); + protected Mono> getAuthParamsAndHeadersInheritFromLogin(User user, String authId) { + if(authId == null) { + return Mono.empty(); + } + Optional activeConnectionOptional = user.getConnections() + .stream() + .filter(connection -> connection.getAuthId().equals(authId)) + .findFirst(); + if(!activeConnectionOptional.isPresent() || activeConnectionOptional.get().getAuthConnectionAuthToken() == null) { + return Mono.empty(); + } + return Mono.just(Collections.singletonList(new Property("Authorization","Bearer " + activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header"))); } protected void onNextOrError(QueryExecutionRequest queryExecutionRequest, QueryVisitorContext queryVisitorContext, diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java index 52768afbf..c9eb022d5 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java @@ -6,9 +6,7 @@ import static org.lowcoder.sdk.util.ExceptionUtils.deferredError; import static org.lowcoder.sdk.util.ExceptionUtils.ofError; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -23,6 +21,7 @@ import org.lowcoder.api.usermanagement.OrgDevChecker; import org.lowcoder.api.util.BusinessEventPublisher; import org.lowcoder.api.util.ViewBuilder; +import org.lowcoder.domain.authentication.AuthenticationService; import org.lowcoder.domain.datasource.model.Datasource; import org.lowcoder.domain.datasource.service.DatasourceService; import org.lowcoder.domain.organization.model.OrgMember; @@ -35,6 +34,7 @@ import org.lowcoder.domain.query.service.LibraryQueryRecordService; import org.lowcoder.domain.query.service.LibraryQueryService; import org.lowcoder.domain.query.service.QueryExecutionService; +import org.lowcoder.domain.user.model.Connection; import org.lowcoder.domain.user.model.User; import org.lowcoder.domain.user.service.UserService; import org.lowcoder.sdk.config.CommonConfig; @@ -42,6 +42,8 @@ import org.lowcoder.sdk.exception.PluginCommonError; import org.lowcoder.sdk.models.Property; import org.lowcoder.sdk.models.QueryExecutionResult; +import org.lowcoder.sdk.plugin.restapi.RestApiDatasourceConfig; +import org.lowcoder.sdk.plugin.restapi.auth.OAuthInheritAuthConfig; import org.lowcoder.sdk.query.QueryVisitorContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -87,6 +89,9 @@ public class LibraryQueryApiService { @Autowired private CommonConfig commonConfig; + @Autowired + private AuthenticationService authenticationService; + @Value("${server.port}") private int port; @@ -245,15 +250,19 @@ public Mono executeLibraryQueryFromJs(ServerWebExchange ex Mono visitorOrgMemberCache = sessionUserService.getVisitorOrgMemberCache() .onErrorReturn(NOT_EXIST); - return Mono.zip(visitorOrgMemberCache, baseQueryMono, datasourceMono) + + Mono userMono = sessionUserService.getVisitor(); + + return Mono.zip(visitorOrgMemberCache, baseQueryMono, datasourceMono, userMono) .flatMap(tuple -> { OrgMember orgMember = tuple.getT1(); String orgId = orgMember.getOrgId(); String userId = orgMember.getUserId(); BaseQuery baseQuery = tuple.getT2(); Datasource datasource = tuple.getT3(); + User user = tuple.getT4(); Mono> paramsAndHeadersInheritFromLogin = orgMember.isInvalid() - ? Mono.empty() : getParamsAndHeadersInheritFromLogin(userId, orgId); + ? Mono.empty() : getParamsAndHeadersInheritFromLogin(user, null); QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, orgId, port, exchange.getRequest().getCookies(), @@ -284,17 +293,29 @@ public Mono executeLibraryQuery(ServerWebExchange exchange Mono datasourceMono = baseQueryMono.flatMap(query -> datasourceService.getById(query.getDatasourceId()) .switchIfEmpty(deferredError(BizError.DATASOURCE_NOT_FOUND, "DATASOURCE_NOT_FOUND", query.getDatasourceId()))).cache(); + Mono userMono = sessionUserService.getVisitor(); + return orgDevChecker.checkCurrentOrgDev() .then(Mono.zip(sessionUserService.getVisitorOrgMemberCache(), - baseQueryMono, datasourceMono)) + baseQueryMono, datasourceMono, userMono)) .flatMap(tuple -> { OrgMember orgMember = tuple.getT1(); String orgId = orgMember.getOrgId(); String userId = orgMember.getUserId(); BaseQuery baseQuery = tuple.getT2(); Datasource datasource = tuple.getT3(); - Mono> paramsAndHeadersInheritFromLogin = - getParamsAndHeadersInheritFromLogin(userId, orgId); + User user = tuple.getT4(); + + Mono> paramsAndHeadersInheritFromLogin = Mono.empty(); + + + // check if oauth inherited from login and save token + if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig + && restApiDatasourceConfig.isOauth2InheritFromLogin()) { + paramsAndHeadersInheritFromLogin = getParamsAndHeadersInheritFromLogin + (user, ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId()); + } + QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, orgId, port, cookies, paramsAndHeadersInheritFromLogin, commonConfig.getDisallowedHosts()); Map queryConfig = baseQuery.getQueryConfig(); @@ -322,8 +343,18 @@ private Mono getBaseQuery(LibraryQueryCombineId libraryQueryCombineId .map(LibraryQueryRecord::getQuery); } - protected Mono> getParamsAndHeadersInheritFromLogin(String userId, String orgId) { - return Mono.empty(); + protected Mono> getParamsAndHeadersInheritFromLogin(User user, String authId) { + if(authId == null) { + return Mono.empty(); + } + Optional activeConnectionOptional = user.getConnections() + .stream() + .filter(connection -> connection.getAuthId().equals(authId)) + .findFirst(); + if(!activeConnectionOptional.isPresent() || activeConnectionOptional.get().getAuthConnectionAuthToken() == null) { + return Mono.empty(); + } + return Mono.just(Collections.singletonList(new Property("Authorization","Bearer " + activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header"))); } protected void onNextOrError(QueryExecutionRequest queryExecutionRequest, QueryVisitorContext queryVisitorContext, BaseQuery baseQuery, diff --git a/server/api-service/lowcoder-server/src/main/resources/application-lowcoder.yml b/server/api-service/lowcoder-server/src/main/resources/application-lowcoder.yml index b223846d4..d3c248caa 100644 --- a/server/api-service/lowcoder-server/src/main/resources/application-lowcoder.yml +++ b/server/api-service/lowcoder-server/src/main/resources/application-lowcoder.yml @@ -3,9 +3,9 @@ spring: mongodb: authentication-database: admin auto-index-creation: false - uri: mongodb://192.168.8.103:27017/lowcoder?authSource=admin + uri: mongodb://localhost:27017/lowcoder?authSource=admin redis: - url: redis://192.168.8.103:6379 + url: redis://localhost:6379 main: allow-bean-definition-overriding: true allow-circular-references: true