Skip to content

Commit c990411

Browse files
authored
Merge pull request #612 from lowcoder-org/add-oauth-handling-to-graphql
Add OAuth(Inherit From Login) Handling To GraphQL API Datasource
2 parents 33e9b9a + 6ed2520 commit c990411

File tree

4 files changed

+104
-46
lines changed

4 files changed

+104
-46
lines changed

server/api-service/lowcoder-plugins/graphqlPlugin/src/main/java/org/lowcoder/plugin/graphql/GraphQLExecutor.java

Lines changed: 85 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.lowcoder.plugin.graphql;
22

33
import static com.google.common.base.MoreObjects.firstNonNull;
4+
import static org.apache.commons.collections4.MapUtils.emptyIfNull;
45
import static org.apache.commons.lang3.StringUtils.firstNonBlank;
56
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
67
import static org.lowcoder.plugin.graphql.GraphQLError.GRAPHQL_EXECUTION_ERROR;
@@ -10,6 +11,8 @@
1011
import static org.lowcoder.sdk.exception.PluginCommonError.QUERY_EXECUTION_ERROR;
1112
import static org.lowcoder.sdk.exception.PluginCommonError.QUERY_EXECUTION_TIMEOUT;
1213
import static org.lowcoder.sdk.plugin.restapi.auth.RestApiAuthType.DIGEST_AUTH;
14+
import static org.lowcoder.sdk.plugin.restapi.auth.RestApiAuthType.OAUTH2_INHERIT_FROM_LOGIN;
15+
import static org.lowcoder.sdk.util.ExceptionUtils.propagateError;
1316
import static org.lowcoder.sdk.util.JsonUtils.readTree;
1417
import static org.lowcoder.sdk.util.JsonUtils.toJsonThrows;
1518
import static org.lowcoder.sdk.util.MustacheHelper.renderMustacheString;
@@ -30,6 +33,8 @@
3033

3134
import javax.annotation.Nullable;
3235

36+
import com.google.common.collect.ImmutableMap;
37+
import org.apache.commons.collections4.CollectionUtils;
3338
import org.apache.commons.lang3.ObjectUtils;
3439
import org.apache.commons.lang3.StringUtils;
3540
import org.lowcoder.plugin.graphql.constants.ResponseDataType;
@@ -83,6 +88,8 @@
8388
public class GraphQLExecutor implements QueryExecutor<GraphQLDatasourceConfig, Object, GraphQLQueryExecutionContext> {
8489
private static final String RESPONSE_DATA_TYPE = "X-LOWCODER-RESPONSE-DATA-TYPE";
8590
private static final String GRAPHQL_TYPE = "application/graphql";
91+
92+
private static final String DEFAULT_GRAPHQL_ERROR_CODE = "GRAPHQL_EXECUTION_ERROR";
8693
private static final int MAX_REDIRECTS = 5;
8794
private static final Set<String> BINARY_DATA_TYPES = Set.of("application/zip",
8895
"application/octet-stream",
@@ -245,53 +252,54 @@ private List<Property> buildBodyParams(List<Property> datasourceBodyFormData, Li
245252

246253
@Override
247254
public Mono<QueryExecutionResult> executeQuery(Object o, GraphQLQueryExecutionContext context) {
248-
return Mono.defer(() -> {
249-
URI uri = RestApiUriBuilder.buildUri(context.getUrl(), new HashMap<>(), context.getUrlParams());
250-
WebClient.Builder webClientBuilder = WebClientBuildHelper.builder()
251-
.disallowedHosts(commonConfig.getDisallowedHosts())
252-
.toWebClientBuilder();
253-
254-
Map<String, String> allHeaders = context.getHeaders();
255-
String contentType = context.getContentType();
256-
allHeaders.forEach(webClientBuilder::defaultHeader);
257-
258-
//basic auth
259-
AuthConfig authConfig = context.getAuthConfig();
260-
if (authConfig != null && authConfig.getType() == RestApiAuthType.BASIC_AUTH) {
261-
webClientBuilder.defaultHeaders(AuthHelper.basicAuth((BasicAuthConfig) authConfig));
262-
}
255+
return Mono.defer(() -> authByOauth2InheritFromLogin(context))
256+
.then(Mono.defer(() -> {
257+
URI uri = RestApiUriBuilder.buildUri(context.getUrl(), new HashMap<>(), context.getUrlParams());
258+
WebClient.Builder webClientBuilder = WebClientBuildHelper.builder()
259+
.disallowedHosts(commonConfig.getDisallowedHosts())
260+
.toWebClientBuilder();
261+
262+
Map<String, String> allHeaders = context.getHeaders();
263+
String contentType = context.getContentType();
264+
allHeaders.forEach(webClientBuilder::defaultHeader);
265+
266+
//basic auth
267+
AuthConfig authConfig = context.getAuthConfig();
268+
if (authConfig != null && authConfig.getType() == RestApiAuthType.BASIC_AUTH) {
269+
webClientBuilder.defaultHeaders(AuthHelper.basicAuth((BasicAuthConfig) authConfig));
270+
}
263271

264-
if (MediaType.MULTIPART_FORM_DATA_VALUE.equals(contentType)) {
265-
webClientBuilder.filter(new BufferingFilter());
266-
}
272+
if (MediaType.MULTIPART_FORM_DATA_VALUE.equals(contentType)) {
273+
webClientBuilder.filter(new BufferingFilter());
274+
}
267275

268-
webClientBuilder.defaultCookies(injectCookies(context));
276+
webClientBuilder.defaultCookies(injectCookies(context));
269277

270-
WebClient client = webClientBuilder
271-
.exchangeStrategies(EXCHANGE_STRATEGIES)
272-
.build();
273-
if (!GRAPHQL_TYPE.equalsIgnoreCase(contentType)) {
274-
context.setQueryBody(convertToGraphQLBody(context));
275-
}
276-
BodyInserter<?, ? super ClientHttpRequest> bodyInserter = buildBodyInserter(
277-
context.isEncodeParams(),
278-
contentType,
279-
context.getQueryBody(),
280-
context.getBodyParams());
281-
return httpCall(client, context.getHttpMethod(), uri, bodyInserter, 0, authConfig, DEFAULT_HEADERS_CONSUMER)
282-
.flatMap(clientResponse -> clientResponse.toEntity(byte[].class))
283-
.map(this::convertToQueryExecutionResult)
284-
.onErrorResume(error -> {
285-
if (error instanceof TimeoutException) {
286-
return Mono.just(QueryExecutionResult.error(QUERY_EXECUTION_TIMEOUT, "QUERY_TIMEOUT_ERROR", error));
287-
}
288-
if (error instanceof PluginException pluginException) {
289-
throw pluginException;
290-
}
291-
return Mono.just(
292-
QueryExecutionResult.error(GRAPHQL_EXECUTION_ERROR, "GRAPHQL_EXECUTION_ERROR", error));
293-
});
294-
});
278+
WebClient client = webClientBuilder
279+
.exchangeStrategies(EXCHANGE_STRATEGIES)
280+
.build();
281+
if (!GRAPHQL_TYPE.equalsIgnoreCase(contentType)) {
282+
context.setQueryBody(convertToGraphQLBody(context));
283+
}
284+
BodyInserter<?, ? super ClientHttpRequest> bodyInserter = buildBodyInserter(
285+
context.isEncodeParams(),
286+
contentType,
287+
context.getQueryBody(),
288+
context.getBodyParams());
289+
return httpCall(client, context.getHttpMethod(), uri, bodyInserter, 0, authConfig, DEFAULT_HEADERS_CONSUMER)
290+
.flatMap(clientResponse -> clientResponse.toEntity(byte[].class))
291+
.map(this::convertToQueryExecutionResult)
292+
.onErrorResume(error -> {
293+
if (error instanceof TimeoutException) {
294+
return Mono.just(QueryExecutionResult.error(QUERY_EXECUTION_TIMEOUT, "QUERY_TIMEOUT_ERROR", error));
295+
}
296+
if (error instanceof PluginException pluginException) {
297+
throw pluginException;
298+
}
299+
return Mono.just(
300+
QueryExecutionResult.error(GRAPHQL_EXECUTION_ERROR, "GRAPHQL_EXECUTION_ERROR", error));
301+
});
302+
}));
295303
}
296304

297305
private Consumer<MultiValueMap<String, String>> injectCookies(GraphQLQueryExecutionContext request) {
@@ -458,6 +466,39 @@ private ResponseBodyData parseResponseDataInfo(byte[] body, MediaType contentTyp
458466
}
459467
}
460468

469+
private Mono<Void> authByOauth2InheritFromLogin(GraphQLQueryExecutionContext context) {
470+
if (context.getAuthConfig() == null || context.getAuthConfig().getType() != OAUTH2_INHERIT_FROM_LOGIN) {
471+
return Mono.empty();
472+
}
473+
return context.getAuthTokenMono()
474+
.doOnNext(properties -> {
475+
Map<String, List<Property>> propertyMap = properties.stream()
476+
.collect(Collectors.groupingBy(Property::getType));
477+
478+
List<Property> params = propertyMap.get("param");
479+
if (CollectionUtils.isNotEmpty(params)) {
480+
Map<String, String> paramMap = new HashMap<>(emptyIfNull(context.getUrlParams()));
481+
for (Property param : params) {
482+
paramMap.put(param.getKey(), param.getValue());
483+
}
484+
context.setUrlParams(ImmutableMap.copyOf(paramMap));
485+
}
486+
487+
List<Property> headers = propertyMap.get("header");
488+
if (CollectionUtils.isNotEmpty(headers)) {
489+
Map<String, String> headerMap = new HashMap<>(emptyIfNull(context.getHeaders()));
490+
for (Property header : headers) {
491+
headerMap.put(header.getKey(), header.getValue());
492+
}
493+
context.setHeaders(ImmutableMap.copyOf(headerMap));
494+
}
495+
})
496+
.switchIfEmpty(Mono.error(new PluginException(GRAPHQL_EXECUTION_ERROR, DEFAULT_GRAPHQL_ERROR_CODE,
497+
"$ACCESS_TOKEN parameter missing.")))
498+
.onErrorResume(throwable -> propagateError(GRAPHQL_EXECUTION_ERROR, DEFAULT_GRAPHQL_ERROR_CODE, throwable))
499+
.then();
500+
}
501+
461502
@Getter
462503
@Builder
463504
private static class ResponseBodyData {

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/plugin/graphql/GraphQLDatasourceConfig.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ public boolean isForwardAllCookies() {
110110
return forwardAllCookies;
111111
}
112112

113+
public boolean isOauth2InheritFromLogin() {
114+
if (this.authConfig != null) {
115+
return this.authConfig.getType().name().equals(RestApiAuthType.OAUTH2_INHERIT_FROM_LOGIN.name());
116+
}
117+
return false;
118+
}
119+
113120
@Override
114121
public DatasourceConnectionConfig mergeWithUpdatedConfig(DatasourceConnectionConfig updatedConfig) {
115122
if (!(updatedConfig instanceof GraphQLDatasourceConfig updatedApiConfig)) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.lowcoder.sdk.exception.BizError;
2424
import org.lowcoder.sdk.models.Property;
2525
import org.lowcoder.sdk.models.QueryExecutionResult;
26+
import org.lowcoder.sdk.plugin.graphql.GraphQLDatasourceConfig;
2627
import org.lowcoder.sdk.plugin.restapi.RestApiDatasourceConfig;
2728
import org.lowcoder.sdk.plugin.restapi.auth.OAuthInheritAuthConfig;
2829
import org.lowcoder.sdk.query.QueryVisitorContext;
@@ -122,7 +123,11 @@ public Mono<QueryExecutionResult> executeApplicationQuery(ServerWebExchange exch
122123
if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig
123124
&& restApiDatasourceConfig.isOauth2InheritFromLogin()) {
124125
paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId());
126+
}
125127

128+
if(datasource.getDetailConfig() instanceof GraphQLDatasourceConfig graphQLDatasourceConfig
129+
&& graphQLDatasourceConfig.isOauth2InheritFromLogin()) {
130+
paramsAndHeadersInheritFromLogin = getAuthParamsAndHeadersInheritFromLogin(tuple.getT1(), ((OAuthInheritAuthConfig)graphQLDatasourceConfig.getAuthConfig()).getAuthId());
126131
}
127132

128133
QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, app.getOrganizationId(), port, cookies, paramsAndHeadersInheritFromLogin, commonConfig.getDisallowedHosts());

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
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.graphql.GraphQLDatasourceConfig;
4546
import org.lowcoder.sdk.plugin.restapi.RestApiDatasourceConfig;
4647
import org.lowcoder.sdk.plugin.restapi.auth.OAuthInheritAuthConfig;
4748
import org.lowcoder.sdk.query.QueryVisitorContext;
@@ -310,12 +311,16 @@ public Mono<QueryExecutionResult> executeLibraryQuery(ServerWebExchange exchange
310311

311312

312313
// check if oauth inherited from login and save token
313-
if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig
314-
&& restApiDatasourceConfig.isOauth2InheritFromLogin()) {
314+
if(datasource.getDetailConfig() instanceof RestApiDatasourceConfig restApiDatasourceConfig && restApiDatasourceConfig.isOauth2InheritFromLogin()) {
315315
paramsAndHeadersInheritFromLogin = getParamsAndHeadersInheritFromLogin
316316
(user, ((OAuthInheritAuthConfig)restApiDatasourceConfig.getAuthConfig()).getAuthId());
317317
}
318318

319+
if(datasource.getDetailConfig() instanceof GraphQLDatasourceConfig graphQLDatasourceConfig && graphQLDatasourceConfig.isOauth2InheritFromLogin()) {
320+
paramsAndHeadersInheritFromLogin = getParamsAndHeadersInheritFromLogin
321+
(user, ((OAuthInheritAuthConfig)graphQLDatasourceConfig.getAuthConfig()).getAuthId());
322+
}
323+
319324
QueryVisitorContext queryVisitorContext = new QueryVisitorContext(userId, orgId, port, cookies, paramsAndHeadersInheritFromLogin,
320325
commonConfig.getDisallowedHosts());
321326
Map<String, Object> queryConfig = baseQuery.getQueryConfig();

0 commit comments

Comments
 (0)