fix: Forking fails when template has REST datasource with Bearer Token Authentication (#19930)

## Description
When an application is exported with credentials i.e. template
application, it does not contain the bearer token authentication
credentials. As a result, when user is trying to import this
application, the datasource configuration popup appears. This PR fixes
this issue.

TL;DR enable export-import application with bearer token authentication

Fixes #19415 

Media
> A video or a GIF is preferred. when using Loom, don’t embed because it
looks like it’s a GIF. instead, just link to the video

## Type of change
- Bug fix (non-breaking change which fixes an issue)


## How Has This Been Tested?
- Manual
- Jest

### Test Plan
> Add Testsmith test cases links that relate to this PR

### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)


## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
This commit is contained in:
Nayan 2023-01-20 15:36:49 +06:00 committed by GitHub
parent e4ade30d18
commit 89ae5ddf7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 174 additions and 3 deletions

View File

@ -30,6 +30,8 @@ public class DecryptedSensitiveFields {
BasicAuth basicAuth;
OAuth2 openAuth2;
BearerTokenAuth bearerTokenAuth;
public DecryptedSensitiveFields(AuthenticationResponse authResponse) {
this.token = authResponse.getToken();

View File

@ -7,6 +7,7 @@ import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.AuthenticationResponse;
import com.appsmith.external.models.BaseDomain;
import com.appsmith.external.models.BasicAuth;
import com.appsmith.external.models.BearerTokenAuth;
import com.appsmith.external.models.DBAuth;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfiguration;
@ -1983,6 +1984,10 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
authResponse.setExpiresAt(Instant.now());
auth2.setAuthenticationResponse(authResponse);
datasource.getDatasourceConfiguration().setAuthentication(auth2);
} else if (StringUtils.equals(authType, BearerTokenAuth.class.getName())) {
BearerTokenAuth auth = new BearerTokenAuth();
auth.setBearerToken(decryptedFields.getBearerTokenAuth().getBearerToken());
datasource.getDatasourceConfiguration().setAuthentication(auth);
}
return datasource;
}
@ -2020,6 +2025,8 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
} else if (authentication instanceof BasicAuth auth) {
dsDecryptedFields.setPassword(auth.getPassword());
dsDecryptedFields.setBasicAuth(auth);
} else if (authentication instanceof BearerTokenAuth auth) {
dsDecryptedFields.setBearerTokenAuth(auth);
}
dsDecryptedFields.setAuthType(authentication.getClass().getName());
return dsDecryptedFields;

View File

@ -7,6 +7,7 @@ import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.AuthenticationResponse;
import com.appsmith.external.models.BaseDomain;
import com.appsmith.external.models.BasicAuth;
import com.appsmith.external.models.BearerTokenAuth;
import com.appsmith.external.models.DBAuth;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfiguration;
@ -32,7 +33,6 @@ import com.appsmith.server.domains.Workspace;
import com.appsmith.server.dtos.ActionCollectionDTO;
import com.appsmith.server.dtos.ApplicationImportDTO;
import com.appsmith.server.dtos.ApplicationJson;
import com.appsmith.server.dtos.CustomJSLibApplicationDTO;
import com.appsmith.server.dtos.ExportFileDTO;
import com.appsmith.server.dtos.PageDTO;
import com.appsmith.server.exceptions.AppsmithError;
@ -2019,6 +2019,10 @@ public class ImportExportApplicationServiceCEImplV2 implements ImportExportAppli
authResponse.setExpiresAt(Instant.now());
auth2.setAuthenticationResponse(authResponse);
datasource.getDatasourceConfiguration().setAuthentication(auth2);
} else if (org.apache.commons.lang.StringUtils.equals(authType, BearerTokenAuth.class.getName())) {
BearerTokenAuth auth = new BearerTokenAuth();
auth.setBearerToken(decryptedFields.getBearerTokenAuth().getBearerToken());
datasource.getDatasourceConfiguration().setAuthentication(auth);
}
return datasource;
}
@ -2059,6 +2063,8 @@ public class ImportExportApplicationServiceCEImplV2 implements ImportExportAppli
BasicAuth auth = (BasicAuth) authentication;
dsDecryptedFields.setPassword(auth.getPassword());
dsDecryptedFields.setBasicAuth(auth);
} else if (authentication instanceof BearerTokenAuth auth) {
dsDecryptedFields.setBearerTokenAuth(auth);
}
dsDecryptedFields.setAuthType(authentication.getClass().getName());
return dsDecryptedFields;

View File

@ -3,13 +3,17 @@ package com.appsmith.server.solutions;
import com.appsmith.external.helpers.AppsmithBeanUtils;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.BearerTokenAuth;
import com.appsmith.external.models.Connection;
import com.appsmith.external.models.DBAuth;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DecryptedSensitiveFields;
import com.appsmith.external.models.InvisibleActionFields;
import com.appsmith.external.models.PluginType;
import com.appsmith.external.models.Policy;
import com.appsmith.external.models.Property;
import com.appsmith.external.models.SSLDetails;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.constants.SerialiseApplicationObjective;
import com.appsmith.server.domains.ActionCollection;
@ -3509,4 +3513,79 @@ public class ImportExportApplicationServiceTests {
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void exportApplication_WithBearerTokenAndExportWithConfig_exportedWithDecryptedFields() {
String randomUUID = UUID.randomUUID().toString();
Workspace testWorkspace = new Workspace();
testWorkspace.setName("workspace-" + randomUUID);
// User apiUser = userService.findByEmail("api_user").block();
Mono<Workspace> workspaceMono = workspaceService.create(testWorkspace).cache();
Mono<Application> applicationMono = workspaceMono.flatMap(workspace -> {
Application testApplication = new Application();
testApplication.setName("application-" + randomUUID);
testApplication.setExportWithConfiguration(true);
testApplication.setWorkspaceId(workspace.getId());
return applicationPageService.createApplication(testApplication);
}).flatMap(application -> {
ApplicationAccessDTO accessDTO = new ApplicationAccessDTO();
accessDTO.setPublicAccess(true);
return applicationService.changeViewAccess(application.getId(), accessDTO).thenReturn(application);
});
Mono<Datasource> datasourceMono = workspaceMono.zipWith(pluginRepository.findByPackageName("restapi-plugin"))
.flatMap(objects -> {
Workspace workspace = objects.getT1();
Plugin plugin = objects.getT2();
Datasource datasource = new Datasource();
datasource.setPluginId(plugin.getId());
datasource.setName("RestAPIWithBearerToken");
datasource.setWorkspaceId(workspace.getId());
datasource.setIsConfigured(true);
BearerTokenAuth bearerTokenAuth = new BearerTokenAuth();
bearerTokenAuth.setBearerToken("token_" + randomUUID);
SSLDetails sslDetails = new SSLDetails();
sslDetails.setAuthType(SSLDetails.AuthType.DEFAULT);
Connection connection = new Connection();
connection.setSsl(sslDetails);
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
datasourceConfiguration.setAuthentication(bearerTokenAuth);
datasourceConfiguration.setConnection(connection);
datasourceConfiguration.setConnection(new Connection());
datasourceConfiguration.setUrl("https://mock-api.appsmith.com");
datasource.setDatasourceConfiguration(datasourceConfiguration);
return datasourceService.create(datasource);
});
Mono<ApplicationJson> exportAppMono = Mono.zip(applicationMono, datasourceMono).flatMap(objects -> {
ApplicationPage applicationPage = objects.getT1().getPages().get(0);
ActionDTO action = new ActionDTO();
action.setName("validAction");
action.setPageId(applicationPage.getId());
action.setPluginId(objects.getT2().getPluginId());
action.setDatasource(objects.getT2());
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setHttpMethod(HttpMethod.GET);
actionConfiguration.setPath("/test/path");
action.setActionConfiguration(actionConfiguration);
return layoutActionService.createSingleAction(action, Boolean.FALSE)
.then(importExportApplicationService.exportApplicationById(objects.getT1().getId(), ""));
});
StepVerifier.create(exportAppMono).assertNext(applicationJson -> {
assertThat(applicationJson.getDecryptedFields()).isNotEmpty();
DecryptedSensitiveFields fields = applicationJson.getDecryptedFields().get("RestAPIWithBearerToken");
assertThat(fields.getBearerTokenAuth().getBearerToken()).isEqualTo("token_" + randomUUID);
}).verifyComplete();
}
}

View File

@ -3,13 +3,17 @@ package com.appsmith.server.solutions;
import com.appsmith.external.helpers.AppsmithBeanUtils;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.BearerTokenAuth;
import com.appsmith.external.models.Connection;
import com.appsmith.external.models.DBAuth;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DecryptedSensitiveFields;
import com.appsmith.external.models.InvisibleActionFields;
import com.appsmith.external.models.PluginType;
import com.appsmith.external.models.Policy;
import com.appsmith.external.models.Property;
import com.appsmith.external.models.SSLDetails;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.constants.SerialiseApplicationObjective;
import com.appsmith.server.domains.ActionCollection;
@ -30,7 +34,6 @@ import com.appsmith.server.dtos.ApplicationAccessDTO;
import com.appsmith.server.dtos.ApplicationImportDTO;
import com.appsmith.server.dtos.ApplicationJson;
import com.appsmith.server.dtos.ApplicationPagesDTO;
import com.appsmith.server.dtos.CustomJSLibApplicationDTO;
import com.appsmith.server.dtos.PageDTO;
import com.appsmith.server.dtos.PageNameIdDTO;
import com.appsmith.server.exceptions.AppsmithError;
@ -95,7 +98,6 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -3601,4 +3603,79 @@ public class ImportExportApplicationServiceV2Tests {
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void exportApplication_WithBearerTokenAndExportWithConfig_exportedWithDecryptedFields() {
String randomUUID = UUID.randomUUID().toString();
Workspace testWorkspace = new Workspace();
testWorkspace.setName("workspace-" + randomUUID);
// User apiUser = userService.findByEmail("api_user").block();
Mono<Workspace> workspaceMono = workspaceService.create(testWorkspace).cache();
Mono<Application> applicationMono = workspaceMono.flatMap(workspace -> {
Application testApplication = new Application();
testApplication.setName("application-" + randomUUID);
testApplication.setExportWithConfiguration(true);
testApplication.setWorkspaceId(workspace.getId());
return applicationPageService.createApplication(testApplication);
}).flatMap(application -> {
ApplicationAccessDTO accessDTO = new ApplicationAccessDTO();
accessDTO.setPublicAccess(true);
return applicationService.changeViewAccess(application.getId(), accessDTO).thenReturn(application);
});
Mono<Datasource> datasourceMono = workspaceMono.zipWith(pluginRepository.findByPackageName("restapi-plugin"))
.flatMap(objects -> {
Workspace workspace = objects.getT1();
Plugin plugin = objects.getT2();
Datasource datasource = new Datasource();
datasource.setPluginId(plugin.getId());
datasource.setName("RestAPIWithBearerToken");
datasource.setWorkspaceId(workspace.getId());
datasource.setIsConfigured(true);
BearerTokenAuth bearerTokenAuth = new BearerTokenAuth();
bearerTokenAuth.setBearerToken("token_" + randomUUID);
SSLDetails sslDetails = new SSLDetails();
sslDetails.setAuthType(SSLDetails.AuthType.DEFAULT);
Connection connection = new Connection();
connection.setSsl(sslDetails);
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
datasourceConfiguration.setAuthentication(bearerTokenAuth);
datasourceConfiguration.setConnection(connection);
datasourceConfiguration.setConnection(new Connection());
datasourceConfiguration.setUrl("https://mock-api.appsmith.com");
datasource.setDatasourceConfiguration(datasourceConfiguration);
return datasourceService.create(datasource);
});
Mono<ApplicationJson> exportAppMono = Mono.zip(applicationMono, datasourceMono).flatMap(objects -> {
ApplicationPage applicationPage = objects.getT1().getPages().get(0);
ActionDTO action = new ActionDTO();
action.setName("validAction");
action.setPageId(applicationPage.getId());
action.setPluginId(objects.getT2().getPluginId());
action.setDatasource(objects.getT2());
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setHttpMethod(HttpMethod.GET);
actionConfiguration.setPath("/test/path");
action.setActionConfiguration(actionConfiguration);
return layoutActionService.createSingleAction(action, Boolean.FALSE)
.then(importExportApplicationService.exportApplicationById(objects.getT1().getId(), ""));
});
StepVerifier.create(exportAppMono).assertNext(applicationJson -> {
assertThat(applicationJson.getDecryptedFields()).isNotEmpty();
DecryptedSensitiveFields fields = applicationJson.getDecryptedFields().get("RestAPIWithBearerToken");
assertThat(fields.getBearerTokenAuth().getBearerToken()).isEqualTo("token_" + randomUUID);
}).verifyComplete();
}
}