Merge branch 'release'

This commit is contained in:
Arpit Mohan 2020-07-09 18:01:21 +05:30
commit 71298cd3f8
7 changed files with 197 additions and 17 deletions

View File

@ -68,15 +68,15 @@ jobs:
- name: Push release image to Docker Hub - name: Push release image to Docker Hub
if: success() && github.ref == 'refs/heads/release' if: success() && github.ref == 'refs/heads/release'
run: | run: |
docker build -t appsmith/appsmith-server-ee:${{steps.vars.outputs.tag}} . docker build -t appsmith/appsmith-server:${{steps.vars.outputs.tag}} .
echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin
docker push appsmith/appsmith-server-ee docker push appsmith/appsmith-server
# Build master Docker image and push to Docker Hub # Build master Docker image and push to Docker Hub
- name: Push master image to Docker Hub with commit tag - name: Push master image to Docker Hub with commit tag
if: success() && github.ref == 'refs/heads/master' if: success() && github.ref == 'refs/heads/master'
run: | run: |
docker build -t appsmith/appsmith-server-ee:${GITHUB_SHA} . docker build -t appsmith/appsmith-server:${GITHUB_SHA} .
docker build -t appsmith/appsmith-server-ee:latest . docker build -t appsmith/appsmith-server:latest .
echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin
docker push appsmith/appsmith-server-ee docker push appsmith/appsmith-server

View File

@ -22,3 +22,12 @@ This will
$ cd ./dist $ cd ./dist
$ java -jar -Dspring.profiles.active=$env server-1.0-SNAPSHOT.jar $ java -jar -Dspring.profiles.active=$env server-1.0-SNAPSHOT.jar
``` ```
### How to test
In order to test the code, you can run the following command
```
mvn clean package
```
Please make sure that you have a local Redis instance running for the test cases. The MongoDB is run in-memory during tests so that shouldn't be a problem.

View File

@ -4,6 +4,7 @@ package com.appsmith.server.configurations;
import com.appsmith.server.authentication.handlers.AccessDeniedHandler; import com.appsmith.server.authentication.handlers.AccessDeniedHandler;
import com.appsmith.server.authentication.handlers.CustomServerOAuth2AuthorizationRequestResolver; import com.appsmith.server.authentication.handlers.CustomServerOAuth2AuthorizationRequestResolver;
import com.appsmith.server.authentication.handlers.LogoutSuccessHandler; import com.appsmith.server.authentication.handlers.LogoutSuccessHandler;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.constants.Url; import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.User; import com.appsmith.server.domains.User;
import com.appsmith.server.services.UserService; import com.appsmith.server.services.UserService;
@ -161,8 +162,8 @@ public class SecurityConfig {
private User createAnonymousUser() { private User createAnonymousUser() {
User user = new User(); User user = new User();
user.setName("anonymousUser"); user.setName(FieldName.ANONYMOUS_USER);
user.setEmail("anonymousUser"); user.setEmail(FieldName.ANONYMOUS_USER);
user.setCurrentOrganizationId(""); user.setCurrentOrganizationId("");
user.setOrganizationIds(new HashSet<>()); user.setOrganizationIds(new HashSet<>());
user.setIsAnonymous(true); user.setIsAnonymous(true);

View File

@ -48,4 +48,5 @@ public class FieldName {
" \"leftColumn\": 0,\n" + " \"leftColumn\": 0,\n" +
" \"children\": []\n" + " \"children\": []\n" +
"}"; "}";
public static String ANONYMOUS_USER = "anonymousUser";
} }

View File

@ -67,11 +67,16 @@ public abstract class BaseAppsmithRepositoryImpl<T extends BaseDomain> {
.and("permission").is(permission.getValue()) .and("permission").is(permission.getValue())
); );
Criteria anonymousUserCriteria = Criteria.where(fieldName(QBaseDomain.baseDomain.policies))
.elemMatch(Criteria.where("users").all(FieldName.ANONYMOUS_USER)
.and("permission").is(permission.getValue())
);
Criteria groupCriteria = Criteria.where(fieldName(QBaseDomain.baseDomain.policies)) Criteria groupCriteria = Criteria.where(fieldName(QBaseDomain.baseDomain.policies))
.elemMatch(Criteria.where("groups").all(user.getGroupIds()) .elemMatch(Criteria.where("groups").all(user.getGroupIds())
.and("permission").is(permission.getValue())); .and("permission").is(permission.getValue()));
return new Criteria().orOperator(userCriteria, groupCriteria); return new Criteria().orOperator(userCriteria, groupCriteria, anonymousUserCriteria);
} }
protected Criteria getIdCriteria(Object id) { protected Criteria getIdCriteria(Object id) {
@ -187,7 +192,8 @@ public abstract class BaseAppsmithRepositoryImpl<T extends BaseDomain> {
Set<String> policyGroups = policy.getGroups(); Set<String> policyGroups = policy.getGroups();
if (policyUsers != null && policyUsers.contains(user.getUsername())) { if (policyUsers != null &&
(policyUsers.contains(user.getUsername()) || policyUsers.contains(FieldName.ANONYMOUS_USER))) {
permissions.add(policy.getPermission()); permissions.add(policy.getPermission());
} }

View File

@ -1,18 +1,22 @@
package com.appsmith.server.services; package com.appsmith.server.services;
import com.appsmith.external.models.Policy;
import com.appsmith.server.acl.AclPermission; import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.constants.AnalyticsEvents; import com.appsmith.server.constants.AnalyticsEvents;
import com.appsmith.server.constants.FieldName; import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Application; import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.ApplicationPage; import com.appsmith.server.domains.ApplicationPage;
import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.Layout;
import com.appsmith.server.domains.Organization; import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.User; import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.ApplicationAccessDTO; import com.appsmith.server.dtos.ApplicationAccessDTO;
import com.appsmith.server.dtos.OrganizationApplicationsDTO; import com.appsmith.server.dtos.OrganizationApplicationsDTO;
import com.appsmith.server.dtos.UserHomepageDTO; import com.appsmith.server.dtos.UserHomepageDTO;
import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.PolicyUtils;
import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.ApplicationRepository;
import com.appsmith.server.repositories.PageRepository; import com.appsmith.server.repositories.PageRepository;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -26,7 +30,6 @@ import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Scheduler;
import javax.validation.Validator; import javax.validation.Validator;
import java.text.Format;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
@ -51,6 +54,7 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
private final SessionUserService sessionUserService; private final SessionUserService sessionUserService;
private final OrganizationService organizationService; private final OrganizationService organizationService;
private final UserService userService; private final UserService userService;
private final PolicyUtils policyUtils;
@Autowired @Autowired
public ApplicationServiceImpl(Scheduler scheduler, public ApplicationServiceImpl(Scheduler scheduler,
@ -62,12 +66,14 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
PageRepository pageRepository, PageRepository pageRepository,
SessionUserService sessionUserService, SessionUserService sessionUserService,
OrganizationService organizationService, OrganizationService organizationService,
UserService userService) { UserService userService,
PolicyUtils policyUtils) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService); super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.pageRepository = pageRepository; this.pageRepository = pageRepository;
this.sessionUserService = sessionUserService; this.sessionUserService = sessionUserService;
this.organizationService = organizationService; this.organizationService = organizationService;
this.userService = userService; this.userService = userService;
this.policyUtils = policyUtils;
} }
@Override @Override
@ -241,8 +247,52 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
.findById(id, MANAGE_APPLICATIONS) .findById(id, MANAGE_APPLICATIONS)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.APPLICATION_ID, id))) .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.APPLICATION_ID, id)))
.flatMap(application -> { .flatMap(application -> {
if (application.getIsPublic().equals(applicationAccessDTO.getPublicAccess())) {
// No change. The required public access is the same as current public access. Do nothing
return Mono.just(application);
}
if (application.getIsPublic() == null && applicationAccessDTO.getPublicAccess().equals(false)) {
return Mono.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION));
}
application.setIsPublic(applicationAccessDTO.getPublicAccess()); application.setIsPublic(applicationAccessDTO.getPublicAccess());
return repository.save(application); return generateAndSetPoliciesForPublicView(application, applicationAccessDTO.getPublicAccess());
}); });
} }
private Mono<Application> generateAndSetPoliciesForPublicView(Application application, Boolean isPublic) {
AclPermission permission = READ_APPLICATIONS;
User user = new User();
user.setName(FieldName.ANONYMOUS_USER);
user.setEmail(FieldName.ANONYMOUS_USER);
user.setIsAnonymous(true);
Map<String, Policy> applicationPolicyMap = policyUtils.generatePolicyFromPermission(Set.of(permission), user);
Map<String, Policy> pagePolicyMap = policyUtils.generatePagePoliciesFromApplicationPolicies(applicationPolicyMap, user);
Map<String, Policy> actionPolicyMap = policyUtils.generateActionPoliciesFromPagePolicies(pagePolicyMap, user);
Flux<Page> updatedPagesFlux = policyUtils.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, isPublic);
Flux<Action> updatedActionsFlux = updatedPagesFlux
.flatMap(page -> policyUtils.updateWithPagePermissionsToAllItsActions(page.getId(), actionPolicyMap, isPublic));
return updatedActionsFlux
.collectList()
.thenReturn(application)
.flatMap(app -> {
Application updatedApplication;
if (isPublic) {
updatedApplication = (Application) policyUtils.addPoliciesToExistingObject(applicationPolicyMap, (Application) application);
} else {
updatedApplication = (Application) policyUtils.removePoliciesFromExistingObject(applicationPolicyMap, (Application) application);
}
return repository.save(updatedApplication);
});
}
} }

View File

@ -6,10 +6,12 @@ import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Organization; import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Page; import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.User; import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.ApplicationAccessDTO;
import com.appsmith.server.dtos.OrganizationApplicationsDTO; import com.appsmith.server.dtos.OrganizationApplicationsDTO;
import com.appsmith.server.dtos.UserHomepageDTO; import com.appsmith.server.dtos.UserHomepageDTO;
import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.PageRepository;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -55,6 +57,9 @@ public class ApplicationServiceTest {
@Autowired @Autowired
OrganizationService organizationService; OrganizationService organizationService;
@Autowired
PageRepository pageRepository;
String orgId; String orgId;
@Before @Before
@ -206,11 +211,14 @@ public class ApplicationServiceTest {
.block(); .block();
assertThat(applicationList.size() > 0); assertThat(applicationList.size() > 0);
applicationList.forEach(t -> { applicationList
assertThat(t.getId()).isNotNull(); .stream()
assertThat(t.getPolicies()).isNotEmpty(); .filter(t -> t.getName().equals("validGetApplications-Test"))
assertThat(t.getPolicies()).containsAll(Set.of(readAppPolicy)); .forEach(t -> {
}); assertThat(t.getId()).isNotNull();
assertThat(t.getPolicies()).isNotEmpty();
assertThat(t.getPolicies()).containsAll(Set.of(readAppPolicy));
});
} }
/* Tests for Update Application Flow */ /* Tests for Update Application Flow */
@ -322,4 +330,109 @@ public class ApplicationServiceTest {
} }
@Test
@WithUserDetails(value = "api_user")
public void validMakeApplicationPublic() {
Application application = new Application();
application.setName("validMakeApplicationPublic-Test");
Policy manageAppPolicy = Policy.builder().permission(MANAGE_APPLICATIONS.getValue())
.users(Set.of("api_user"))
.build();
Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue())
.users(Set.of("api_user", FieldName.ANONYMOUS_USER))
.build();
Policy managePagePolicy = Policy.builder().permission(MANAGE_PAGES.getValue())
.users(Set.of("api_user"))
.build();
Policy readPagePolicy = Policy.builder().permission(READ_PAGES.getValue())
.users(Set.of("api_user", FieldName.ANONYMOUS_USER))
.build();
Application createdApplication = applicationPageService.createApplication(application, orgId).block();
ApplicationAccessDTO applicationAccessDTO = new ApplicationAccessDTO();
applicationAccessDTO.setPublicAccess(true);
Mono<Application> publicAppMono = applicationService
.changeViewAccess(createdApplication.getId(), applicationAccessDTO)
.cache();
Mono<Page> pageMono = publicAppMono
.flatMap(app -> {
String pageId = app.getPages().get(0).getId();
return pageRepository.findById(pageId);
});
StepVerifier
.create(Mono.zip(publicAppMono, pageMono))
.assertNext(tuple -> {
Application publicApp = tuple.getT1();
Page page = tuple.getT2();
assertThat(publicApp.getIsPublic()).isTrue();
assertThat(publicApp.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy));
// Check the child page's policies
assertThat(page.getPolicies()).containsAll(Set.of(managePagePolicy, readPagePolicy));
})
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void validMakeApplicationPrivate() {
Application application = new Application();
application.setName("validMakeApplicationPrivate-Test");
Policy manageAppPolicy = Policy.builder().permission(MANAGE_APPLICATIONS.getValue())
.users(Set.of("api_user"))
.build();
Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue())
.users(Set.of("api_user"))
.build();
Policy managePagePolicy = Policy.builder().permission(MANAGE_PAGES.getValue())
.users(Set.of("api_user"))
.build();
Policy readPagePolicy = Policy.builder().permission(READ_PAGES.getValue())
.users(Set.of("api_user"))
.build();
Mono<Application> createApplication = applicationPageService.createApplication(application, orgId);
ApplicationAccessDTO applicationAccessDTO = new ApplicationAccessDTO();
Mono<Application> privateAppMono = createApplication
.flatMap(application1 -> {
applicationAccessDTO.setPublicAccess(true);
return applicationService.changeViewAccess(application1.getId(), applicationAccessDTO);
})
.flatMap(application1 -> {
applicationAccessDTO.setPublicAccess(false);
return applicationService.changeViewAccess(application1.getId(), applicationAccessDTO);
})
.cache();
Mono<Page> pageMono = privateAppMono
.flatMap(app -> {
String pageId = app.getPages().get(0).getId();
return pageRepository.findById(pageId);
});
StepVerifier
.create(Mono.zip(privateAppMono, pageMono))
.assertNext(tuple -> {
Application app = tuple.getT1();
Page page = tuple.getT2();
assertThat(app.getIsPublic()).isFalse();
assertThat(app.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy));
// Check the child page's policies
assertThat(page.getPolicies()).containsAll(Set.of(managePagePolicy, readPagePolicy));
})
.verifyComplete();
}
} }