From 2eced18c91e28f2cde49d5d2fa76f5492a3b378d Mon Sep 17 00:00:00 2001 From: lvhuichao <1228240570@qq.com> Date: Fri, 30 Dec 2022 16:16:47 +0800 Subject: [PATCH] 1. add data source events 2. refactor threshold checker --- .../AbstractBizThresholdChecker.java | 87 ++++++++++++++++++ .../bizthreshold/BizThresholdChecker.java | 80 +++++----------- .../invitation/service/InvitationService.java | 4 +- .../com/openblocks/infra/event/EventType.java | 8 ++ .../event/datasource/DatasourceEvent.java | 18 ++++ .../datasource/DatasourcePermissionEvent.java | 24 +++++ .../com/openblocks/infra/util/RSAUtils.java | 39 ++++++++ .../src/main/resources/locale_en.properties | 6 ++ .../application/ApplicationApiService.java | 4 +- .../com/openblocks/api/config/ConfigView.java | 5 +- .../api/datasource/DatasourceController.java | 40 +++++++- .../framework/security/SecurityConfig.java | 2 + .../openblocks/api/home/FolderController.java | 17 +++- .../api/usermanagement/GroupApiService.java | 9 +- .../api/usermanagement/OrgApiServiceImpl.java | 4 +- .../api/util/BusinessEventPublisher.java | 91 +++++++++++++++++++ .../main/resources/application-openblocks.yml | 2 +- .../resources/selfhost/ce/application.yml | 2 +- 18 files changed, 366 insertions(+), 76 deletions(-) create mode 100644 server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/AbstractBizThresholdChecker.java create mode 100644 server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourceEvent.java create mode 100644 server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourcePermissionEvent.java create mode 100644 server/openblocks-infra/src/main/java/com/openblocks/infra/util/RSAUtils.java diff --git a/server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/AbstractBizThresholdChecker.java b/server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/AbstractBizThresholdChecker.java new file mode 100644 index 00000000..48de102d --- /dev/null +++ b/server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/AbstractBizThresholdChecker.java @@ -0,0 +1,87 @@ +package com.openblocks.domain.bizthreshold; + +import static com.openblocks.sdk.util.ExceptionUtils.deferredError; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.openblocks.domain.application.model.ApplicationStatus; +import com.openblocks.domain.application.service.ApplicationService; +import com.openblocks.domain.group.service.GroupService; +import com.openblocks.domain.organization.model.OrgMember; +import com.openblocks.domain.organization.service.OrgMemberService; +import com.openblocks.sdk.exception.BizError; + +import reactor.core.publisher.Mono; + +@Component +public abstract class AbstractBizThresholdChecker { + + @Autowired + private OrgMemberService orgMemberService; + + @Autowired + private GroupService groupService; + + @Autowired + private ApplicationService applicationService; + + protected abstract int getMaxOrgPerUser(); + + protected abstract int getMaxOrgMemberCount(); + + protected abstract int getMaxOrgGroupCount(); + + protected abstract int getMaxOrgAppCount(); + + protected abstract Map getUserOrgCountWhiteList(); + + protected abstract Map getOrgMemberCountWhiteList(); + + protected abstract Map getOrgAppCountWhiteList(); + + public Mono checkMaxOrgCount(String userId) { + return orgMemberService.countAllActiveOrgs(userId) + .filter(userOrgCount -> userOrgCountBelowThreshold(userId, userOrgCount)) + .switchIfEmpty(deferredError(BizError.EXCEED_MAX_USER_ORG_COUNT, "EXCEED_MAX_USER_ORG_COUNT")) + .then(); + } + + private boolean userOrgCountBelowThreshold(String userId, long userOrgCount) { + return userOrgCount < Math.max(getUserOrgCountWhiteList().getOrDefault(userId, 0), + getMaxOrgPerUser()); + } + + public Mono checkMaxOrgMemberCount(String orgId) { + return orgMemberService.getOrgMemberCount(orgId) + .filter(orgMemberCount -> orgMemberCountBelowThreshold(orgId, orgMemberCount)) + .switchIfEmpty(deferredError(BizError.EXCEED_MAX_ORG_MEMBER_COUNT, "EXCEED_MAX_ORG_MEMBER_COUNT")) + .then(); + } + + private boolean orgMemberCountBelowThreshold(String orgId, Long orgMemberCount) { + return orgMemberCount < Math.max(getMaxOrgMemberCount(), getOrgMemberCountWhiteList().getOrDefault(orgId, 0)); + } + + public Mono checkMaxGroupCount(OrgMember orgMemberMono) { + return groupService.getOrgGroupCount(orgMemberMono.getOrgId()) + .filter(it -> it < getMaxOrgGroupCount()) + .switchIfEmpty(deferredError(BizError.EXCEED_MAX_GROUP_COUNT, "EXCEED_MAX_GROUP_COUNT")) + .then(); + } + + public Mono checkMaxOrgApplicationCount(OrgMember orgMember) { + String orgId = orgMember.getOrgId(); + return applicationService.countByOrganizationId(orgId, ApplicationStatus.NORMAL) + .filter(orgAppCount -> orgAppCountBelowThreshold(orgId, orgAppCount)) + .switchIfEmpty(deferredError(BizError.EXCEED_MAX_APP_COUNT, "EXCEED_MAX_APP_COUNT")) + .then(); + } + + private boolean orgAppCountBelowThreshold(String orgId, long orgAppCount) { + return orgAppCount < Math.max(getMaxOrgAppCount(), getOrgAppCountWhiteList().getOrDefault(orgId, 0)); + } + +} diff --git a/server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/BizThresholdChecker.java b/server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/BizThresholdChecker.java index 7ab3b213..6ea7feea 100644 --- a/server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/BizThresholdChecker.java +++ b/server/openblocks-domain/src/main/java/com/openblocks/domain/bizthreshold/BizThresholdChecker.java @@ -1,42 +1,23 @@ package com.openblocks.domain.bizthreshold; -import static com.openblocks.sdk.util.ExceptionUtils.deferredError; - import java.util.Collections; import java.util.Map; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -import com.openblocks.domain.application.model.ApplicationStatus; -import com.openblocks.domain.application.service.ApplicationService; -import com.openblocks.domain.group.service.GroupService; -import com.openblocks.domain.organization.model.OrgMember; -import com.openblocks.domain.organization.service.OrgMemberService; import com.openblocks.sdk.config.dynamic.Conf; import com.openblocks.sdk.config.dynamic.ConfigCenter; import com.openblocks.sdk.config.dynamic.ConfigInstance; -import com.openblocks.sdk.exception.BizError; - -import reactor.core.publisher.Mono; -@Component -public class BizThresholdChecker { - - @Autowired - private OrgMemberService orgMemberService; - - @Autowired - private GroupService groupService; +@Service +public class BizThresholdChecker extends AbstractBizThresholdChecker { @Autowired private ConfigCenter configCenter; - @Autowired - private ApplicationService applicationService; - private Conf maxOrgPerUser; private Conf maxOrgMemberCount; private Conf maxOrgGroupCount; @@ -50,58 +31,45 @@ private void init() { ConfigInstance threshold = configCenter.threshold(); maxOrgPerUser = threshold.ofInteger("maxOrgPerUser", 5); userOrgCountWhiteList = threshold.ofMap("userOrgCountWhiteList", String.class, Integer.class, Collections.emptyMap()); - maxOrgMemberCount = threshold.ofInteger("maxOrgMemberCount", 50); orgMemberCountWhiteList = threshold.ofMap("orgMemberCountWhiteList", String.class, Integer.class, Collections.emptyMap()); - maxOrgGroupCount = threshold.ofInteger("maxOrgGroupCount", 10); - maxOrgAppCount = threshold.ofInteger("maxOrgAppCount", 50); orgAppCountWhiteList = threshold.ofMap("orgAppCountWhiteList", String.class, Integer.class, Collections.emptyMap()); } - public Mono checkMaxOrgCount(String userId) { - return orgMemberService.countAllActiveOrgs(userId) - .filter(userOrgCount -> userOrgCountBelowThreshold(userId, userOrgCount)) - .switchIfEmpty(deferredError(BizError.EXCEED_MAX_USER_ORG_COUNT, "EXCEED_MAX_USER_ORG_COUNT")) - .then(); + @Override + protected int getMaxOrgPerUser() { + return maxOrgPerUser.get(); } - private boolean userOrgCountBelowThreshold(String userId, long userOrgCount) { - return userOrgCount < Math.max(userOrgCountWhiteList.get().getOrDefault(userId, 0), - maxOrgPerUser.get()); + @Override + protected int getMaxOrgMemberCount() { + return maxOrgMemberCount.get(); } - public Mono checkMaxOrgMemberCount(String orgId) { - return orgMemberService.getOrgMemberCount(orgId) - .filter(orgMemberCount -> orgMemberCountBelowThreshold(orgId, orgMemberCount)) - .switchIfEmpty(deferredError(BizError.EXCEED_MAX_ORG_MEMBER_COUNT, "EXCEED_MAX_ORG_MEMBER_COUNT")) - .then(); + @Override + protected int getMaxOrgGroupCount() { + return maxOrgGroupCount.get(); } - private boolean orgMemberCountBelowThreshold(String orgId, Long orgMemberCount) { - return orgMemberCount < Math.max(maxOrgMemberCount.get(), orgMemberCountWhiteList.get().getOrDefault(orgId, 0)); + @Override + protected int getMaxOrgAppCount() { + return maxOrgAppCount.get(); } - public Mono checkMaxGroupCount(Mono orgMemberMono) { - return orgMemberMono - .flatMap(it -> groupService.getOrgGroupCount(it.getOrgId())) - .filter(it -> it < maxOrgGroupCount.get()) - .switchIfEmpty(deferredError(BizError.EXCEED_MAX_GROUP_COUNT, "EXCEED_MAX_GROUP_COUNT")) - .then(orgMemberMono); + @Override + protected Map getUserOrgCountWhiteList() { + return userOrgCountWhiteList.get(); } - - public Mono checkMaxOrgApplicationCount(OrgMember orgMember) { - String orgId = orgMember.getOrgId(); - return applicationService.countByOrganizationId(orgId, ApplicationStatus.NORMAL) - .filter(orgAppCount -> orgAppCountBelowThreshold(orgId, orgAppCount)) - .switchIfEmpty(deferredError(BizError.EXCEED_MAX_APP_COUNT, "EXCEED_MAX_APP_COUNT")) - .then(); + @Override + protected Map getOrgMemberCountWhiteList() { + return orgMemberCountWhiteList.get(); } - private boolean orgAppCountBelowThreshold(String orgId, long orgAppCount) { - return orgAppCount < Math.max(maxOrgAppCount.get(), orgAppCountWhiteList.get().getOrDefault(orgId, 0)); + @Override + protected Map getOrgAppCountWhiteList() { + return orgAppCountWhiteList.get(); } - } diff --git a/server/openblocks-domain/src/main/java/com/openblocks/domain/invitation/service/InvitationService.java b/server/openblocks-domain/src/main/java/com/openblocks/domain/invitation/service/InvitationService.java index d7ebb9d0..c88aa5fc 100644 --- a/server/openblocks-domain/src/main/java/com/openblocks/domain/invitation/service/InvitationService.java +++ b/server/openblocks-domain/src/main/java/com/openblocks/domain/invitation/service/InvitationService.java @@ -7,7 +7,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import com.openblocks.domain.bizthreshold.BizThresholdChecker; +import com.openblocks.domain.bizthreshold.AbstractBizThresholdChecker; import com.openblocks.domain.invitation.model.Invitation; import com.openblocks.domain.invitation.repository.InvitationRepository; import com.openblocks.domain.organization.model.MemberRole; @@ -26,7 +26,7 @@ public class InvitationService { private OrgMemberService orgMemberService; @Autowired - private BizThresholdChecker bizThresholdChecker; + private AbstractBizThresholdChecker bizThresholdChecker; @Autowired private InvitationRepository repository; diff --git a/server/openblocks-infra/src/main/java/com/openblocks/infra/event/EventType.java b/server/openblocks-infra/src/main/java/com/openblocks/infra/event/EventType.java index be2258da..fa4b2211 100644 --- a/server/openblocks-infra/src/main/java/com/openblocks/infra/event/EventType.java +++ b/server/openblocks-infra/src/main/java/com/openblocks/infra/event/EventType.java @@ -35,6 +35,14 @@ public enum EventType { GROUP_MEMBER_REMOVE("EVENT_TYPE_GROUP_MEMBER_REMOVE"), //system SERVER_START_UP("EVENT_TYPE_SERVER_START_UP"), + + // data source + DATA_SOURCE_CREATE("DATA_SOURCE_CREATE"), + DATA_SOURCE_UPDATE("DATA_SOURCE_UPDATE"), + DATA_SOURCE_DELETE("DATA_SOURCE_DELETE"), + DATA_SOURCE_PERMISSION_GRANT("DATA_SOURCE_PERMISSION_GRANT"), + DATA_SOURCE_PERMISSION_UPDATE("DATA_SOURCE_PERMISSION_UPDATE"), + DATA_SOURCE_PERMISSION_DELETE("DATA_SOURCE_PERMISSION_DELETE"), ; private final String desc; diff --git a/server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourceEvent.java b/server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourceEvent.java new file mode 100644 index 00000000..a16f16ae --- /dev/null +++ b/server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourceEvent.java @@ -0,0 +1,18 @@ +package com.openblocks.infra.event.datasource; + +import com.openblocks.infra.event.AbstractEvent; +import com.openblocks.infra.event.EventType; + +import lombok.Getter; +import lombok.experimental.SuperBuilder; + +@Getter +@SuperBuilder +public class DatasourceEvent extends AbstractEvent { + + private final String datasourceId; + private final String name; + private final String type; + + private final EventType eventType; +} diff --git a/server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourcePermissionEvent.java b/server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourcePermissionEvent.java new file mode 100644 index 00000000..63ef85b2 --- /dev/null +++ b/server/openblocks-infra/src/main/java/com/openblocks/infra/event/datasource/DatasourcePermissionEvent.java @@ -0,0 +1,24 @@ +package com.openblocks.infra.event.datasource; + +import java.util.Collection; + +import com.openblocks.infra.event.AbstractEvent; +import com.openblocks.infra.event.EventType; + +import lombok.Getter; +import lombok.experimental.SuperBuilder; + +@Getter +@SuperBuilder +public class DatasourcePermissionEvent extends AbstractEvent { + + private final String datasourceId; + private final String name; + private final String type; + + private final Collection userIds; + private final Collection groupIds; + private final String role; + + private final EventType eventType; +} diff --git a/server/openblocks-infra/src/main/java/com/openblocks/infra/util/RSAUtils.java b/server/openblocks-infra/src/main/java/com/openblocks/infra/util/RSAUtils.java new file mode 100644 index 00000000..ca2db2e5 --- /dev/null +++ b/server/openblocks-infra/src/main/java/com/openblocks/infra/util/RSAUtils.java @@ -0,0 +1,39 @@ +package com.openblocks.infra.util; + +import java.security.Key; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.Cipher; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RSAUtils { + + private static final String algorithm = "RSA"; + + public static byte[] encrypt(Key key, byte[] data) throws Exception { + final Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } + + public static byte[] decrypt(Key key, byte[] encryptedData) throws Exception { + final Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key); + return cipher.doFinal(encryptedData); + } + + public static PublicKey getPublicKey(byte[] encryptedPublicKey) throws Exception { + return KeyFactory.getInstance(algorithm).generatePublic(new X509EncodedKeySpec(encryptedPublicKey)); + } + + public static PrivateKey getPrivateKey(byte[] encryptedPrivateKey) throws Exception { + return KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(encryptedPrivateKey)); + } + +} \ No newline at end of file diff --git a/server/openblocks-sdk/src/main/resources/locale_en.properties b/server/openblocks-sdk/src/main/resources/locale_en.properties index ec0ff56b..92b7915a 100644 --- a/server/openblocks-sdk/src/main/resources/locale_en.properties +++ b/server/openblocks-sdk/src/main/resources/locale_en.properties @@ -239,6 +239,12 @@ EVENT_TYPE_GROUP_MEMBER_ROLE_UPDATE=Group Member Role Update EVENT_TYPE_GROUP_MEMBER_LEAVE=Group Member Leave EVENT_TYPE_GROUP_MEMBER_REMOVE=Group Member Remove EVENT_TYPE_SERVER_START_UP=Server Startup +DATA_SOURCE_CREATE=Create data source +DATA_SOURCE_UPDATE=Update data source +DATA_SOURCE_DELETE=Delete data source +DATA_SOURCE_PERMISSION_GRANT=Grant data source permissions +DATA_SOURCE_PERMISSION_UPDATE=Update data source permissions +DATA_SOURCE_PERMISSION_DELETE=Delete data source permissions ## threshold EXCEED_MAX_USER_ORG_COUNT=Sorry, you have reached maximum count of workspaces. EXCEED_MAX_ORG_MEMBER_COUNT=Sorry, this workspace has reached the maximum count of members. diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/application/ApplicationApiService.java b/server/openblocks-server/src/main/java/com/openblocks/api/application/ApplicationApiService.java index 6b065469..8592bd91 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/application/ApplicationApiService.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/application/ApplicationApiService.java @@ -45,7 +45,7 @@ import com.openblocks.domain.application.model.ApplicationStatus; import com.openblocks.domain.application.model.ApplicationType; import com.openblocks.domain.application.service.ApplicationService; -import com.openblocks.domain.bizthreshold.BizThresholdChecker; +import com.openblocks.domain.bizthreshold.AbstractBizThresholdChecker; import com.openblocks.domain.datasource.model.Datasource; import com.openblocks.domain.group.service.GroupService; import com.openblocks.domain.interaction.UserApplicationInteractionService; @@ -104,7 +104,7 @@ public class ApplicationApiService { private UserService userService; @Autowired - private BizThresholdChecker bizThresholdChecker; + private AbstractBizThresholdChecker bizThresholdChecker; @Autowired private TemplateSolution templateSolution; diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/config/ConfigView.java b/server/openblocks-server/src/main/java/com/openblocks/api/config/ConfigView.java index cf34eb4b..ec57ed12 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/config/ConfigView.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/config/ConfigView.java @@ -3,6 +3,8 @@ import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.openblocks.sdk.auth.AbstractAuthConfig; import com.openblocks.sdk.constants.WorkspaceMode; @@ -15,6 +17,7 @@ public class ConfigView { private boolean isCloudHosting; private List authConfigs; - private boolean needUpdate; + @JsonInclude(Include.NON_EMPTY) + private String warning; private WorkspaceMode workspaceMode; } diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/datasource/DatasourceController.java b/server/openblocks-server/src/main/java/com/openblocks/api/datasource/DatasourceController.java index 860d2744..ee723212 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/datasource/DatasourceController.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/datasource/DatasourceController.java @@ -1,5 +1,11 @@ package com.openblocks.api.datasource; +import static com.openblocks.infra.event.EventType.DATA_SOURCE_CREATE; +import static com.openblocks.infra.event.EventType.DATA_SOURCE_DELETE; +import static com.openblocks.infra.event.EventType.DATA_SOURCE_PERMISSION_DELETE; +import static com.openblocks.infra.event.EventType.DATA_SOURCE_PERMISSION_GRANT; +import static com.openblocks.infra.event.EventType.DATA_SOURCE_PERMISSION_UPDATE; +import static com.openblocks.infra.event.EventType.DATA_SOURCE_UPDATE; import static com.openblocks.sdk.exception.BizError.INVALID_PARAMETER; import static com.openblocks.sdk.util.ExceptionUtils.ofError; import static com.openblocks.sdk.util.LocaleUtils.getLocale; @@ -11,6 +17,7 @@ import javax.annotation.Nullable; import javax.validation.Valid; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -28,6 +35,7 @@ import com.fasterxml.jackson.annotation.JsonView; import com.openblocks.api.framework.view.ResponseView; import com.openblocks.api.permission.view.CommonPermissionView; +import com.openblocks.api.util.BusinessEventPublisher; import com.openblocks.domain.datasource.model.Datasource; import com.openblocks.domain.datasource.service.DatasourceStructureService; import com.openblocks.domain.permission.model.ResourceRole; @@ -49,13 +57,17 @@ public class DatasourceController { private final DatasourceStructureService datasourceStructureService; private final DatasourceApiService datasourceApiService; private final UpsertDatasourceRequestMapper upsertDatasourceRequestMapper; + private final BusinessEventPublisher businessEventPublisher; @Autowired - public DatasourceController(DatasourceStructureService datasourceStructureService, - DatasourceApiService datasourceApiService, UpsertDatasourceRequestMapper upsertDatasourceRequestMapper) { + public DatasourceController( + DatasourceStructureService datasourceStructureService, + DatasourceApiService datasourceApiService, + UpsertDatasourceRequestMapper upsertDatasourceRequestMapper, BusinessEventPublisher businessEventPublisher) { this.datasourceStructureService = datasourceStructureService; this.datasourceApiService = datasourceApiService; this.upsertDatasourceRequestMapper = upsertDatasourceRequestMapper; + this.businessEventPublisher = businessEventPublisher; } @JsonView(JsonViews.Public.class) @@ -63,6 +75,7 @@ public DatasourceController(DatasourceStructureService datasourceStructureServic @ResponseStatus(HttpStatus.CREATED) public Mono> create(@Valid @RequestBody UpsertDatasourceRequest request) { return datasourceApiService.create(upsertDatasourceRequestMapper.resolve(request)) + .delayUntil(datasource -> businessEventPublisher.publishDatasourceEvent(datasource, DATA_SOURCE_CREATE)) .map(ResponseView::success); } @@ -79,12 +92,19 @@ public Mono> update(@PathVariable String id, @RequestBody UpsertDatasourceRequest request) { Datasource resolvedDatasource = upsertDatasourceRequestMapper.resolve(request); return datasourceApiService.update(id, resolvedDatasource) + .delayUntil(datasource -> businessEventPublisher.publishDatasourceEvent(datasource, DATA_SOURCE_UPDATE)) .map(ResponseView::success); } @DeleteMapping("/{id}") public Mono> delete(@PathVariable String id) { return datasourceApiService.delete(id) + .delayUntil(result -> { + if (BooleanUtils.isTrue(result)) { + return businessEventPublisher.publishDatasourceEvent(id, DATA_SOURCE_DELETE); + } + return Mono.empty(); + }) .map(ResponseView::success); } @@ -150,6 +170,13 @@ public Mono> grantPermission(@PathVariable String datasour return ofError(INVALID_PARAMETER, "INVALID_PARAMETER", request.role()); } return datasourceApiService.grantPermission(datasourceId, request.userIds(), request.groupIds(), role) + .delayUntil(result -> { + if (BooleanUtils.isTrue(result)) { + return businessEventPublisher.publishDatasourcePermissionEvent(datasourceId, request.userIds, + request.groupIds(), request.role(), DATA_SOURCE_PERMISSION_GRANT); + } + return Mono.empty(); + }) .map(ResponseView::success); } @@ -160,12 +187,19 @@ public Mono> updatePermission(@PathVariable("permissionId" return ofError(INVALID_PARAMETER, "INVALID_PARAMETER", request.role()); } return datasourceApiService.updatePermission(permissionId, request.getResourceRole()) + .delayUntil(result -> { + if (BooleanUtils.isTrue(result)) { + return businessEventPublisher.publishDatasourcePermissionEvent(permissionId, DATA_SOURCE_PERMISSION_UPDATE); + } + return Mono.empty(); + }) .map(ResponseView::success); } @DeleteMapping("/permissions/{permissionId}") public Mono> deletePermission(@PathVariable("permissionId") String permissionId) { - return datasourceApiService.deletePermission(permissionId) + return businessEventPublisher.publishDatasourcePermissionEvent(permissionId, DATA_SOURCE_PERMISSION_DELETE) + .then(datasourceApiService.deletePermission(permissionId)) .map(ResponseView::success); } diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/framework/security/SecurityConfig.java b/server/openblocks-server/src/main/java/com/openblocks/api/framework/security/SecurityConfig.java index 9442501e..7ae47cf5 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/framework/security/SecurityConfig.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/framework/security/SecurityConfig.java @@ -82,6 +82,7 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { ServerWebExchangeMatchers.pathMatchers(HttpMethod.HEAD, STATE_URL + "/healthCheck"), // used in public viewed apps ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, CONFIG_URL), // system config + ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, CONFIG_URL + "/deploymentId"), // system config ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, APPLICATION_URL + "/*/view"), // application view ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/me"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/currentUser"), @@ -101,6 +102,7 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.INVITATION_URL + "/**"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, NewUrl.CUSTOM_AUTH + "/logout"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.CONFIG_URL), + ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.CONFIG_URL + "/deploymentId"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.HEAD, NewUrl.STATE_URL + "/healthCheck"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.APPLICATION_URL + "/*/view"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.USER_URL + "/me"), diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/home/FolderController.java b/server/openblocks-server/src/main/java/com/openblocks/api/home/FolderController.java index 47ca6be8..291dc999 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/home/FolderController.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/home/FolderController.java @@ -23,6 +23,7 @@ import com.openblocks.api.util.BusinessEventPublisher; import com.openblocks.domain.application.model.ApplicationType; import com.openblocks.domain.folder.model.Folder; +import com.openblocks.domain.folder.service.FolderService; import com.openblocks.domain.permission.model.ResourceRole; import com.openblocks.infra.constant.NewUrl; import com.openblocks.infra.event.EventType; @@ -33,6 +34,8 @@ @RequestMapping(NewUrl.FOLDER_URL) public class FolderController { + @Autowired + private FolderService folderService; @Autowired private FolderApiService folderApiService; @Autowired @@ -53,11 +56,19 @@ public Mono> delete(@PathVariable("id") String folderId) { .then(Mono.fromSupplier(() -> ResponseView.success(null))); } + /** + * update name only. + */ @PutMapping public Mono> update(@RequestBody Folder folder) { - return folderApiService.update(folder) - .delayUntil(__ -> businessEventPublisher.publishFolderCommonEvent(folder.getId(), folder.getName(), EventType.FOLDER_UPDATE)) - .map(ResponseView::success); + return folderService.findById(folder.getId()) + .zipWhen(__ -> folderApiService.update(folder)) + .delayUntil(tuple2 -> { + Folder old = tuple2.getT1(); + return businessEventPublisher.publishFolderCommonEvent(folder.getId(), old.getName() + " => " + folder.getName(), + EventType.FOLDER_UPDATE); + }) + .map(tuple2 -> ResponseView.success(tuple2.getT2())); } /** diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/GroupApiService.java b/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/GroupApiService.java index 2632230e..496495ce 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/GroupApiService.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/GroupApiService.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -24,7 +23,7 @@ import com.openblocks.api.usermanagement.view.GroupView; import com.openblocks.api.usermanagement.view.UpdateGroupRequest; import com.openblocks.api.usermanagement.view.UpdateRoleRequest; -import com.openblocks.domain.bizthreshold.BizThresholdChecker; +import com.openblocks.domain.bizthreshold.AbstractBizThresholdChecker; import com.openblocks.domain.group.model.Group; import com.openblocks.domain.group.model.GroupMember; import com.openblocks.domain.group.service.GroupMemberService; @@ -51,7 +50,7 @@ public class GroupApiService { @Autowired private GroupService groupService; @Autowired - private BizThresholdChecker bizThresholdChecker; + private AbstractBizThresholdChecker bizThresholdChecker; public Mono getGroupMembers(String groupId, int page, int count) { Mono> groupAndOrgMemberInfo = getGroupAndOrgMemberInfo(groupId).cache(); @@ -89,7 +88,7 @@ public Mono getGroupMembers(String groupId, int page, return new GroupMemberView(orgMember, user); }) .filter(Objects::nonNull) - .collect(Collectors.toList())); + .toList()); }) .zipWith(visitorRoleMono) .map(tuple -> { @@ -209,7 +208,7 @@ public Mono create(CreateGroupRequest createGroupRequest) { return sessionUserService.getVisitorOrgMemberCache() .filter(OrgMember::isAdmin) .switchIfEmpty(deferredError(BizError.NOT_AUTHORIZED, NOT_AUTHORIZED)) - .transform(bizThresholdChecker::checkMaxGroupCount) + .delayUntil(orgMember -> bizThresholdChecker.checkMaxGroupCount(orgMember)) .flatMap(orgMember -> { String orgId = orgMember.getOrgId(); Group group = new Group(); diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/OrgApiServiceImpl.java b/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/OrgApiServiceImpl.java index 889ebd64..30230e78 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/OrgApiServiceImpl.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/usermanagement/OrgApiServiceImpl.java @@ -27,7 +27,7 @@ import com.openblocks.api.usermanagement.view.OrgView; import com.openblocks.api.usermanagement.view.UpdateOrgRequest; import com.openblocks.api.usermanagement.view.UpdateRoleRequest; -import com.openblocks.domain.bizthreshold.BizThresholdChecker; +import com.openblocks.domain.bizthreshold.AbstractBizThresholdChecker; import com.openblocks.domain.organization.event.OrgMemberLeftEvent; import com.openblocks.domain.organization.model.MemberRole; import com.openblocks.domain.organization.model.OrgMember; @@ -61,7 +61,7 @@ public class OrgApiServiceImpl implements OrgApiService { @Autowired private OrganizationService organizationService; @Autowired - private BizThresholdChecker bizThresholdChecker; + private AbstractBizThresholdChecker bizThresholdChecker; @Autowired private ApplicationContext applicationContext; @Autowired diff --git a/server/openblocks-server/src/main/java/com/openblocks/api/util/BusinessEventPublisher.java b/server/openblocks-server/src/main/java/com/openblocks/api/util/BusinessEventPublisher.java index 616caa4d..ad1ef805 100644 --- a/server/openblocks-server/src/main/java/com/openblocks/api/util/BusinessEventPublisher.java +++ b/server/openblocks-server/src/main/java/com/openblocks/api/util/BusinessEventPublisher.java @@ -1,5 +1,10 @@ package com.openblocks.api.util; +import static com.openblocks.domain.permission.model.ResourceHolder.USER; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Locale; import java.util.Optional; @@ -16,18 +21,25 @@ import com.openblocks.api.usermanagement.view.AddMemberRequest; import com.openblocks.api.usermanagement.view.UpdateRoleRequest; import com.openblocks.domain.application.service.ApplicationService; +import com.openblocks.domain.datasource.model.Datasource; +import com.openblocks.domain.datasource.service.DatasourceService; import com.openblocks.domain.folder.model.Folder; import com.openblocks.domain.folder.service.FolderService; import com.openblocks.domain.group.model.Group; import com.openblocks.domain.group.model.GroupMember; import com.openblocks.domain.group.service.GroupService; import com.openblocks.domain.organization.model.OrgMember; +import com.openblocks.domain.permission.model.ResourceHolder; +import com.openblocks.domain.permission.model.ResourcePermission; +import com.openblocks.domain.permission.service.ResourcePermissionService; import com.openblocks.domain.user.model.User; import com.openblocks.domain.user.service.UserService; import com.openblocks.infra.event.ApplicationCommonEvent; import com.openblocks.infra.event.EventType; import com.openblocks.infra.event.FolderCommonEvent; import com.openblocks.infra.event.QueryExecutionEvent; +import com.openblocks.infra.event.datasource.DatasourceEvent; +import com.openblocks.infra.event.datasource.DatasourcePermissionEvent; import com.openblocks.infra.event.group.GroupCreateEvent; import com.openblocks.infra.event.group.GroupDeleteEvent; import com.openblocks.infra.event.group.GroupUpdateEvent; @@ -58,6 +70,10 @@ public class BusinessEventPublisher { private FolderService folderService; @Autowired private ApplicationService applicationService; + @Autowired + private DatasourceService datasourceService; + @Autowired + private ResourcePermissionService resourcePermissionService; public Mono publishFolderCommonEvent(String folderId, String folderName, EventType eventType) { return sessionUserService.getVisitorOrgMemberCache() @@ -366,4 +382,79 @@ public Mono publishGroupMemberRemoveEvent(boolean publish, GroupMember pre public void publishQueryExecutionEvent(QueryExecutionEvent queryExecutionEvent) { applicationEventPublisher.publishEvent(queryExecutionEvent); } + + public Mono publishDatasourceEvent(String id, EventType eventType) { + return datasourceService.getById(id) + .flatMap(datasource -> publishDatasourceEvent(datasource, eventType)) + .onErrorResume(throwable -> { + log.error("publishDatasourceEvent error.", throwable); + return Mono.empty(); + }); + } + + public Mono publishDatasourceEvent(Datasource datasource, EventType eventType) { + return sessionUserService.getVisitorOrgMemberCache() + .flatMap(orgMember -> { + DatasourceEvent event = DatasourceEvent.builder() + .datasourceId(datasource.getId()) + .name(datasource.getName()) + .type(datasource.getType()) + .eventType(eventType) + .userId(orgMember.getUserId()) + .orgId(orgMember.getOrgId()) + .build(); + applicationEventPublisher.publishEvent(event); + return Mono. empty(); + }) + .onErrorResume(throwable -> { + log.error("publishDatasourceEvent error.", throwable); + return Mono.empty(); + }); + } + + public Mono publishDatasourcePermissionEvent(String permissionId, EventType eventType) { + return resourcePermissionService.getById(permissionId) + .zipWhen(resourcePermission -> datasourceService.getById(resourcePermission.getResourceId())) + .flatMap(tuple -> { + ResourcePermission resourcePermission = tuple.getT1(); + ResourceHolder holder = resourcePermission.getResourceHolder(); + Datasource datasource = tuple.getT2(); + return publishDatasourcePermissionEvent(datasource.getId(), + holder == USER ? List.of(resourcePermission.getResourceHolderId()) : Collections.emptyList(), + holder == USER ? Collections.emptyList() : List.of(resourcePermission.getResourceHolderId()), + resourcePermission.getResourceRole().getValue(), + eventType); + }) + .onErrorResume(throwable -> { + log.error("publishDatasourcePermissionEvent error.", throwable); + return Mono.empty(); + }); + } + + public Mono publishDatasourcePermissionEvent(String datasourceId, + Collection userIds, Collection groupIds, String role, + EventType eventType) { + return Mono.zip(sessionUserService.getVisitorOrgMemberCache(), datasourceService.getById(datasourceId)) + .flatMap(tuple -> { + OrgMember orgMember = tuple.getT1(); + Datasource datasource = tuple.getT2(); + DatasourcePermissionEvent datasourcePermissionEvent = DatasourcePermissionEvent.builder() + .datasourceId(datasourceId) + .name(datasource.getName()) + .type(datasource.getType()) + .userId(orgMember.getUserId()) + .orgId(orgMember.getOrgId()) + .userIds(userIds) + .groupIds(groupIds) + .role(role) + .eventType(eventType) + .build(); + applicationEventPublisher.publishEvent(datasourcePermissionEvent); + return Mono. empty(); + }) + .onErrorResume(throwable -> { + log.error("publishDatasourcePermissionEvent error.", throwable); + return Mono.empty(); + }); + } } diff --git a/server/openblocks-server/src/main/resources/application-openblocks.yml b/server/openblocks-server/src/main/resources/application-openblocks.yml index 6a70bfe7..be25e902 100644 --- a/server/openblocks-server/src/main/resources/application-openblocks.yml +++ b/server/openblocks-server/src/main/resources/application-openblocks.yml @@ -35,7 +35,7 @@ common: security: cors-allowed-domains: - '*' - version: 1.1.2 + version: 1.1.3 block-hound-enable: false material: diff --git a/server/openblocks-server/src/main/resources/selfhost/ce/application.yml b/server/openblocks-server/src/main/resources/selfhost/ce/application.yml index 283b2aed..380a5577 100644 --- a/server/openblocks-server/src/main/resources/selfhost/ce/application.yml +++ b/server/openblocks-server/src/main/resources/selfhost/ce/application.yml @@ -26,7 +26,7 @@ common: domain: default-value: openblocks.dev cloud: false - version: 1.1.2 + version: 1.1.3 block-hound-enable: false material: