diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java
index e86dc17e82..129bb9e251 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java
@@ -37,6 +37,7 @@ import static com.appsmith.server.constants.Url.ACTION_COLLECTION_URL;
import static com.appsmith.server.constants.Url.ACTION_URL;
import static com.appsmith.server.constants.Url.APPLICATION_URL;
import static com.appsmith.server.constants.Url.PAGE_URL;
+import static com.appsmith.server.constants.Url.THEME_URL;
import static com.appsmith.server.constants.Url.USER_URL;
import static java.time.temporal.ChronoUnit.DAYS;
@@ -124,6 +125,7 @@ public class SecurityConfig {
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, ACTION_COLLECTION_URL + "/view"),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, PAGE_URL + "/**"),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, APPLICATION_URL + "/**"),
+ ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, THEME_URL + "/**"),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, ACTION_URL + "/execute")
)
.permitAll()
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/PageControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/PageControllerCE.java
index 340efbc69f..86343b3ce1 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/PageControllerCE.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/PageControllerCE.java
@@ -2,6 +2,7 @@ package com.appsmith.server.controllers.ce;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.constants.Url;
+import com.appsmith.server.domains.ApplicationMode;
import com.appsmith.server.dtos.ApplicationPagesDTO;
import com.appsmith.server.dtos.CRUDPageResourceDTO;
import com.appsmith.server.dtos.CRUDPageResponseDTO;
@@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@@ -147,4 +149,24 @@ public class PageControllerCE {
return newPageService.updatePageByDefaultPageIdAndBranch(defaultPageId, resource, branchName)
.map(updatedResource -> new ResponseDTO<>(HttpStatus.OK.value(), updatedResource, null));
}
+
+ /**
+ * Returns a list of pages. It takes either Application ID or a Page ID and mode as input.
+ * If Application ID is present, it'll fetch all pages of that application in the provided mode.
+ * if Page ID is present, it'll fetch all pages of the corresponding Application.
+ * If both IDs are present, it'll use the Application ID only and ignore the Page ID
+ * @param applicationId Id of the application
+ * @param pageId id of a page
+ * @param mode In which mode it's in
+ * @param branchName name of the current branch
+ * @return List of ApplicationPagesDTO along with other meta data
+ */
+ @GetMapping
+ public Mono
> getAllPages(@RequestParam(required = false) String applicationId,
+ @RequestParam(required = false) String pageId,
+ @RequestParam(required = true, defaultValue = "EDIT") ApplicationMode mode,
+ @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) {
+ return newPageService.findApplicationPages(applicationId, pageId, branchName, mode)
+ .map(resources -> new ResponseDTO<>(HttpStatus.OK.value(), resources, null));
+ }
}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/UserControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/UserControllerCE.java
index 69428538e1..42bde452eb 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/UserControllerCE.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/UserControllerCE.java
@@ -127,8 +127,12 @@ public class UserControllerCE extends BaseController
public Mono> forgotPasswordRequest(@RequestBody ResetUserPasswordDTO userPasswordDTO,
@RequestHeader("Origin") String originHeader) {
userPasswordDTO.setBaseUrl(originHeader);
+ // We shouldn't leak information on whether this operation was successful or not to the client. This can enable
+ // username scraping, where the response of this API can prove whether an email has an account or not.
return service.forgotPasswordTokenGenerate(userPasswordDTO)
- .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null));
+ .defaultIfEmpty(true)
+ .onErrorReturn(true)
+ .thenReturn(new ResponseDTO<>(HttpStatus.OK.value(), true, null));
}
@GetMapping("/verifyPasswordResetToken")
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ApplicationPagesDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ApplicationPagesDTO.java
index 5d5f7dfd2d..3fc8563612 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ApplicationPagesDTO.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/ApplicationPagesDTO.java
@@ -1,5 +1,6 @@
package com.appsmith.server.dtos;
+import com.appsmith.server.domains.Application;
import lombok.Getter;
import lombok.Setter;
@@ -11,6 +12,8 @@ public class ApplicationPagesDTO {
String organizationId;
+ Application application;
+
List pages;
}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java
index 20ce8e3347..a67337c99d 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java
@@ -2061,6 +2061,9 @@ public class GitServiceCEImpl implements GitServiceCE {
.flatMap(application -> {
GitApplicationMetadata gitApplicationMetadata = application.getGitApplicationMetadata();
Path repoPath = Paths.get(application.getOrganizationId(), defaultApplicationId, gitApplicationMetadata.getRepoName());
+ if(branchName.equals(gitApplicationMetadata.getDefaultBranchName())) {
+ return Mono.error(new AppsmithException(AppsmithError.GIT_ACTION_FAILED, " delete branch", "Cannot delete default branch"));
+ }
return gitExecutor.deleteBranch(repoPath, branchName)
.flatMap(isBranchDeleted -> {
if(Boolean.FALSE.equals(isBranchDeleted)) {
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java
index 7bbcab6046..721d34d8ba 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCE.java
@@ -1,6 +1,7 @@
package com.appsmith.server.services.ce;
import com.appsmith.server.acl.AclPermission;
+import com.appsmith.server.domains.ApplicationMode;
import com.appsmith.server.domains.Layout;
import com.appsmith.server.domains.NewPage;
import com.appsmith.server.dtos.ApplicationPagesDTO;
@@ -38,6 +39,8 @@ public interface NewPageServiceCE extends CrudService {
Boolean view,
boolean markApplicationAsRecentlyAccessed);
+ Mono findApplicationPages(String applicationId, String pageId, String branchName, ApplicationMode mode);
+
Mono findApplicationPagesByApplicationIdViewMode(
String applicationId, Boolean view, boolean markApplicationAsRecentlyAccessed
);
@@ -70,6 +73,8 @@ public interface NewPageServiceCE extends CrudService {
Mono findBranchedPageId(String branchName, String defaultPageId, AclPermission permission);
+ Mono findBranchedApplicationIdFromNewPage(String branchName, String defaultPageId);
+
Mono findByGitSyncIdAndDefaultApplicationId(String defaultApplicationId, String gitSyncId, AclPermission permission);
Flux findPageSlugsByApplicationIds(List applicationIds, AclPermission aclPermission);
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java
index abbc8eab30..21923b3f7d 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/NewPageServiceCEImpl.java
@@ -4,6 +4,7 @@ import com.appsmith.external.models.DefaultResources;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
+import com.appsmith.server.domains.ApplicationMode;
import com.appsmith.server.domains.ApplicationPage;
import com.appsmith.server.domains.Layout;
import com.appsmith.server.domains.NewPage;
@@ -46,6 +47,7 @@ import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValues
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
+import static com.appsmith.server.exceptions.AppsmithError.INVALID_PARAMETER;
@Slf4j
@@ -333,10 +335,13 @@ public class NewPageServiceCEImpl extends BaseService {
Application application = tuple.getT1();
+ application.setPages(null);
+ application.setPublishedPages(null);
List nameIdDTOList = tuple.getT2();
ApplicationPagesDTO applicationPagesDTO = new ApplicationPagesDTO();
applicationPagesDTO.setOrganizationId(application.getOrganizationId());
applicationPagesDTO.setPages(nameIdDTOList);
+ applicationPagesDTO.setApplication(application);
return applicationPagesDTO;
});
}
@@ -525,6 +530,24 @@ public class NewPageServiceCEImpl extends BaseService findBranchedApplicationIdFromNewPage(String branchName, String defaultPageId) {
+ Mono getPageMono;
+ if (!StringUtils.hasLength(branchName)) {
+ if (!StringUtils.hasLength(defaultPageId)) {
+ return Mono.error(new AppsmithException(INVALID_PARAMETER, FieldName.PAGE_ID, defaultPageId));
+ }
+ getPageMono = repository.findById(defaultPageId, READ_PAGES);
+ } else {
+ getPageMono = repository.findPageByBranchNameAndDefaultPageId(branchName, defaultPageId, READ_PAGES);
+ }
+ return getPageMono
+ .switchIfEmpty(Mono.error(
+ new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.PAGE_ID, defaultPageId + ", " + branchName))
+ )
+ .map(NewPage::getApplicationId);
+ }
+
@Override
public Mono findByGitSyncIdAndDefaultApplicationId(String defaultApplicationId, String gitSyncId, AclPermission permission) {
return repository.findByGitSyncIdAndDefaultApplicationId(defaultApplicationId, gitSyncId, permission);
@@ -534,4 +557,29 @@ public class NewPageServiceCEImpl extends BaseService findPageSlugsByApplicationIds(List applicationIds, AclPermission aclPermission) {
return repository.findSlugsByApplicationIds(applicationIds, aclPermission);
}
+
+ /**
+ * Returns a list of pages of an Application.
+ * If Application ID is present, it'll fetch all pages of that application in the provided mode.
+ * if Page ID is present, it'll fetch all pages of the corresponding Application.
+ * If both IDs are present, it'll use the Application ID only and ignore the Page ID
+ * @param applicationId Id of the application
+ * @param pageId id of a page
+ * @param branchName name of the current branch
+ * @param mode In which mode it's in
+ * @return List of ApplicationPagesDTO
+ */
+ @Override
+ public Mono findApplicationPages(String applicationId, String pageId, String branchName, ApplicationMode mode) {
+ boolean isViewMode = (mode == ApplicationMode.PUBLISHED);
+ if(StringUtils.hasLength(applicationId)) {
+ return findApplicationPagesByApplicationIdViewModeAndBranch(applicationId, branchName, isViewMode, true);
+ } else if(StringUtils.hasLength(pageId)) {
+ return findBranchedApplicationIdFromNewPage(branchName, pageId)
+ .flatMap(branchedApplicationId -> findApplicationPagesByApplicationIdViewMode(branchedApplicationId, isViewMode, true))
+ .map(responseUtils::updateApplicationPagesDTOWithDefaultResources);
+ } else {
+ return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.APPLICATION_ID + " or " + FieldName.PAGE_ID));
+ }
+ }
}
diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/GitServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/GitServiceTest.java
index 1cb3b66839..5a728aa45f 100644
--- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/GitServiceTest.java
+++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/GitServiceTest.java
@@ -2523,6 +2523,8 @@ public class GitServiceTest {
@WithUserDetails(value ="api_user")
public void deleteBranch_staleBranchNotInDB_Success() throws IOException {
Application application = createApplicationConnectedToGit("deleteBranch_staleBranchNotInDB_Success", "master");
+ application.getGitApplicationMetadata().setDefaultBranchName("master");
+ applicationService.save(application).block();
Mockito.when(gitExecutor.deleteBranch(Mockito.any(Path.class), Mockito.anyString()))
.thenReturn(Mono.just(true));
@@ -2541,6 +2543,8 @@ public class GitServiceTest {
@WithUserDetails(value ="api_user")
public void deleteBranch_existsInDB_Success() throws IOException {
Application application = createApplicationConnectedToGit("deleteBranch_existsInDB_Success", "master");
+ application.getGitApplicationMetadata().setDefaultBranchName("test");
+ applicationService.save(application).block();
Mockito.when(gitExecutor.deleteBranch(Mockito.any(Path.class), Mockito.anyString()))
.thenReturn(Mono.just(true));
@@ -2559,6 +2563,8 @@ public class GitServiceTest {
@WithUserDetails(value = "api_user")
public void deleteBranch_branchDoesNotExist_ThrowError() throws IOException {
Application application = createApplicationConnectedToGit("deleteBranch_branchDoesNotExist_ThrowError", "master");
+ application.getGitApplicationMetadata().setDefaultBranchName("test");
+ applicationService.save(application).block();
Mockito.when(gitExecutor.deleteBranch(Mockito.any(Path.class), Mockito.anyString()))
.thenReturn(Mono.just(false));
@@ -2570,4 +2576,22 @@ public class GitServiceTest {
throwable.getMessage().contains("delete branch. Branch does not exists in the repo"))
.verify();
}
+
+ @Test
+ @WithUserDetails(value = "api_user")
+ public void deleteBranch_defaultBranch_ThrowError() throws IOException {
+ Application application = createApplicationConnectedToGit("deleteBranch_defaultBranch_ThrowError", "master");
+ application.getGitApplicationMetadata().setDefaultBranchName("master");
+ applicationService.save(application).block();
+ Mockito.when(gitExecutor.deleteBranch(Mockito.any(Path.class), Mockito.anyString()))
+ .thenReturn(Mono.just(false));
+
+ Mono applicationMono = gitService.deleteBranch(application.getId(), "master");
+
+ StepVerifier
+ .create(applicationMono)
+ .expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
+ throwable.getMessage().contains("Cannot delete default branch"))
+ .verify();
+ }
}
\ No newline at end of file
diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java
new file mode 100644
index 0000000000..66ad1beb1a
--- /dev/null
+++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java
@@ -0,0 +1,97 @@
+package com.appsmith.server.services;
+
+import com.appsmith.server.domains.Application;
+import com.appsmith.server.domains.ApplicationMode;
+import com.appsmith.server.domains.Organization;
+import com.appsmith.server.dtos.ApplicationPagesDTO;
+import com.appsmith.server.dtos.PageDTO;
+import com.appsmith.server.exceptions.AppsmithException;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.test.context.support.WithUserDetails;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest
+@RunWith(SpringRunner.class)
+class NewPageServiceTest {
+
+ @Autowired
+ NewPageService newPageService;
+
+ @Autowired
+ ApplicationPageService applicationPageService;
+
+ @Autowired
+ OrganizationService organizationService;
+
+ @Test
+ @WithUserDetails("api_user")
+ void findApplicationPages_WhenApplicationIdAndPageIdNotPresent_ThrowsException() {
+ StepVerifier.create(
+ newPageService.findApplicationPages(null, null, "master", ApplicationMode.EDIT)
+ )
+ .expectError(AppsmithException.class)
+ .verify();
+ }
+
+ @Test
+ @WithUserDetails("api_user")
+ void findApplicationPages_WhenApplicationIdPresent_ReturnsPages() {
+ String randomId = UUID.randomUUID().toString();
+ Organization organization = new Organization();
+ organization.setName("org_" + randomId);
+ Mono applicationPagesDTOMono = organizationService.create(organization).flatMap(createdOrg -> {
+ Application application = new Application();
+ application.setName("app_" + randomId);
+ return applicationPageService.createApplication(application, createdOrg.getId());
+ }).flatMap(application -> {
+ PageDTO pageDTO = new PageDTO();
+ pageDTO.setName("page_" + randomId);
+ pageDTO.setApplicationId(application.getId());
+ return applicationPageService.createPage(pageDTO);
+ }).flatMap(pageDTO ->
+ newPageService.findApplicationPages(pageDTO.getApplicationId(), null, null, ApplicationMode.EDIT)
+ );
+
+ StepVerifier.create(applicationPagesDTOMono).assertNext(applicationPagesDTO -> {
+ assertThat(applicationPagesDTO.getApplication()).isNotNull();
+ assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo("app_"+randomId);
+ assertThat(applicationPagesDTO.getPages()).isNotEmpty();
+ }).verifyComplete();
+ }
+
+ @Test
+ @WithUserDetails("api_user")
+ void findApplicationPages_WhenPageIdPresent_ReturnsPages() {
+ String randomId = UUID.randomUUID().toString();
+ Organization organization = new Organization();
+ organization.setName("org_" + randomId);
+ Mono applicationPagesDTOMono = organizationService.create(organization).flatMap(createdOrg -> {
+ Application application = new Application();
+ application.setName("app_" + randomId);
+ return applicationPageService.createApplication(application, createdOrg.getId());
+ }).flatMap(application -> {
+ PageDTO pageDTO = new PageDTO();
+ pageDTO.setName("page_" + randomId);
+ pageDTO.setApplicationId(application.getId());
+ return applicationPageService.createPage(pageDTO);
+ }).flatMap(pageDTO ->
+ newPageService.findApplicationPages(null, pageDTO.getId(), null, ApplicationMode.EDIT)
+ );
+
+ StepVerifier.create(applicationPagesDTOMono).assertNext(applicationPagesDTO -> {
+ assertThat(applicationPagesDTO.getApplication()).isNotNull();
+ assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo("app_"+randomId);
+ assertThat(applicationPagesDTO.getPages()).isNotEmpty();
+ }).verifyComplete();
+ }
+
+}
\ No newline at end of file