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 f58887d38..6a4d1fb02 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 @@ -10,6 +10,8 @@ import org.lowcoder.sdk.config.CommonConfig; import org.lowcoder.sdk.exception.BizException; import org.lowcoder.sdk.exception.PluginException; +import org.lowcoder.sdk.models.JsDatasourceConnectionConfig; +import org.lowcoder.sdk.models.Property; import org.lowcoder.sdk.models.QueryExecutionResult; import org.lowcoder.sdk.query.QueryExecutionContext; import org.lowcoder.sdk.query.QueryVisitorContext; @@ -18,6 +20,7 @@ import reactor.core.publisher.Mono; import java.time.Duration; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeoutException; @@ -94,6 +97,26 @@ private Mono executeByNodeJs(Datasource datasource, Map injectOauth2Token(queryVisitorContext, context)) + .then(Mono.defer(() -> datasourcePluginClient.executeQuery(datasource.getType(), queryConfig, context, datasource.getDetailConfig()))); + } else { + return datasourcePluginClient.executeQuery(datasource.getType(), queryConfig, context, datasource.getDetailConfig()); + } + + + } + + private Mono injectOauth2Token(QueryVisitorContext queryVisitorContext, List> context) { + return queryVisitorContext.getAuthTokenMono() + .doOnNext(properties -> { + for (Property property : properties) { + context.add(Map.of("key" , property.getKey(), "value", property.getValue())); + } + }) + .then(); } } diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/models/JsDatasourceConnectionConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/models/JsDatasourceConnectionConfig.java index 96749d228..752e89d11 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/models/JsDatasourceConnectionConfig.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/models/JsDatasourceConnectionConfig.java @@ -16,6 +16,7 @@ import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.ObjectUtils; +import org.lowcoder.sdk.plugin.restapi.auth.RestApiAuthType; import org.springframework.data.annotation.Transient; import lombok.Getter; @@ -159,6 +160,20 @@ public DatasourceConnectionConfig mergeWithUpdatedConfig(DatasourceConnectionCon if (this.containsKey("extra") || jsDatasourceConnectionConfig.containsKey("extra")) { newJsDatasourceConnectionConfig.putIfAbsent("extra", ObjectUtils.firstNonNull(jsDatasourceConnectionConfig.getExtra(), this.getExtra())); } + + // for oauth handling + if(this.containsKey("authConfig")) { + if(jsDatasourceConnectionConfig.containsKey("authConfig")) { + newJsDatasourceConnectionConfig.put("authConfig", jsDatasourceConnectionConfig.get("authConfig")); + } else { + // do nothing, save empty ( this will clear db ) + } + } else { + if(jsDatasourceConnectionConfig.containsKey("authConfig")) { + newJsDatasourceConnectionConfig.put("authConfig", jsDatasourceConnectionConfig.get("authConfig")); + } + } + return newJsDatasourceConnectionConfig; } @@ -199,4 +214,18 @@ private DatasourceConnectionConfig doEncryptOrDecrypt(Function e return this; } + + public boolean isOauth2InheritFromLogin() { + if (this.get("authConfig") != null) { + return ((HashMap)this.get("authConfig")).get("type").equals(RestApiAuthType.OAUTH2_INHERIT_FROM_LOGIN.name()); + } + return false; + } + + public String getAuthId() { + if(isOauth2InheritFromLogin()) { + return ((HashMap)this.get("authConfig")).get("authId"); + } + return null; + } } 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 dbf77db11..9a1242691 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 @@ -21,6 +21,7 @@ import org.lowcoder.infra.util.TupleUtils; import org.lowcoder.sdk.config.CommonConfig; import org.lowcoder.sdk.exception.BizError; +import org.lowcoder.sdk.models.JsDatasourceConnectionConfig; import org.lowcoder.sdk.models.Property; import org.lowcoder.sdk.models.QueryExecutionResult; import org.lowcoder.sdk.plugin.graphql.GraphQLDatasourceConfig; @@ -122,12 +123,18 @@ public Mono executeApplicationQuery(ServerWebExchange exch // 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()); + paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId(), false); } if(datasource.getDetailConfig() instanceof GraphQLDatasourceConfig graphQLDatasourceConfig && graphQLDatasourceConfig.isOauth2InheritFromLogin()) { - paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), ((OAuthInheritAuthConfig)graphQLDatasourceConfig.getAuthConfig()).getAuthId()); + paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), ((OAuthInheritAuthConfig)graphQLDatasourceConfig.getAuthConfig()).getAuthId(), false); + } + + + if(datasource.getDetailConfig() instanceof JsDatasourceConnectionConfig jsDatasourceConnectionConfig + && jsDatasourceConnectionConfig.isOauth2InheritFromLogin()) { + paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), jsDatasourceConnectionConfig.getAuthId(), true); } QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, app.getOrganizationId(), port, cookies, paramsAndHeadersInheritFromLogin, commonConfig.getDisallowedHosts()); @@ -196,7 +203,7 @@ private Mono getBaseQueryFromLibraryQuery(ApplicationQuery query) { .map(LibraryQueryRecord::getQuery); } - protected Mono> getAuthParamsAndHeadersInheritFromLogin(User user, String authId) { + protected Mono> getAuthParamsAndHeadersInheritFromLogin(User user, String authId, boolean isJsQuery) { if(authId == null) { return Mono.empty(); } @@ -207,7 +214,11 @@ protected Mono> getAuthParamsAndHeadersInheritFromLogin(User user if(!activeConnectionOptional.isPresent() || activeConnectionOptional.get().getAuthConnectionAuthToken() == null) { return Mono.empty(); } - return Mono.just(Collections.singletonList(new Property("Authorization","Bearer " + activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header"))); + if(isJsQuery) { + return Mono.just(Collections.singletonList(new Property("OAUTH_ACCESS_TOKEN",activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header"))); + } else { + 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 663a3be5d..a86495ac5 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 @@ -40,6 +40,7 @@ import org.lowcoder.sdk.config.CommonConfig; import org.lowcoder.sdk.exception.BizError; import org.lowcoder.sdk.exception.PluginCommonError; +import org.lowcoder.sdk.models.JsDatasourceConnectionConfig; import org.lowcoder.sdk.models.Property; import org.lowcoder.sdk.models.QueryExecutionResult; import org.lowcoder.sdk.plugin.graphql.GraphQLDatasourceConfig; @@ -263,7 +264,7 @@ public Mono executeLibraryQueryFromJs(ServerWebExchange ex Datasource datasource = tuple.getT3(); User user = tuple.getT4(); Mono> paramsAndHeadersInheritFromLogin = orgMember.isInvalid() - ? Mono.empty() : getParamsAndHeadersInheritFromLogin(user, null); + ? Mono.empty() : getParamsAndHeadersInheritFromLogin(user, null, false); QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, orgId, port, exchange.getRequest().getCookies(), @@ -313,12 +314,18 @@ public Mono executeLibraryQuery(ServerWebExchange exchange // check if oauth inherited from login and save token if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig && restApiDatasourceConfig.isOauth2InheritFromLogin()) { paramsAndHeadersInheritFromLogin = getParamsAndHeadersInheritFromLogin - (user, ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId()); + (user, ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId(), false); } if(datasource.getDetailConfig() instanceof GraphQLDatasourceConfig graphQLDatasourceConfig && graphQLDatasourceConfig.isOauth2InheritFromLogin()) { paramsAndHeadersInheritFromLogin = getParamsAndHeadersInheritFromLogin - (user, ((OAuthInheritAuthConfig)graphQLDatasourceConfig.getAuthConfig()).getAuthId()); + (user, ((OAuthInheritAuthConfig)graphQLDatasourceConfig.getAuthConfig()).getAuthId(), false); + } + + if(datasource.getDetailConfig() instanceof JsDatasourceConnectionConfig jsDatasourceConnectionConfig + && jsDatasourceConnectionConfig.isOauth2InheritFromLogin()) { + paramsAndHeadersInheritFromLogin = getParamsAndHeadersInheritFromLogin + (user, jsDatasourceConnectionConfig.getAuthId(), true); } QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, orgId, port, cookies, paramsAndHeadersInheritFromLogin, @@ -348,7 +355,7 @@ private Mono getBaseQuery(LibraryQueryCombineId libraryQueryCombineId .map(LibraryQueryRecord::getQuery); } - protected Mono> getParamsAndHeadersInheritFromLogin(User user, String authId) { + protected Mono> getParamsAndHeadersInheritFromLogin(User user, String authId, boolean isJsQuery) { if(authId == null) { return Mono.empty(); } @@ -359,7 +366,11 @@ protected Mono> getParamsAndHeadersInheritFromLogin(User user, St if(!activeConnectionOptional.isPresent() || activeConnectionOptional.get().getAuthConnectionAuthToken() == null) { return Mono.empty(); } - return Mono.just(Collections.singletonList(new Property("Authorization","Bearer " + activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header"))); + if(isJsQuery) { + return Mono.just(Collections.singletonList(new Property("OAUTH_ACCESS_TOKEN",activeConnectionOptional.get().getAuthConnectionAuthToken().getAccessToken(),"header"))); + } else { + 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/node-service/src/plugins/openApi/index.ts b/server/node-service/src/plugins/openApi/index.ts index 32e555ca4..27f9ed950 100644 --- a/server/node-service/src/plugins/openApi/index.ts +++ b/server/node-service/src/plugins/openApi/index.ts @@ -102,7 +102,7 @@ export async function runOpenApi( try { const { parameters, requestBody } = normalizeParams(otherActionData, operation, isOas3Spec); - const securities = extractSecurityParams(dataSourceConfig.dynamicParamsConfig, definition); + let securities = extractSecurityParams(dataSourceConfig, definition); const response = await SwaggerClient.execute({ spec: definition, operationId: realOperationId, diff --git a/server/node-service/src/plugins/openApi/util.test.ts b/server/node-service/src/plugins/openApi/util.test.ts index 9000de2ca..98f6a7a53 100644 --- a/server/node-service/src/plugins/openApi/util.test.ts +++ b/server/node-service/src/plugins/openApi/util.test.ts @@ -1,7 +1,7 @@ import { extractSecurityParams, getSchemaExample, extractLevelData, parseUrl } from "./util"; test("extractSecurityParams", () => { - const params = extractSecurityParams({ "ApiKeyAuth.value": "hello", ApiKeyAuth: null }, { + const params = extractSecurityParams({"dynamicParamsConfig":{ "ApiKeyAuth.value": "hello", ApiKeyAuth: null }}, { openapi: "3.0", components: { securitySchemes: { diff --git a/server/node-service/src/plugins/openApi/util.ts b/server/node-service/src/plugins/openApi/util.ts index 827042f28..4f8a9a262 100644 --- a/server/node-service/src/plugins/openApi/util.ts +++ b/server/node-service/src/plugins/openApi/util.ts @@ -81,7 +81,8 @@ interface NormalizedParams { requestBody?: any; } -export function extractSecurityParams(config: any, spec: OpenAPI.Document) { +export function extractSecurityParams(datasourceConfig: any, spec: OpenAPI.Document) { + const config = datasourceConfig.dynamicParamsConfig; if (!config) { return {}; } @@ -96,6 +97,18 @@ export function extractSecurityParams(config: any, spec: OpenAPI.Document) { names = Object.keys(swagger2Spec.securityDefinitions || {}); } const authorized = _.pick(authData, names); + + let oauthAccessToken = datasourceConfig["OAUTH_ACCESS_TOKEN"]; + + if(oauthAccessToken) { + return { + authorized: { + OAUTH_ACCESS_TOKEN: { value: oauthAccessToken } + }, + specSecurity: [] + }; + } + return { authorized, specSecurity: spec.security }; } diff --git a/server/node-service/src/services/plugin.ts b/server/node-service/src/services/plugin.ts index 6aa9c3ee2..2dbe984f2 100644 --- a/server/node-service/src/services/plugin.ts +++ b/server/node-service/src/services/plugin.ts @@ -212,9 +212,12 @@ export async function runPluginQuery( const queryConfig = await getQueryConfig(plugin, dataSourceConfig); const action = await evalToValue(queryConfig, dsl, context, dataSourceConfig); - //forward cookies + // forward cookies context.forEach(({ key, value }) => { - if (dataSourceConfig.dynamicParamsConfig && key in dataSourceConfig.dynamicParamsConfig) { + // for oauth(inherit from login) support + if(key == "OAUTH_ACCESS_TOKEN") { + dataSourceConfig["OAUTH_ACCESS_TOKEN"] = value + } else if (dataSourceConfig.dynamicParamsConfig && key in dataSourceConfig.dynamicParamsConfig) { const valueKey = `${key}.value`; dataSourceConfig.dynamicParamsConfig[valueKey] = value[0].value }