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:
parent
b16fadc763
commit
50172ecd64
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ function renderComponent(props: renderComponentProps) {
|
|||
|
||||
return (
|
||||
<StyledCheckbox
|
||||
cypressSelector={props?.input?.name}
|
||||
isDefaultChecked={props?.input?.checked as boolean}
|
||||
{...props}
|
||||
info={undefined}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ export interface Oauth2Common {
|
|||
resource: string;
|
||||
sendScopeWithRefreshToken: string;
|
||||
refreshTokenClientCredentialsLocation: string;
|
||||
useSelfSignedCert?: boolean;
|
||||
}
|
||||
|
||||
export interface ClientCredentials extends Oauth2Common {
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ const formToDatasourceAuthentication = (
|
|||
sendScopeWithRefreshToken: authentication.sendScopeWithRefreshToken,
|
||||
refreshTokenClientCredentialsLocation:
|
||||
authentication.refreshTokenClientCredentialsLocation,
|
||||
useSelfSignedCert: authentication.useSelfSignedCert,
|
||||
};
|
||||
if (isClientCredentials(authType, authentication)) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ public class OAuth2 extends AuthenticationDTO {
|
|||
|
||||
String resource;
|
||||
|
||||
boolean useSelfSignedCert = false;
|
||||
|
||||
public String getScopeString() {
|
||||
if (scopeString != null && !scopeString.isBlank()) {
|
||||
return scopeString;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user