diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/DimensionProvider.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/DimensionProvider.java index 06c36a277..e849eb5db 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/DimensionProvider.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/DimensionProvider.java @@ -4,9 +4,9 @@ import reactor.core.publisher.Flux; public interface DimensionProvider { - Flux getAllType(); + Flux getAllType(); - Flux getDimensionByUserId(String userId); + Flux getDimensionByUserId(String userId); Flux getUserIdByDimensionId(String dimensionId); diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfig.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfig.java index 50c21d46a..150e0117c 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfig.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfig.java @@ -62,12 +62,6 @@ public interface DataAccessConfig extends Serializable { * @see OwnCreatedDataAccessConfig#getType() */ String OWN_CREATED = "OWN_CREATED"; - /** - * 字段值范围 - * - * @see FieldScopeDataAccessConfig#getType() - */ - String FIELD_SCOPE = "FIELD_SCOPE"; /** * 禁止操作字段 @@ -76,6 +70,13 @@ public interface DataAccessConfig extends Serializable { */ String DENY_FIELDS = "DENY_FIELDS"; + /** + * 禁止操作字段 + * + * @see org.hswebframework.web.authorization.simple.DimensionDataAccessConfig#getType() + */ + String DIMENSION_SCOPE = "DIMENSION_SCOPE"; + } } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfiguration.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfiguration.java new file mode 100644 index 000000000..992b0f9a0 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfiguration.java @@ -0,0 +1,4 @@ +package org.hswebframework.web.authorization.access; + +public interface DataAccessConfiguration { +} diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DimensionHelper.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DimensionHelper.java new file mode 100644 index 000000000..24bc18002 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DimensionHelper.java @@ -0,0 +1,67 @@ +package org.hswebframework.web.authorization.access; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.collections.CollectionUtils; +import org.hswebframework.web.authorization.Authentication; +import org.hswebframework.web.authorization.Dimension; +import org.hswebframework.web.authorization.DimensionType; +import org.hswebframework.web.authorization.Permission; +import org.hswebframework.web.authorization.simple.DimensionDataAccessConfig; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public abstract class DimensionHelper { + + + public static Set getDimensionDataAccessScope(Authentication atz, + Permission permission, + String action, + String dimensionType) { + return permission + .getDataAccesses(action) + .stream() + .filter(DimensionDataAccessConfig.class::isInstance) + .map(DimensionDataAccessConfig.class::cast) + .filter(conf -> dimensionType.equals(conf.getScopeType())) + .flatMap(conf -> { + if (CollectionUtils.isEmpty(conf.getScope())) { + return atz.getDimensions(dimensionType) + .stream() + .map(Dimension::getId); + } + return conf.getScope().stream(); + }).collect(Collectors.toSet()); + } + + public static Set getDimensionDataAccessScope(Authentication atz, + Permission permission, + String action, + DimensionType dimensionType) { + return getDimensionDataAccessScope(atz, permission, action, dimensionType.getId()); + } + + + public static Set getDimensionDataAccessScope(Authentication atz, + String permission, + String action, + String dimensionType) { + return atz + .getPermission(permission) + .map(per -> getDimensionDataAccessScope(atz, per, action, dimensionType)).orElseGet(Collections::emptySet); + } + + public static Set getDimensionDataAccessScope(Authentication atz, + String permission, + String action, + DimensionType dimensionType) { + return atz + .getPermission(permission) + .map(per -> getDimensionDataAccessScope(atz, per, action, dimensionType)) + .orElseGet(Collections::emptySet); + } + +} diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java index 6d7ee4f41..33d8bc093 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java @@ -1,5 +1,6 @@ package org.hswebframework.web.authorization.annotation; +import org.hswebframework.web.authorization.access.DataAccessConfiguration; import org.hswebframework.web.authorization.access.DataAccessController; import java.lang.annotation.*; @@ -19,5 +20,8 @@ public @interface DataAccessType { /** * @see DataAccessController */ - Class controller() default DataAccessController.class; + Class controller() default DataAccessController.class; + + Class configuration() default DataAccessConfiguration.class; + } \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java new file mode 100644 index 000000000..fe4e4f0bd --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java @@ -0,0 +1,31 @@ +package org.hswebframework.web.authorization.annotation; + +import org.hswebframework.web.authorization.define.Phased; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.*; + +@DataAccessType(id = "dimension", name = "维度数据权限") +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) +@Authorize +public @interface DimensionDataAccess { + + Mapping[] mapping() default {}; + + @AliasFor(annotation = Authorize.class) + Phased phased() default Phased.before; + + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) + @interface Mapping { + String dimensionType(); + + String property(); + + int idParamIndex() default -1; + } + +} diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/ResourceAction.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/ResourceAction.java index a7ba2b7c0..335ef878d 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/ResourceAction.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/ResourceAction.java @@ -1,5 +1,7 @@ package org.hswebframework.web.authorization.annotation; +import org.hswebframework.web.authorization.define.Phased; + import java.lang.annotation.*; /** diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessTypeDefinition.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessTypeDefinition.java index ad959c02b..abdce6057 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessTypeDefinition.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessTypeDefinition.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.Setter; import org.hswebframework.web.authorization.access.DataAccessController; import org.hswebframework.web.authorization.access.DataAccessType; +import org.hswebframework.web.authorization.access.DataAccessConfiguration; import org.hswebframework.web.bean.FastBeanCopier; @Getter @@ -19,6 +20,8 @@ public class DataAccessTypeDefinition implements DataAccessType { private Class controller; + private Class configuration; + public DataAccessTypeDefinition copy(){ return FastBeanCopier.copy(this,DataAccessTypeDefinition::new); } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourceDefinition.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourceDefinition.java index 6b93dc960..065cbdc64 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourceDefinition.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourceDefinition.java @@ -33,6 +33,8 @@ public class ResourceDefinition { private Logical logical = Logical.DEFAULT; + private Phased phased = Phased.before; + public static ResourceDefinition of(String id, String name) { ResourceDefinition definition = new ResourceDefinition(); definition.setId(id); diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java index 145bd8502..befc744d9 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java @@ -3,7 +3,7 @@ package org.hswebframework.web.authorization.simple; import org.hswebframework.web.authorization.*; import org.hswebframework.web.authorization.builder.AuthenticationBuilderFactory; import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory; -import org.hswebframework.web.authorization.simple.builder.DataAccessConfigConvert; +import org.hswebframework.web.authorization.simple.builder.DataAccessConfigConverter; import org.hswebframework.web.authorization.simple.builder.SimpleAuthenticationBuilderFactory; import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory; import org.hswebframework.web.authorization.token.DefaultUserTokenManager; @@ -18,7 +18,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import java.util.List; @@ -30,7 +29,7 @@ import java.util.List; public class DefaultAuthorizationAutoConfiguration { @Autowired(required = false) - private List dataAccessConfigConverts; + private List dataAccessConfigConverters; @Bean @ConditionalOnMissingBean(UserTokenManager.class) @@ -69,8 +68,8 @@ public class DefaultAuthorizationAutoConfiguration { @ConfigurationProperties(prefix = "hsweb.authorization.data-access", ignoreInvalidFields = true) public SimpleDataAccessConfigBuilderFactory dataAccessConfigBuilderFactory() { SimpleDataAccessConfigBuilderFactory factory = new SimpleDataAccessConfigBuilderFactory(); - if (null != dataAccessConfigConverts) { - dataAccessConfigConverts.forEach(factory::addConvert); + if (null != dataAccessConfigConverters) { + dataAccessConfigConverters.forEach(factory::addConvert); } return factory; } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DimensionDataAccessConfig.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DimensionDataAccessConfig.java new file mode 100644 index 000000000..18b453489 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DimensionDataAccessConfig.java @@ -0,0 +1,30 @@ +package org.hswebframework.web.authorization.simple; + +import lombok.Getter; +import lombok.Setter; +import org.hswebframework.web.authorization.DimensionType; +import org.hswebframework.web.authorization.access.DataAccessType; +import org.hswebframework.web.authorization.access.DefaultDataAccessType; +import org.hswebframework.web.authorization.access.ScopeDataAccessConfig; +import org.hswebframework.web.authorization.simple.AbstractDataAccessConfig; + +import java.util.Set; + +@Getter +@Setter +public class DimensionDataAccessConfig extends AbstractDataAccessConfig implements ScopeDataAccessConfig { + + private Set scope; + + private boolean children; + + /** + * @see DimensionType#getId() + */ + private String scopeType; + + @Override + public DefaultDataAccessType getType() { + return DefaultDataAccessType.DIMENSION_SCOPE; + } +} diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConvert.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConverter.java similarity index 87% rename from hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConvert.java rename to hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConverter.java index 5d75f706f..cae56d363 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConvert.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConverter.java @@ -5,7 +5,7 @@ import org.hswebframework.web.authorization.access.DataAccessConfig; /** * @author zhouhao */ -public interface DataAccessConfigConvert { +public interface DataAccessConfigConverter { boolean isSupport(String type, String action, String config); diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilder.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilder.java index 357535ce7..2d3784a2c 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilder.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilder.java @@ -15,12 +15,12 @@ import java.util.Objects; */ public class SimpleDataAccessConfigBuilder implements DataAccessConfigBuilder { - private List converts; + private List converts; private Map config = new HashMap<>(); - public SimpleDataAccessConfigBuilder(List converts) { + public SimpleDataAccessConfigBuilder(List converts) { Objects.requireNonNull(converts); this.converts = converts; } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilderFactory.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilderFactory.java index 129bf7a32..82d9d9071 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilderFactory.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilderFactory.java @@ -23,12 +23,11 @@ public class SimpleDataAccessConfigBuilderFactory implements DataAccessConfigBui private List defaultSupportConvert = Arrays.asList( OWN_CREATED, - FIELD_SCOPE, DENY_FIELDS); - private List converts = new LinkedList<>(); + private List converts = new LinkedList<>(); - public SimpleDataAccessConfigBuilderFactory addConvert(DataAccessConfigConvert configBuilderConvert) { + public SimpleDataAccessConfigBuilderFactory addConvert(DataAccessConfigConverter configBuilderConvert) { Objects.requireNonNull(configBuilderConvert); converts.add(configBuilderConvert); return this; @@ -42,13 +41,13 @@ public class SimpleDataAccessConfigBuilderFactory implements DataAccessConfigBui return defaultSupportConvert; } - protected DataAccessConfigConvert createJsonConfig(String supportType, Class clazz) { + protected DataAccessConfigConverter createJsonConfig(String supportType, Class clazz) { return createConfig(supportType, (action, config) -> JSON.parseObject(config, clazz)); } - protected DataAccessConfigConvert createConfig(String supportType, BiFunction function) { - return new DataAccessConfigConvert() { + protected DataAccessConfigConverter createConfig(String supportType, BiFunction function) { + return new DataAccessConfigConverter() { @Override public boolean isSupport(String type, String action, String config) { return supportType.equals(type); @@ -73,6 +72,10 @@ public class SimpleDataAccessConfigBuilderFactory implements DataAccessConfigBui converts.add(createJsonConfig(DENY_FIELDS, SimpleFieldFilterDataAccessConfig.class)); } + if (defaultSupportConvert.contains(DIMENSION_SCOPE)) { + converts.add(createJsonConfig(DIMENSION_SCOPE, DimensionDataAccessConfig.class)); + } + if (defaultSupportConvert.contains(OWN_CREATED)) { converts.add(createConfig(OWN_CREATED, (action, config) -> new SimpleOwnCreatedDataAccessConfig(action))); } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/RedisUserTokenManager.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/RedisUserTokenManager.java new file mode 100644 index 000000000..d182001e6 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/RedisUserTokenManager.java @@ -0,0 +1,92 @@ +package org.hswebframework.web.authorization.token; + +import org.springframework.data.redis.core.ReactiveHashOperations; +import org.springframework.data.redis.core.ReactiveRedisOperations; +import org.springframework.data.redis.core.ReactiveSetOperations; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class RedisUserTokenManager implements UserTokenManager { + + ReactiveRedisOperations operations; + + ReactiveHashOperations userTokenStore; + + ReactiveSetOperations userTokenMapping; + + Map localCache=new ConcurrentHashMap<>(); + + @Override + public Mono getByToken(String token) { + + return null; + } + + @Override + public Flux getByUserId(String userId) { + return null; + } + + @Override + public Mono userIsLoggedIn(String userId) { + return null; + } + + @Override + public Mono tokenIsLoggedIn(String token) { + return null; + } + + @Override + public Mono totalUser() { + return null; + } + + @Override + public Mono totalToken() { + return null; + } + + @Override + public Flux allLoggedUser() { + return null; + } + + @Override + public Mono signOutByUserId(String userId) { + return null; + } + + @Override + public Mono signOutByToken(String token) { + return null; + } + + @Override + public Mono changeUserState(String userId, TokenState state) { + return null; + } + + @Override + public Mono changeTokenState(String token, TokenState state) { + return null; + } + + @Override + public Mono signIn(String token, String type, String userId, long maxInactiveInterval) { + return null; + } + + @Override + public Mono touch(String token) { + return null; + } + + @Override + public Mono checkExpiredToken() { + return null; + } +} diff --git a/hsweb-authorization/hsweb-authorization-basic/pom.xml b/hsweb-authorization/hsweb-authorization-basic/pom.xml index 3ad22e91b..7a74a196b 100644 --- a/hsweb-authorization/hsweb-authorization-basic/pom.xml +++ b/hsweb-authorization/hsweb-authorization-basic/pom.xml @@ -30,6 +30,12 @@ spring-boot-starter-aop + + org.hswebframework.web + hsweb-commons-crud + ${project.version} + + org.hswebframework.web hsweb-access-logging-api @@ -45,6 +51,7 @@ org.springframework spring-webmvc + true @@ -73,6 +80,18 @@ test + + org.springframework.boot.experimental + spring-boot-starter-data-r2dbc + test + + + + io.r2dbc + r2dbc-h2 + test + + org.springframework spring-aspects diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java index bc22cf901..5c2eb555a 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java @@ -70,6 +70,7 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor context.setAuthentication(auth); Function afterRuner = runnable -> { MethodInterceptorContext interceptorContext = holder.createParamContext(invoker.get()); + context.setParamContext(interceptorContext); runnable.run(); return (Publisher) interceptorContext.getInvokeResult(); }; diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java index 22ff47907..fda119eb8 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java @@ -163,6 +163,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { resource.setId(ann.id()); resource.setName(ann.name()); resource.setLogical(ann.logical()); + resource.setPhased(ann.phased()); resource.setDescription(String.join("\n", ann.description())); for (ResourceAction action : ann.actions()) { putAnnotation(resource, action); @@ -193,6 +194,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { typeDefinition.setId(dataAccessType.id()); typeDefinition.setName(dataAccessType.name()); typeDefinition.setController(dataAccessType.controller()); + typeDefinition.setConfiguration(dataAccessType.configuration()); typeDefinition.setDescription(String.join("\n", dataAccessType.description())); } definition.getDataAccess() @@ -205,6 +207,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { typeDefinition.setId(dataAccessType.id()); typeDefinition.setName(dataAccessType.name()); typeDefinition.setController(dataAccessType.controller()); + typeDefinition.setConfiguration(dataAccessType.configuration()); typeDefinition.setDescription(String.join("\n", dataAccessType.description())); definition.getDataAccess() .getDataAccessTypes() diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DataAccessHandlerContext.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DataAccessHandlerContext.java new file mode 100644 index 000000000..27963a3bb --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DataAccessHandlerContext.java @@ -0,0 +1,60 @@ +package org.hswebframework.web.authorization.basic.handler.access; + +import lombok.Getter; +import lombok.Setter; +import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository; +import org.hswebframework.utils.ClassUtils; +import org.hswebframework.web.aop.MethodInterceptorContext; +import org.hswebframework.web.authorization.Authentication; +import org.hswebframework.web.authorization.Dimension; +import org.hswebframework.web.authorization.DimensionType; +import org.hswebframework.web.authorization.define.AuthorizingContext; +import org.hswebframework.web.crud.web.reactive.*; + +import java.util.List; + +@Getter +@Setter +public class DataAccessHandlerContext { + + private Class entityType; + + private ReactiveRepository repository; + + private Authentication authentication; + + private List dimensions; + + private MethodInterceptorContext paramContext; + + public static DataAccessHandlerContext of(AuthorizingContext context, String type) { + DataAccessHandlerContext requestContext = new DataAccessHandlerContext(); + Authentication authentication = context.getAuthentication(); + requestContext.setDimensions(authentication.getDimensions(type)); + requestContext.setAuthentication(context.getAuthentication()); + requestContext.setParamContext(context.getParamContext()); + + Object target = context.getParamContext().getTarget(); + Class entityType = ClassUtils.getGenericType(org.springframework.util.ClassUtils.getUserClass(target)); + if (entityType != Object.class) { + requestContext.setEntityType(entityType); + } + + if (target instanceof ReactiveQueryController) { + requestContext.setRepository(((ReactiveQueryController) target).getRepository()); + } else if (target instanceof ReactiveSaveController) { + requestContext.setRepository(((ReactiveSaveController) target).getRepository()); + } else if (target instanceof ReactiveDeleteController) { + requestContext.setRepository(((ReactiveDeleteController) target).getRepository()); + } else if (target instanceof ReactiveServiceQueryController) { + requestContext.setRepository(((ReactiveServiceQueryController) target).getService().getRepository()); + } else if (target instanceof ReactiveServiceSaveController) { + requestContext.setRepository(((ReactiveServiceSaveController) target).getService().getRepository()); + } else if (target instanceof ReactiveServiceDeleteController) { + requestContext.setRepository(((ReactiveServiceDeleteController) target).getService().getRepository()); + } + // TODO: 2019-11-18 not reactive implements + + return requestContext; + } +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java index 0b1480ba9..5c67912c5 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java @@ -30,7 +30,8 @@ public final class DefaultDataAccessController implements DataAccessController { throw new UnsupportedOperationException(); } this.parent = parent; - addHandler(new FieldFilterDataAccessHandler()); + addHandler(new FieldFilterDataAccessHandler()) + .addHandler(new DimensionDataAccessHandler()); } @Override diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DimensionDataAccessHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DimensionDataAccessHandler.java new file mode 100644 index 000000000..355af86f2 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DimensionDataAccessHandler.java @@ -0,0 +1,382 @@ +package org.hswebframework.web.authorization.basic.handler.access; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.hswebframework.ezorm.core.param.Param; +import org.hswebframework.ezorm.core.param.QueryParam; +import org.hswebframework.web.api.crud.entity.Entity; +import org.hswebframework.web.api.crud.entity.QueryParamEntity; +import org.hswebframework.web.authorization.Authentication; +import org.hswebframework.web.authorization.Dimension; +import org.hswebframework.web.authorization.Permission; +import org.hswebframework.web.authorization.access.DataAccessConfig; +import org.hswebframework.web.authorization.access.DataAccessHandler; +import org.hswebframework.web.authorization.annotation.DimensionDataAccess; +import org.hswebframework.web.authorization.define.AuthorizingContext; +import org.hswebframework.web.authorization.exception.AccessDenyException; +import org.hswebframework.web.authorization.simple.DimensionDataAccessConfig; +import org.hswebframework.web.bean.FastBeanCopier; +import org.hswebframework.web.utils.AnnotationUtils; +import org.reactivestreams.Publisher; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Slf4j +public class DimensionDataAccessHandler implements DataAccessHandler { + @Override + public boolean isSupport(DataAccessConfig access) { + return access instanceof DimensionDataAccessConfig; + } + + @Override + public boolean handle(DataAccessConfig access, AuthorizingContext context) { + DimensionDataAccessConfig config = ((DimensionDataAccessConfig) access); + DataAccessHandlerContext requestContext = DataAccessHandlerContext.of(context, config.getScopeType()); + if (!checkSupported(config, requestContext)) { + return false; + } + switch (access.getAction()) { + case Permission.ACTION_QUERY: + case Permission.ACTION_GET: + return doHandleQuery(config, requestContext); + case Permission.ACTION_ADD: + case Permission.ACTION_SAVE: + case Permission.ACTION_UPDATE: + return doHandleUpdate(config, requestContext); + case Permission.ACTION_DELETE: + return doHandleDelete(config, requestContext); + default: + if (log.isDebugEnabled()) { + log.debug("data access [{}] not support for {}", config.getType().getId(), access.getAction()); + } + return true; + } + + } + + @SneakyThrows + protected String getProperty(DimensionDataAccessConfig cfg, + DataAccessHandlerContext ct) { + return Optional.ofNullable( + getMappingInfo(ct).get(cfg.getScopeType())) + .map(MappingInfo::getProperty) + .orElseGet(() -> { + log.warn("{} not supported dimension data access", ct.getParamContext().getMethod()); + return null; + }); + } + + protected boolean checkSupported(DimensionDataAccessConfig cfg, DataAccessHandlerContext ctx) { + Authentication authentication = ctx.getAuthentication(); + + /* + DataAccessHelper.assert() + */ + if (CollectionUtils.isEmpty(ctx.getDimensions())) { + log.warn("user:[{}] dimension not setup", authentication.getUser().getId()); + return false; + } + + if (!getMappingInfo(ctx).containsKey(cfg.getScopeType())) { + log.warn("{} not supported dimension data access.see annotation: @DimensionDataAccess", ctx.getParamContext().getMethod()); + return false; + } + + return true; + } + + protected boolean doHandleDelete(DimensionDataAccessConfig cfg, + DataAccessHandlerContext context) { + + + // TODO: 2019-11-18 + return doHandleUpdate(cfg, context); + + } + + @SuppressWarnings("all") + protected Object handleUpdateById(DimensionDataAccessConfig config, + DataAccessHandlerContext context, + MappingInfo mappingInfo, + Object id) { + List dimensions = context.getDimensions(); + + Set scope = CollectionUtils.isNotEmpty(config.getScope()) ? + config.getScope() : + dimensions + .stream() + .map(Dimension::getId) + .collect(Collectors.toSet()); + + Function, Mono> reactiveCheck = obj -> context + .getRepository() + .findById(obj) + .doOnNext(r -> { + Object val = FastBeanCopier.copy(r, new HashMap<>(), FastBeanCopier.include(mappingInfo.getProperty())) + .get(mappingInfo.getProperty()); + if (!StringUtils.isEmpty(val) + && !scope.contains(val)) { + throw new AccessDenyException(); + } + }) + .then(); + if (id instanceof Publisher) { + if (id instanceof Mono) { + return ((Mono) id) + .flatMap(r -> reactiveCheck.apply(r instanceof Collection ? ((Collection) r) : Collections.singleton(r))) + .then((Mono) id); + } + if (id instanceof Flux) { + return ((Flux) id) + .collectList() + .flatMap(reactiveCheck) + .thenMany((Flux) id); + } + } + Collection idVal = id instanceof Collection ? ((Collection) id) : Collections.singleton(id); + + Object result = context.getParamContext().getInvokeResult(); + if (result instanceof Mono) { + context.getParamContext() + .setInvokeResult(((Mono) result) + .flatMap(res -> { + return reactiveCheck.apply(idVal).thenReturn(res); + })); + } else if (result instanceof Flux) { + context.getParamContext() + .setInvokeResult(((Flux) result) + .flatMap(res -> { + return reactiveCheck.apply(idVal).thenReturn(res); + })); + } else { + // TODO: 2019-11-19 非响应式处理 + } + return id; + } + + protected boolean doHandleUpdate(DimensionDataAccessConfig cfg, + DataAccessHandlerContext context) { + MappingInfo info = getMappingInfo(context).get(cfg.getScopeType()); + if (info != null) { + if (info.idParamIndex != -1) { + Object param = context.getParamContext().getArguments()[info.idParamIndex]; + context.getParamContext().getArguments()[info.idParamIndex] = handleUpdateById(cfg, context, info, param); + return true; + } + } else { + return true; + } + + boolean reactive = context.getParamContext() + .handleReactiveArguments(publisher -> { + if (publisher instanceof Mono) { + return Mono.from(publisher) + .flatMap(payload -> applyReactiveUpdatePayload(cfg, info, Collections.singleton(payload), context) + .thenReturn(payload)); + } + if (publisher instanceof Flux) { + return Flux.from(publisher) + .collectList() + .flatMapMany(list -> + applyReactiveUpdatePayload(cfg, info, list, context) + .flatMapIterable(v -> list)); + } + + return publisher; + }); + + if (!reactive) { + applyUpdatePayload(cfg, info, Arrays + .stream(context.getParamContext().getArguments()) + .flatMap(obj -> { + if (obj instanceof Collection) { + return ((Collection) obj).stream(); + } + return Stream.of(obj); + }) + .filter(Entity.class::isInstance) + .collect(Collectors.toSet()), context); + + return true; + } + return true; + + } + + protected void applyUpdatePayload(DimensionDataAccessConfig config, + MappingInfo mappingInfo, + Collection payloads, + DataAccessHandlerContext context) { + List dimensions = context.getDimensions(); + + Set scope = CollectionUtils.isNotEmpty(config.getScope()) ? + config.getScope() : + dimensions + .stream() + .map(Dimension::getId) + .collect(Collectors.toSet()); + + for (Object payload : payloads) { + if (!(payload instanceof Entity)) { + continue; + } + if (payload instanceof Param) { + applyQueryParam(config, context, ((Param) payload)); + continue; + } + String property = mappingInfo.getProperty(); + Map map = FastBeanCopier.copy(payload, new HashMap<>(), FastBeanCopier.include(property)); + Object value = map.get(property); + if (StringUtils.isEmpty(value)) { + if (dimensions.size() == 1) { + map.put(property, dimensions.get(0).getId()); + FastBeanCopier.copy(map, payload, property); + } + continue; + } + if (CollectionUtils.isNotEmpty(scope)) { + if (!scope.contains(value)) { + throw new AccessDenyException(); + } + } + } + } + + protected Mono applyReactiveUpdatePayload(DimensionDataAccessConfig config, + MappingInfo info, + Collection payloads, + DataAccessHandlerContext context) { + + return Mono.fromRunnable(() -> applyUpdatePayload(config, info, payloads, context)); + } + + protected boolean doHandleQuery(DimensionDataAccessConfig cfg, DataAccessHandlerContext requestContext) { + boolean reactive = requestContext.getParamContext().handleReactiveArguments(publisher -> { + if (publisher instanceof Mono) { + return Mono + .from(publisher) + .flatMap(param -> this + .applyReactiveQueryParam(cfg, requestContext, param) + .thenReturn(param)); + } + + return publisher; + }); + + if (!reactive) { + Object[] args = requestContext.getParamContext().getArguments(); + this.applyQueryParam(cfg, requestContext, args); + } + return true; + } + + protected String getTermType(DimensionDataAccessConfig cfg) { + return "in"; + } + + protected void applyQueryParam(DimensionDataAccessConfig cfg, + DataAccessHandlerContext requestContext, + Param param) { + Set scope = CollectionUtils.isNotEmpty(cfg.getScope()) ? + cfg.getScope() : + requestContext.getDimensions() + .stream() + .map(Dimension::getId) + .collect(Collectors.toSet()); + + QueryParamEntity entity = new QueryParamEntity(); + entity.setTerms(new ArrayList<>(param.getTerms())); + entity.toNestQuery(query -> + query.where( + getProperty(cfg, requestContext), + getTermType(cfg), + scope)); + param.setTerms(entity.getTerms()); + } + + protected void applyQueryParam(DimensionDataAccessConfig cfg, + DataAccessHandlerContext requestContext, + Object... params) { + for (Object param : params) { + if (param instanceof QueryParam) { + applyQueryParam(cfg, requestContext, (QueryParam) param); + } + } + } + + protected Mono applyReactiveQueryParam(DimensionDataAccessConfig cfg, + DataAccessHandlerContext requestContext, + Object... param) { + + + return Mono.fromRunnable(() -> applyQueryParam(cfg, requestContext, param)); + } + + private Map> cache = new ConcurrentHashMap<>(); + + + public Map getMappingInfo(DataAccessHandlerContext context) { + return getMappingInfo(ClassUtils.getUserClass(context.getParamContext().getTarget()), context.getParamContext().getMethod()); + + } + + private Set> ann = new HashSet<>(Arrays.asList(DimensionDataAccess.class, DimensionDataAccess.Mapping.class)); + + + private Map getMappingInfo(Class target, Method method) { + + return cache.computeIfAbsent(method, m -> { + Set methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, ann); + Set classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(target, ann); + + if (CollectionUtils.isEmpty(methodAnnotation)) { + return Collections.emptyMap(); + } + List all = new ArrayList<>(classAnnotation); + all.addAll(methodAnnotation); + + Map mappingInfoMap = new HashMap<>(); + for (Annotation annotation : all) { + if (annotation instanceof DimensionDataAccess) { + for (DimensionDataAccess.Mapping mapping : ((DimensionDataAccess) annotation).mapping()) { + mappingInfoMap.put(mapping.dimensionType(), MappingInfo.of(mapping)); + } + } + if (annotation instanceof DimensionDataAccess.Mapping) { + mappingInfoMap.put(((DimensionDataAccess.Mapping) annotation).dimensionType(), MappingInfo.of(((DimensionDataAccess.Mapping) annotation))); + } + } + return mappingInfoMap; + }); + } + + @Getter + @Setter + @AllArgsConstructor + static class MappingInfo { + String dimension; + + String property; + + int idParamIndex; + + static MappingInfo of(DimensionDataAccess.Mapping mapping) { + return new MappingInfo(mapping.dimensionType(), mapping.property(), mapping.idParamIndex()); + } + } +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java index 11878aca3..736dd60ac 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java @@ -39,6 +39,7 @@ public class FieldFilterDataAccessHandler implements DataAccessHandler { case Permission.ACTION_GET: return doQueryAccess(filterDataAccessConfig, context); case Permission.ACTION_ADD: + case Permission.ACTION_SAVE: case Permission.ACTION_UPDATE: return doUpdateAccess(filterDataAccessConfig, context); default: diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java index 2b6454ebd..a75ac701b 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java @@ -2,36 +2,20 @@ package org.hswebframework.web.authorization.basic.aop; import org.hswebframework.ezorm.core.param.Param; import org.hswebframework.ezorm.core.param.QueryParam; -import org.hswebframework.web.authorization.Authentication; -import org.hswebframework.web.authorization.ReactiveAuthenticationHolder; -import org.hswebframework.web.authorization.ReactiveAuthenticationSupplier; -import org.hswebframework.web.authorization.User; -import org.hswebframework.web.authorization.basic.handler.access.FieldFilterDataAccessHandler; -import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser; +import org.hswebframework.ezorm.core.param.Term; +import org.hswebframework.web.authorization.*; import org.hswebframework.web.authorization.exception.AccessDenyException; -import org.hswebframework.web.authorization.exception.UnAuthorizedException; -import org.hswebframework.web.authorization.simple.SimpleAuthentication; -import org.hswebframework.web.authorization.simple.SimpleFieldFilterDataAccessConfig; -import org.hswebframework.web.authorization.simple.SimplePermission; -import org.hswebframework.web.authorization.simple.SimpleUser; -import org.hswebframework.web.authorization.token.ParsedToken; -import org.junit.Before; +import org.hswebframework.web.authorization.simple.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; - -import static org.junit.Assert.*; +import java.util.*; +import java.util.function.Function; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = TestApplication.class) @@ -76,10 +60,10 @@ public class AopAuthorizingControllerTest { } @Test - public void testFiledDeny(){ + public void testFiledDeny() { SimpleAuthentication authentication = new SimpleAuthentication(); - SimpleFieldFilterDataAccessConfig config=new SimpleFieldFilterDataAccessConfig(); + SimpleFieldFilterDataAccessConfig config = new SimpleFieldFilterDataAccessConfig(); config.setAction("query"); config.setFields(new HashSet<>(Arrays.asList("name"))); @@ -104,13 +88,84 @@ public class AopAuthorizingControllerTest { testController.queryUser(new QueryParam()) .map(Param::getExcludes) .as(StepVerifier::create) - .expectNextMatches(f->f.contains("name")) + .expectNextMatches(f -> f.contains("name")) .verifyComplete(); testController.queryUser(Mono.just(new QueryParam())) .map(Param::getExcludes) .as(StepVerifier::create) - .expectNextMatches(f->f.contains("name")) + .expectNextMatches(f -> f.contains("name")) .verifyComplete(); } + + @Test + public void testDimensionDataAccess() { + SimpleAuthentication authentication = new SimpleAuthentication(); + + DimensionDataAccessConfig config = new DimensionDataAccessConfig(); + config.setAction("query"); + config.setScopeType("role"); + + DimensionDataAccessConfig config2 = new DimensionDataAccessConfig(); + config2.setAction("save"); + config2.setScopeType("role"); + ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() { + @Override + public Mono get(String userId) { + return Mono.empty(); + } + + @Override + public Mono get() { + return Mono.just(authentication); + } + }); + + authentication.setUser(SimpleUser.builder().id("test").username("test").build()); + authentication.setPermissions(Arrays.asList(SimplePermission.builder() + .actions(new HashSet<>(Arrays.asList("query", "save"))) + .dataAccesses(new HashSet<>(Arrays.asList(config, config2))) + .id("test").build())); + authentication.setDimensions(Collections.singletonList(Dimension.of("test", "test", DefaultDimensionType.role))); + + testController.queryUserByDimension(Mono.just(new QueryParam())) + .map(Param::getTerms) + .flatMapIterable(Function.identity()) + .next() + .map(Term::getValue) + .>map(Collection.class::cast) + .flatMapIterable(Function.identity()) + .next() + .as(StepVerifier::create) + .expectNextMatches("test"::equals) + .verifyComplete(); + + TestEntity testEntity = new TestEntity(); + testEntity.setRoleId("123"); + + testController.save(Mono.just(testEntity)) + .as(StepVerifier::create) + .expectError(AccessDenyException.class) + .verify(); + + testController.add(Mono.just(testEntity)) + .as(StepVerifier::create) + .expectNextCount(1) + .verifyComplete(); + + testController.update(testEntity.getId(),Mono.just(testEntity)) + .as(StepVerifier::create) + .expectError(AccessDenyException.class) + .verify(); + + testEntity = new TestEntity(); + testEntity.setRoleId("test"); + + testController.save(Mono.just(testEntity)) + .as(StepVerifier::create) + .expectNextCount(1) + .verifyComplete(); + + + } } \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestApplication.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestApplication.java index 003de9d5e..ee57c9694 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestApplication.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestApplication.java @@ -1,11 +1,13 @@ package org.hswebframework.web.authorization.basic.aop; import org.hswebframework.web.authorization.basic.configuration.EnableAopAuthorize; +import org.hswebframework.web.crud.annotation.EnableEasyormRepository; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAopAuthorize +@EnableEasyormRepository("org.hswebframework.web.authorization.basic.aop") public class TestApplication { public static void main(String[] args) { diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java index 5a0900960..880eaed5f 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java @@ -1,18 +1,21 @@ package org.hswebframework.web.authorization.basic.aop; import org.hswebframework.ezorm.core.param.QueryParam; +import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository; import org.hswebframework.web.authorization.Authentication; import org.hswebframework.web.authorization.User; -import org.hswebframework.web.authorization.access.DataAccessConfig; import org.hswebframework.web.authorization.annotation.*; import org.hswebframework.web.authorization.define.Phased; import org.hswebframework.web.authorization.exception.UnAuthorizedException; +import org.hswebframework.web.crud.web.reactive.ReactiveCrudController; +import org.hswebframework.web.crud.web.reactive.ReactiveQueryController; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController @Resource(id = "test", name = "测试") -public class TestController { +public class TestController implements ReactiveCrudController { @QueryAction public Mono getUser() { @@ -28,15 +31,41 @@ public class TestController { .map(Authentication::getUser); } - @QueryAction(dataAccess = @DataAccess(type = @DataAccessType(id= DataAccessConfig.DefaultType.DENY_FIELDS,name = "禁止访问字段"))) + @QueryAction + @FieldDataAccess public Mono queryUser(QueryParam queryParam) { return Mono.just(queryParam); } - @QueryAction(dataAccess = @DataAccess(type = @DataAccessType(id= DataAccessConfig.DefaultType.DENY_FIELDS,name = "禁止访问字段"))) + @QueryAction + @FieldDataAccess public Mono queryUser(Mono queryParam) { return queryParam; } + @QueryAction + @TestDataAccess + public Mono queryUserByDimension(Mono queryParam) { + return queryParam; + } + @SaveAction + @TestDataAccess + public Mono save(Mono param) { + return param; + } + + @Override + @TestDataAccess(idParamIndex = 0,phased = Phased.after) + public Mono update(String id, Mono payload) { + return ReactiveCrudController.super.update(id, payload); + } + + @Autowired + ReactiveRepository reactiveRepository; + + @Override + public ReactiveRepository getRepository() { + return reactiveRepository; + } } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestDataAccess.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestDataAccess.java new file mode 100644 index 000000000..79b14502b --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestDataAccess.java @@ -0,0 +1,22 @@ +package org.hswebframework.web.authorization.basic.aop; + +import org.hswebframework.web.authorization.annotation.DimensionDataAccess; +import org.hswebframework.web.authorization.define.Phased; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) +@DimensionDataAccess +@DimensionDataAccess.Mapping(dimensionType = "role", property = "roleId") +public @interface TestDataAccess { + + @AliasFor(annotation = DimensionDataAccess.Mapping.class) + int idParamIndex() default -1; + + @AliasFor(annotation = DimensionDataAccess.class) + Phased phased() default Phased.before; + +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestEntity.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestEntity.java new file mode 100644 index 000000000..c4038b57f --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestEntity.java @@ -0,0 +1,20 @@ +package org.hswebframework.web.authorization.basic.aop; + +import lombok.Getter; +import lombok.Setter; +import org.hswebframework.web.api.crud.entity.GenericEntity; +import reactor.core.publisher.Mono; + +import javax.persistence.Column; +import javax.persistence.Table; + +@Getter +@Setter +@Table(name = "test_entity") +public class TestEntity extends GenericEntity { + + @Column + private String roleId; + + +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/resources/application.yml b/hsweb-authorization/hsweb-authorization-basic/src/test/resources/application.yml index c11306ee7..7851f791a 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/test/resources/application.yml +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/resources/application.yml @@ -7,4 +7,9 @@ hsweb: permissions-simple: user-token: - get - - update \ No newline at end of file + - update +easyorm: + dialect: h2 +logging: + level: + org.hswebframework: debug \ No newline at end of file diff --git a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java index 0599d73f0..391f18f33 100644 --- a/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java +++ b/hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/QueryParamEntity.java @@ -101,6 +101,9 @@ public class QueryParamEntity extends QueryParam { if (null != before) { before.accept(query); } + if(terms.isEmpty()){ + return query; + } return query .nest() .each(terms, NestConditional::accept) diff --git a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultDimensionService.java b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultDimensionService.java index 86da611d2..66e5cdea1 100644 --- a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultDimensionService.java +++ b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultDimensionService.java @@ -18,6 +18,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.List; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -46,15 +47,14 @@ public class DefaultDimensionService } @Override - public Flux getAllType() { + public Flux getAllType() { return dimensionTypeRepository .createQuery() - .fetch() - .cast(DimensionType.class); + .fetch(); } @Override - public Flux getDimensionByUserId(String userId) { + public Flux getDimensionByUserId(String userId) { return getAllType() .collect(Collectors.toMap(DimensionType::getId, Function.identity())) .flatMapMany(typeGrouping -> @@ -62,7 +62,19 @@ public class DefaultDimensionService .createQuery() .where(DimensionUserEntity::getUserId, userId) .fetch() - .map(entity -> DynamicDimension.of(entity, typeGrouping.get(entity.getDimensionTypeId())))); + .collectList() + .flatMapMany(list -> { + //查询所有的维度 + return this.findById(Flux.fromIterable(list.stream() + .map(DimensionUserEntity::getDimensionId) + .collect(Collectors.toSet()))) + .map(dimension -> + DynamicDimension.of(dimension, typeGrouping.get(dimension.getTypeId())) + ) + ; + + }) + ); } @Override diff --git a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DynamicDimension.java b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DynamicDimension.java index f7faf1e5b..404c73611 100644 --- a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DynamicDimension.java +++ b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DynamicDimension.java @@ -4,8 +4,10 @@ import lombok.Getter; import lombok.Setter; import org.hswebframework.web.authorization.Dimension; import org.hswebframework.web.authorization.DimensionType; +import org.hswebframework.web.system.authorization.api.entity.DimensionEntity; import org.hswebframework.web.system.authorization.api.entity.DimensionUserEntity; +import java.util.HashMap; import java.util.Map; @Getter @@ -18,12 +20,16 @@ public class DynamicDimension implements Dimension { private Map options; - public static DynamicDimension of(DimensionUserEntity entity, - DimensionType type){ - DynamicDimension dynamicDimension=new DynamicDimension(); - dynamicDimension.setId(entity.getDimensionId()); - dynamicDimension.setName(entity.getDimensionName()); + public static DynamicDimension of(DimensionEntity entity, + DimensionType type) { + DynamicDimension dynamicDimension = new DynamicDimension(); + dynamicDimension.setId(entity.getId()); + dynamicDimension.setName(entity.getName()); dynamicDimension.setType(type); + Map options = new HashMap<>(); + options.put("parentId", entity.getParentId()); + options.put("path", entity.getPath()); + dynamicDimension.setOptions(options); return dynamicDimension; }