fix: Added support for self signed certificate during OAuth2 flows (#14719)

* Added support for self signed certificate during OAuth2 flows, server side changes

* fix: authentication.useSelfSignedCert key added

* Merging from release

* Fixed issue with dependencies, plus bug with using ssl

* Cypress test attempt 1

* Cypress test attempt 2

* Cypress test attempt 3

* Clean up

* Review comments

* Enabled compression again

Co-authored-by: Aman Agarwal <aman@appsmith.com>
This commit is contained in:
Nidhi 2022-07-21 16:10:36 +05:30 committed by GitHub
parent b16fadc763
commit 50172ecd64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 183 additions and 57 deletions

View File

@ -17,6 +17,7 @@ describe("Create a rest datasource", function() {
.trigger("click")
.wait(1000);
agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566
cy.testSelfSignedCertificateSettingsInREST(false);
cy.saveDatasource();
cy.contains(".datasource-highlight", "https://mock-api.appsmith.com");
cy.SaveAndRunAPI();

View File

@ -65,5 +65,9 @@
"mongoUriDropdown": "//p[text()='Use Mongo Connection String URI']/following-sibling::div",
"mongoUriYes": "//div[text()='Yes']",
"mongoUriInput":"//p[text()='Connection String URI']/following-sibling::div//input",
"advancedSettings": "[data-cy='section-Advanced Settings']",
"useSelfSignedCert": ".t--connection\\.ssl\\.authType",
"useCertInAuth": "[data-cy='authentication.useSelfSignedCert'] input",
"certificateDetails": "[data-cy='section-Certificate Details']",
"saveBtn": ".t--save-datasource"
}

View File

@ -15,7 +15,6 @@ const loginPage = require("../locators/LoginPage.json");
const signupPage = require("../locators/SignupPage.json");
import homePage from "../locators/HomePage";
const pages = require("../locators/Pages.json");
const datasourceEditor = require("../locators/DatasourcesEditor.json");
const datasourceFormData = require("../fixtures/datasources.json");
const commonlocators = require("../locators/commonlocators.json");
const queryEditor = require("../locators/QueryEditor.json");
@ -144,6 +143,7 @@ Cypress.Commands.add(
);
expect(firstTxt).to.equal(expectedvalue);
});
cy.testSelfSignedCertificateSettingsInREST(true);
},
);
@ -161,9 +161,25 @@ Cypress.Commands.add(
cy.xpath("//span[text()='Send client credentials in body']").should(
"be.visible",
);
cy.testSelfSignedCertificateSettingsInREST(true);
},
);
Cypress.Commands.add("testSelfSignedCertificateSettingsInREST", (isOAuth2) => {
cy.get(datasource.advancedSettings).click();
cy.get(datasource.useCertInAuth).should("not.exist");
cy.get(datasource.certificateDetails).should("not.exist");
cy.TargetDropdownAndSelectOption(datasource.useSelfSignedCert, "Yes");
if (isOAuth2) {
cy.get(datasource.useCertInAuth).should("exist");
} else {
cy.get(datasource.useCertInAuth).should("not.exist");
}
cy.get(datasource.certificateDetails).should("exist");
cy.TargetDropdownAndSelectOption(datasource.useSelfSignedCert, "No");
cy.get(datasource.advancedSettings).click();
});
Cypress.Commands.add("addBasicProfileDetails", (username, password) => {
cy.get(datasource.authType).click();
cy.xpath(datasource.basic).click();

View File

@ -39,6 +39,7 @@ function renderComponent(props: renderComponentProps) {
return (
<StyledCheckbox
cypressSelector={props?.input?.name}
isDefaultChecked={props?.input?.checked as boolean}
{...props}
info={undefined}

View File

@ -72,6 +72,7 @@ export interface Oauth2Common {
resource: string;
sendScopeWithRefreshToken: string;
refreshTokenClientCredentialsLocation: string;
useSelfSignedCert?: boolean;
}
export interface ClientCredentials extends Oauth2Common {

View File

@ -845,10 +845,13 @@ class DatasourceRestAPIEditor extends React.Component<
};
renderOauth2AdvancedSettings = () => {
const { authentication, authType } = this.props.formData;
const { authentication, authType, connection } = this.props.formData;
const isGrantTypeAuthorizationCode =
_.get(authentication, "grantType") === GrantType.AuthorizationCode;
const isAuthenticationTypeOAuth2 = authType === AuthType.OAuth2;
const isConnectSelfSigned =
_.get(connection, "ssl.authType") === SSLType.SELF_SIGNED_CERTIFICATE;
return (
<>
{isAuthenticationTypeOAuth2 && isGrantTypeAuthorizationCode && (
@ -919,6 +922,16 @@ class DatasourceRestAPIEditor extends React.Component<
"DEFAULT",
)}
</FormInputContainer>
{isAuthenticationTypeOAuth2 && isConnectSelfSigned && (
<FormInputContainer data-replay-id={btoa("selfsignedcert")}>
{this.renderCheckboxViaFormControl(
"authentication.useSelfSignedCert",
"Use Self-Signed Certificate for Authorization requests",
"",
false,
)}
</FormInputContainer>
)}
</>
);
};
@ -1127,6 +1140,30 @@ class DatasourceRestAPIEditor extends React.Component<
/>
);
}
renderCheckboxViaFormControl(
configProperty: string,
label: string,
placeholderText: string,
isRequired: boolean,
) {
return (
<FormControl
config={{
id: "",
isValid: false,
isRequired: isRequired,
controlType: "CHECKBOX",
configProperty: configProperty,
label: label,
conditionals: {},
placeholderText: placeholderText,
formName: DATASOURCE_REST_API_FORM,
}}
formName={DATASOURCE_REST_API_FORM}
/>
);
}
}
const mapStateToProps = (state: AppState, props: any) => {

View File

@ -104,6 +104,7 @@ const formToDatasourceAuthentication = (
sendScopeWithRefreshToken: authentication.sendScopeWithRefreshToken,
refreshTokenClientCredentialsLocation:
authentication.refreshTokenClientCredentialsLocation,
useSelfSignedCert: authentication.useSelfSignedCert,
};
if (isClientCredentials(authType, authentication)) {
return {

View File

@ -129,6 +129,11 @@
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -1,6 +1,10 @@
package com.appsmith.external.helpers;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.SSLDetails;
import com.appsmith.external.models.UploadedFile;
import reactor.netty.tcp.DefaultSslContextSpec;
import reactor.netty.tcp.SslProvider;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
@ -14,6 +18,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.function.Consumer;
public class SSLHelper {
@ -50,4 +55,26 @@ public class SSLHelper {
return trustManagerFactory;
}
public static Consumer<? super SslProvider.SslContextSpec> sslCheckForHttpClient(DatasourceConfiguration datasourceConfiguration) {
return (sslContextSpec) -> {
final DefaultSslContextSpec sslContextSpec1 = DefaultSslContextSpec.forClient();
if (datasourceConfiguration.getConnection() != null &&
datasourceConfiguration.getConnection().getSsl() != null &&
datasourceConfiguration.getConnection().getSsl().getAuthType() == SSLDetails.AuthType.SELF_SIGNED_CERTIFICATE) {
sslContextSpec1.configure(sslContextBuilder -> {
try {
final UploadedFile certificateFile = datasourceConfiguration.getConnection().getSsl().getCertificateFile();
sslContextBuilder.trustManager(SSLHelper.getSslTrustManagerFactory(certificateFile));
} catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
});
}
sslContextSpec.sslContext(sslContextSpec1);
};
}
}

View File

@ -76,6 +76,8 @@ public class OAuth2 extends AuthenticationDTO {
String resource;
boolean useSelfSignedCert = false;
public String getScopeString() {
if (scopeString != null && !scopeString.isBlank()) {
return scopeString;

View File

@ -1,7 +1,22 @@
package com.external.connections;
import com.appsmith.external.helpers.SSLHelper;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.OAuth2;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import reactor.netty.http.client.HttpClient;
// Parent type for all API connections that need to be created during datasource create method.
public abstract class APIConnection implements ExchangeFilterFunction {
HttpClient getSecuredHttpClient(DatasourceConfiguration datasourceConfiguration) {
final OAuth2 oAuth2 = (OAuth2) datasourceConfiguration.getAuthentication();
HttpClient httpClient = HttpClient.create();
if (oAuth2.isUseSelfSignedCert()) {
httpClient = httpClient.secure(SSLHelper.sslCheckForHttpClient(datasourceConfiguration));
}
return httpClient;
}
}

View File

@ -4,6 +4,7 @@ import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.BasicAuth;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.OAuth2;
import com.appsmith.external.models.ApiKeyAuth;
import com.appsmith.external.models.BearerTokenAuth;
@ -12,25 +13,26 @@ import reactor.core.publisher.Mono;
public class APIConnectionFactory {
public static Mono<APIConnection> createConnection(AuthenticationDTO authenticationType) {
if (authenticationType instanceof OAuth2) {
if (OAuth2.Type.CLIENT_CREDENTIALS.equals(((OAuth2) authenticationType).getGrantType())) {
return Mono.from(OAuth2ClientCredentials.create((OAuth2) authenticationType));
} else if (OAuth2.Type.AUTHORIZATION_CODE.equals(((OAuth2) authenticationType).getGrantType())) {
if (!Boolean.TRUE.equals(authenticationType.getIsAuthorized())) {
public static Mono<APIConnection> createConnection(DatasourceConfiguration datasourceConfiguration) {
final AuthenticationDTO authentication = datasourceConfiguration.getAuthentication();
if (authentication instanceof OAuth2) {
if (OAuth2.Type.CLIENT_CREDENTIALS.equals(((OAuth2) authentication).getGrantType())) {
return Mono.from(OAuth2ClientCredentials.create(datasourceConfiguration));
} else if (OAuth2.Type.AUTHORIZATION_CODE.equals(((OAuth2) authentication).getGrantType())) {
if (!Boolean.TRUE.equals(authentication.getIsAuthorized())) {
return Mono.error(new AppsmithPluginException(
AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, "Please authorize datasource"));
}
return Mono.from(OAuth2AuthorizationCode.create((OAuth2) authenticationType));
return Mono.from(OAuth2AuthorizationCode.create(datasourceConfiguration));
} else {
return Mono.empty();
}
} else if (authenticationType instanceof BasicAuth) {
return Mono.from(BasicAuthentication.create((BasicAuth) authenticationType));
} else if (authenticationType instanceof ApiKeyAuth) {
return Mono.from(ApiKeyAuthentication.create((ApiKeyAuth) authenticationType));
} else if (authenticationType instanceof BearerTokenAuth) {
return Mono.from(BearerTokenAuthentication.create((BearerTokenAuth) authenticationType));
} else if (authentication instanceof BasicAuth) {
return Mono.from(BasicAuthentication.create((BasicAuth) authentication));
} else if (authentication instanceof ApiKeyAuth) {
return Mono.from(ApiKeyAuthentication.create((ApiKeyAuth) authentication));
} else if (authentication instanceof BearerTokenAuth) {
return Mono.from(BearerTokenAuthentication.create((BearerTokenAuth) authentication));
} else {
return Mono.empty();
}

View File

@ -4,6 +4,7 @@ import com.appsmith.external.constants.Authentication;
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.AuthenticationResponse;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.OAuth2;
import com.appsmith.external.models.UpdatableConnection;
import lombok.AccessLevel;
@ -14,6 +15,7 @@ import org.bson.internal.Base64;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.BodyExtractors;
@ -25,6 +27,7 @@ import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import java.net.URI;
import java.time.Clock;
@ -69,15 +72,16 @@ public class OAuth2AuthorizationCode extends APIConnection implements UpdatableC
return true;
}
public static Mono<OAuth2AuthorizationCode> create(OAuth2 oAuth2) {
if (oAuth2 == null) {
public static Mono<OAuth2AuthorizationCode> create(DatasourceConfiguration datasourceConfiguration) {
if (datasourceConfiguration == null || datasourceConfiguration.getAuthentication() == null) {
return Mono.empty();
}
final OAuth2 oAuth2 = (OAuth2) datasourceConfiguration.getAuthentication();
// Create OAuth2Connection
OAuth2AuthorizationCode connection = new OAuth2AuthorizationCode();
if (!isAuthenticationResponseValid(oAuth2)) {
return connection.generateOAuth2Token(oAuth2)
return connection.generateOAuth2Token(datasourceConfiguration)
.flatMap(token -> {
updateConnection(connection, token);
return Mono.just(connection);
@ -100,8 +104,13 @@ public class OAuth2AuthorizationCode extends APIConnection implements UpdatableC
return now.isAfter(expiresAt.minus(Duration.ofMinutes(1)));
}
private Mono<OAuth2> generateOAuth2Token(OAuth2 oAuth2) {
private Mono<OAuth2> generateOAuth2Token(DatasourceConfiguration datasourceConfiguration) {
final OAuth2 oAuth2 = (OAuth2) datasourceConfiguration.getAuthentication();
final HttpClient securedHttpClient = this.getSecuredHttpClient(datasourceConfiguration);
// Webclient
WebClient.Builder webClientBuilder = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(securedHttpClient))
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.exchangeStrategies(ExchangeStrategies
.builder()

View File

@ -4,6 +4,7 @@ import com.appsmith.external.constants.Authentication;
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.AuthenticationResponse;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.OAuth2;
import com.appsmith.external.models.UpdatableConnection;
import lombok.AccessLevel;
@ -14,6 +15,7 @@ import org.bson.internal.Base64;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.BodyExtractors;
@ -25,6 +27,7 @@ import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import java.net.URI;
import java.time.Clock;
@ -45,10 +48,11 @@ public class OAuth2ClientCredentials extends APIConnection implements UpdatableC
private Object tokenResponse;
private static final int MAX_IN_MEMORY_SIZE = 10 * 1024 * 1024; // 10 MB
public static Mono<OAuth2ClientCredentials> create(OAuth2 oAuth2) {
if (oAuth2 == null) {
public static Mono<OAuth2ClientCredentials> create(DatasourceConfiguration datasourceConfiguration) {
if (datasourceConfiguration == null) {
return Mono.empty();
}
final OAuth2 oAuth2 = (OAuth2) datasourceConfiguration.getAuthentication();
// Create OAuth2Connection
OAuth2ClientCredentials connection = new OAuth2ClientCredentials();
@ -65,7 +69,7 @@ public class OAuth2ClientCredentials extends APIConnection implements UpdatableC
return now.isBefore(expiresAt.minus(Duration.ofMinutes(1)));
})
// If invalid, regenerate token
.switchIfEmpty(connection.generateOAuth2Token(oAuth2))
.switchIfEmpty(connection.generateOAuth2Token(datasourceConfiguration))
// Store valid token
.flatMap(token -> {
connection.setToken(token.getAuthenticationResponse().getToken());
@ -77,9 +81,13 @@ public class OAuth2ClientCredentials extends APIConnection implements UpdatableC
});
}
private Mono<OAuth2> generateOAuth2Token(OAuth2 oAuth2) {
private Mono<OAuth2> generateOAuth2Token(DatasourceConfiguration datasourceConfiguration) {
final OAuth2 oAuth2 = (OAuth2) datasourceConfiguration.getAuthentication();
final HttpClient securedHttpClient = this.getSecuredHttpClient(datasourceConfiguration);
// Webclient
WebClient.Builder webClientBuilder = WebClient.builder()
final WebClient.Builder webClientBuilder = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(securedHttpClient))
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.exchangeStrategies(ExchangeStrategies
.builder()

View File

@ -16,8 +16,6 @@ import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.PaginationField;
import com.appsmith.external.models.PaginationType;
import com.appsmith.external.models.Property;
import com.appsmith.external.models.SSLDetails;
import com.appsmith.external.models.UploadedFile;
import com.appsmith.external.plugins.BasePlugin;
import com.appsmith.external.plugins.PluginExecutor;
import com.appsmith.external.plugins.SmartSubstitutionInterface;
@ -56,7 +54,6 @@ import reactor.core.Exceptions;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.tcp.DefaultSslContextSpec;
import reactor.util.function.Tuple2;
import javax.crypto.SecretKey;
@ -68,9 +65,6 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@ -302,25 +296,8 @@ public class RestApiPlugin extends BasePlugin {
.build();
HttpClient httpClient = HttpClient.create(provider)
.secure(sslContextSpec -> {
final DefaultSslContextSpec sslContextSpec1 = DefaultSslContextSpec.forClient();
if (datasourceConfiguration.getConnection() != null &&
datasourceConfiguration.getConnection().getSsl() != null &&
datasourceConfiguration.getConnection().getSsl().getAuthType() == SSLDetails.AuthType.SELF_SIGNED_CERTIFICATE) {
sslContextSpec1.configure(sslContextBuilder -> {
try {
final UploadedFile certificateFile = datasourceConfiguration.getConnection().getSsl().getCertificateFile();
sslContextBuilder.trustManager(SSLHelper.getSslTrustManagerFactory(certificateFile));
} catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
});
}
sslContextSpec.sslContext(sslContextSpec1);
}).compress(true);
.secure(SSLHelper.sslCheckForHttpClient(datasourceConfiguration))
.compress(true);
if ("true".equals(System.getProperty("java.net.useSystemProxies"))
&& (!System.getProperty("http.proxyHost", "").isEmpty() || !System.getProperty("https.proxyHost", "").isEmpty())) {
@ -620,7 +597,7 @@ public class RestApiPlugin extends BasePlugin {
@Override
public Mono<APIConnection> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
return APIConnectionFactory.createConnection(datasourceConfiguration.getAuthentication());
return APIConnectionFactory.createConnection(datasourceConfiguration);
}
@Override

View File

@ -2,6 +2,7 @@ package com.external.connections;
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
import com.appsmith.external.models.AuthenticationResponse;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.OAuth2;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
@ -56,13 +57,15 @@ public class OAuth2ClientCredentialsTest {
@Test
public void testValidConnection() {
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
OAuth2 oAuth2 = new OAuth2();
datasourceConfiguration.setAuthentication(oAuth2);
AuthenticationResponse authenticationResponse = new AuthenticationResponse();
oAuth2.setIsTokenHeader(true);
authenticationResponse.setToken("SomeToken");
authenticationResponse.setExpiresAt(Instant.now().plusSeconds(1200));
oAuth2.setAuthenticationResponse(authenticationResponse);
OAuth2ClientCredentials connection = OAuth2ClientCredentials.create(oAuth2).block(Duration.ofMillis(100));
OAuth2ClientCredentials connection = OAuth2ClientCredentials.create(datasourceConfiguration).block(Duration.ofMillis(100));
assertThat(connection).isNotNull();
assertThat(connection.getExpiresAt()).isEqualTo(authenticationResponse.getExpiresAt());
assertThat(connection.getToken()).isEqualTo("SomeToken");
@ -70,13 +73,15 @@ public class OAuth2ClientCredentialsTest {
@Test
public void testStaleFilter() {
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
OAuth2 oAuth2 = new OAuth2();
datasourceConfiguration.setAuthentication(oAuth2);
AuthenticationResponse authenticationResponse = new AuthenticationResponse();
oAuth2.setIsTokenHeader(true);
authenticationResponse.setToken("SomeToken");
authenticationResponse.setExpiresAt(Instant.now().plusSeconds(1200));
oAuth2.setAuthenticationResponse(authenticationResponse);
OAuth2ClientCredentials connection = OAuth2ClientCredentials.create(oAuth2).block(Duration.ofMillis(100));
OAuth2ClientCredentials connection = OAuth2ClientCredentials.create(datasourceConfiguration).block(Duration.ofMillis(100));
connection.setExpiresAt(Instant.now());
Mono<ClientResponse> response = connection.filter(Mockito.mock(ClientRequest.class), Mockito.mock(ExchangeFunction.class));
@ -89,7 +94,9 @@ public class OAuth2ClientCredentialsTest {
public void testCreate_withIsAuthorizationHeaderTrue_sendsCredentialsInHeader() throws InterruptedException {
String baseUrl = String.format("http://localhost:%s", mockEndpoint.getPort());
final DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
OAuth2 oAuth2 = new OAuth2();
datasourceConfiguration.setAuthentication(oAuth2);
oAuth2.setIsAuthorizationHeader(true);
oAuth2.setGrantType(OAuth2.Type.CLIENT_CREDENTIALS);
oAuth2.setAccessTokenUrl(baseUrl);
@ -101,7 +108,7 @@ public class OAuth2ClientCredentialsTest {
.setBody("{}")
.addHeader("Content-Type", "application/json"));
final OAuth2ClientCredentials response = OAuth2ClientCredentials.create(oAuth2).block();
final OAuth2ClientCredentials response = OAuth2ClientCredentials.create(datasourceConfiguration).block();
final RecordedRequest recordedRequest = mockEndpoint.takeRequest(30, TimeUnit.SECONDS);
final String authorizationHeader = recordedRequest.getHeader("Authorization");
@ -113,7 +120,9 @@ public class OAuth2ClientCredentialsTest {
public void testCreate_withIsAuthorizationHeaderFalse_sendsCredentialsInBody() throws InterruptedException, EOFException {
String baseUrl = String.format("http://localhost:%s", mockEndpoint.getPort());
final DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
OAuth2 oAuth2 = new OAuth2();
datasourceConfiguration.setAuthentication(oAuth2);
oAuth2.setGrantType(OAuth2.Type.CLIENT_CREDENTIALS);
oAuth2.setAccessTokenUrl(baseUrl);
oAuth2.setClientId("testId");
@ -124,7 +133,7 @@ public class OAuth2ClientCredentialsTest {
.setBody("{}")
.addHeader("Content-Type", "application/json"));
final OAuth2ClientCredentials response = OAuth2ClientCredentials.create(oAuth2).block();
final OAuth2ClientCredentials response = OAuth2ClientCredentials.create(datasourceConfiguration).block();
final RecordedRequest recordedRequest = mockEndpoint.takeRequest(30, TimeUnit.SECONDS);
final String authorizationHeader = recordedRequest.getHeader("Authorization");

View File

@ -3,6 +3,7 @@ package com.appsmith.server.solutions.ce;
import com.appsmith.external.constants.Authentication;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
import com.appsmith.external.helpers.SSLHelper;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.AuthenticationResponse;
import com.appsmith.external.models.Datasource;
@ -30,6 +31,7 @@ import lombok.extern.slf4j.Slf4j;
import org.bson.internal.Base64;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@ -38,6 +40,7 @@ import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import java.net.ConnectException;
import java.net.URI;
@ -164,7 +167,15 @@ public class AuthenticationServiceCEImpl implements AuthenticationServiceCE {
.flatMap(datasourceService::getById)
.flatMap(datasource -> {
OAuth2 oAuth2 = (OAuth2) datasource.getDatasourceConfiguration().getAuthentication();
WebClient.Builder builder = WebClient.builder().baseUrl(oAuth2.getAccessTokenUrl());
final HttpClient httpClient = HttpClient.create();
if (oAuth2.isUseSelfSignedCert()) {
httpClient.secure(SSLHelper.sslCheckForHttpClient(datasource.getDatasourceConfiguration()));
}
WebClient.Builder builder = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.baseUrl(oAuth2.getAccessTokenUrl());
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();

View File

@ -854,7 +854,6 @@ public class ExamplesWorkspaceClonerTests {
DatasourceConfiguration dc2 = new DatasourceConfiguration();
ds2.setDatasourceConfiguration(dc2);
dc2.setAuthentication(new OAuth2(
OAuth2.Type.CLIENT_CREDENTIALS,
true,
true,
@ -872,7 +871,8 @@ public class ExamplesWorkspaceClonerTests {
new Property("custom token param 2", "custom token param value 2")
),
null,
null
null,
false
));
final Datasource ds3 = new Datasource();