权限细分

This commit is contained in:
mxd
2021-07-09 23:08:18 +08:00
parent dea4ff820d
commit eb5613d960
11 changed files with 251 additions and 73 deletions

View File

@@ -11,6 +11,7 @@ import org.ssssssss.magicapi.model.JsonBean;
import org.ssssssss.magicapi.provider.ApiServiceProvider;
import org.ssssssss.magicapi.provider.MagicAPIService;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
@@ -36,8 +37,9 @@ public class MagicAPIController extends MagicController implements MagicExceptio
*/
@RequestMapping("/delete")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.DELETE)
public JsonBean<Boolean> delete(String id) {
@Valid(readonly = false)
public JsonBean<Boolean> delete(HttpServletRequest request, String id) {
isTrue(allowVisit(request, Authorization.VIEW, getApiInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.deleteApi(id));
}
@@ -46,9 +48,13 @@ public class MagicAPIController extends MagicController implements MagicExceptio
*/
@RequestMapping("/list")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<List<ApiInfo>> list() {
return new JsonBean<>(magicAPIService.apiList().stream().map(ApiInfo::simple).collect(Collectors.toList()));
public JsonBean<List<ApiInfo>> list(HttpServletRequest request) {
return new JsonBean<>(magicAPIService.apiList()
.stream()
.filter(it -> allowVisit(request, Authorization.VIEW, it))
.map(ApiInfo::simple)
.collect(Collectors.toList())
);
}
/**
@@ -58,8 +64,8 @@ public class MagicAPIController extends MagicController implements MagicExceptio
*/
@RequestMapping("/get")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<ApiInfo> get(String id) {
public JsonBean<ApiInfo> get(HttpServletRequest request, String id) {
isTrue(allowVisit(request, Authorization.VIEW, getApiInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.getApiInfo(id));
}
@@ -70,8 +76,8 @@ public class MagicAPIController extends MagicController implements MagicExceptio
*/
@RequestMapping("/backups")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<List<Long>> backups(String id) {
public JsonBean<List<Long>> backups(HttpServletRequest request, String id) {
isTrue(allowVisit(request, Authorization.VIEW, getApiInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(apiServiceProvider.backupList(id));
}
@@ -83,8 +89,8 @@ public class MagicAPIController extends MagicController implements MagicExceptio
*/
@RequestMapping("/backup/get")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<ApiInfo> backups(String id, Long timestamp) {
public JsonBean<ApiInfo> backups(HttpServletRequest request, String id, Long timestamp) {
isTrue(allowVisit(request, Authorization.VIEW, getApiInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(apiServiceProvider.backupInfo(id, timestamp));
}
@@ -93,21 +99,26 @@ public class MagicAPIController extends MagicController implements MagicExceptio
*/
@RequestMapping("/api/move")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.SAVE)
public JsonBean<Boolean> apiMove(String id, String groupId) {
@Valid(readonly = false)
public JsonBean<Boolean> apiMove(HttpServletRequest request, String id, String groupId) {
isTrue(allowVisit(request, Authorization.SAVE, getApiInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.moveApi(id, groupId));
}
/**
* 保存接口
*
* @param info 接口信息
*/
@RequestMapping("/save")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.SAVE)
public JsonBean<String> save(@RequestBody ApiInfo info) {
@Valid(readonly = false)
public JsonBean<String> save(HttpServletRequest request, @RequestBody ApiInfo info) {
isTrue(allowVisit(request, Authorization.SAVE, info), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.saveApi(info));
}
private ApiInfo getApiInfo(String id){
ApiInfo apiInfo = magicAPIService.getApiInfo(id);
notNull(apiInfo, API_NOT_FOUND);
return apiInfo;
}
}

View File

@@ -8,9 +8,7 @@ import org.ssssssss.magicapi.exception.InvalidArgumentException;
import org.ssssssss.magicapi.exception.MagicLoginException;
import org.ssssssss.magicapi.interceptor.Authorization;
import org.ssssssss.magicapi.interceptor.MagicUser;
import org.ssssssss.magicapi.model.Constants;
import org.ssssssss.magicapi.model.JsonBean;
import org.ssssssss.magicapi.model.JsonCodeConstants;
import org.ssssssss.magicapi.model.*;
import javax.servlet.http.HttpServletRequest;
@@ -44,6 +42,50 @@ public class MagicController implements JsonCodeConstants {
return configuration.getAuthorizationInterceptor().allowVisit(magicUser, request, authorization);
}
/**
* 判断是否有权限访问接口
*/
boolean allowVisit(HttpServletRequest request, Authorization authorization, ApiInfo apiInfo) {
if (authorization == null) {
return true;
}
MagicUser magicUser = (MagicUser) request.getAttribute(Constants.ATTRIBUTE_MAGIC_USER);
return configuration.getAuthorizationInterceptor().allowVisit(magicUser, request, authorization, apiInfo);
}
/**
* 判断是否有权限访问函数
*/
boolean allowVisit(HttpServletRequest request, Authorization authorization, FunctionInfo functionInfo) {
if (authorization == null) {
return true;
}
MagicUser magicUser = (MagicUser) request.getAttribute(Constants.ATTRIBUTE_MAGIC_USER);
return configuration.getAuthorizationInterceptor().allowVisit(magicUser, request, authorization, functionInfo);
}
/**
* 判断是否有权限访问分组
*/
boolean allowVisit(HttpServletRequest request, Authorization authorization, Group group) {
if (authorization == null) {
return true;
}
MagicUser magicUser = (MagicUser) request.getAttribute(Constants.ATTRIBUTE_MAGIC_USER);
return configuration.getAuthorizationInterceptor().allowVisit(magicUser, request, authorization, group);
}
/**
* 判断是否有权限访问分组
*/
boolean allowVisit(HttpServletRequest request, Authorization authorization, DataSourceInfo dataSourceInfo) {
if (authorization == null) {
return true;
}
MagicUser magicUser = (MagicUser) request.getAttribute(Constants.ATTRIBUTE_MAGIC_USER);
return configuration.getAuthorizationInterceptor().allowVisit(magicUser, request, authorization, dataSourceInfo);
}
@ExceptionHandler(MagicLoginException.class)
@ResponseBody
public JsonBean<Void> invalidLogin(MagicLoginException exception) {

View File

@@ -6,11 +6,13 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.ssssssss.magicapi.config.MagicConfiguration;
import org.ssssssss.magicapi.config.Valid;
import org.ssssssss.magicapi.interceptor.Authorization;
import org.ssssssss.magicapi.model.DataSourceInfo;
import org.ssssssss.magicapi.model.JsonBean;
import org.ssssssss.magicapi.provider.MagicAPIService;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MagicDataSourceController extends MagicController implements MagicExceptionHandler {
@@ -26,14 +28,17 @@ public class MagicDataSourceController extends MagicController implements MagicE
*/
@RequestMapping("/datasource/list")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<List<Map<String, Object>>> list() {
return new JsonBean<>(magicAPIService.datasourceList());
public JsonBean<List<DataSourceInfo>> list(HttpServletRequest request) {
return new JsonBean<>(magicAPIService.datasourceList()
.stream()
.filter(it -> allowVisit(request, Authorization.VIEW, it))
.collect(Collectors.toList())
);
}
@RequestMapping("/datasource/test")
@ResponseBody
public JsonBean<String> test(@RequestBody Map<String, String> properties) {
public JsonBean<String> test(@RequestBody DataSourceInfo properties) {
return new JsonBean<>(magicAPIService.testDataSource(properties));
}
@@ -43,9 +48,10 @@ public class MagicDataSourceController extends MagicController implements MagicE
* @param properties 数据源配置信息
*/
@RequestMapping("/datasource/save")
@Valid(readonly = false, authorization = Authorization.DATASOURCE_SAVE)
@Valid(readonly = false)
@ResponseBody
public JsonBean<String> save(@RequestBody Map<String, String> properties) {
public JsonBean<String> save(HttpServletRequest request, @RequestBody DataSourceInfo properties) {
isTrue(allowVisit(request, Authorization.SAVE, properties), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.saveDataSource(properties));
}
@@ -55,16 +61,25 @@ public class MagicDataSourceController extends MagicController implements MagicE
* @param id 数据源ID
*/
@RequestMapping("/datasource/delete")
@Valid(readonly = false, authorization = Authorization.DATASOURCE_DELETE)
@Valid(readonly = false)
@ResponseBody
public JsonBean<Boolean> delete(String id) {
public JsonBean<Boolean> delete(HttpServletRequest request, String id) {
DataSourceInfo dataSource = getDataSourceInfo(id);
isTrue(allowVisit(request, Authorization.DELETE, dataSource), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.deleteDataSource(id));
}
@RequestMapping("/datasource/detail")
@Valid(authorization = Authorization.DATASOURCE_VIEW)
@ResponseBody
public JsonBean<Object> detail(String id) {
return new JsonBean<>(magicAPIService.getDataSource(id));
public JsonBean<DataSourceInfo> detail(HttpServletRequest request, String id) {
DataSourceInfo dataSource = getDataSourceInfo(id);
isTrue(allowVisit(request, Authorization.VIEW, dataSource), PERMISSION_INVALID);
return new JsonBean<>(dataSource);
}
private DataSourceInfo getDataSourceInfo(String id){
DataSourceInfo dataSource = magicAPIService.getDataSource(id);
notNull(dataSource, DATASOURCE_NOT_FOUND);
return dataSource;
}
}

View File

@@ -11,7 +11,9 @@ import org.ssssssss.magicapi.model.JsonBean;
import org.ssssssss.magicapi.provider.FunctionServiceProvider;
import org.ssssssss.magicapi.provider.MagicAPIService;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
public class MagicFunctionController extends MagicController implements MagicExceptionHandler {
@@ -27,51 +29,63 @@ public class MagicFunctionController extends MagicController implements MagicExc
@RequestMapping("/function/list")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<List<FunctionInfo>> list() {
return new JsonBean<>(magicAPIService.functionList());
public JsonBean<List<FunctionInfo>> list(HttpServletRequest request) {
return new JsonBean<>(magicAPIService.functionList()
.stream()
.filter(it -> allowVisit(request, Authorization.VIEW, it))
.collect(Collectors.toList())
);
}
@RequestMapping("/function/get")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<FunctionInfo> get(String id) {
public JsonBean<FunctionInfo> get(HttpServletRequest request, String id) {
isTrue(allowVisit(request, Authorization.VIEW, getFunctionInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.getFunctionInfo(id));
}
@RequestMapping("/function/backup/get")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<FunctionInfo> backups(String id, Long timestamp) {
public JsonBean<FunctionInfo> backups(HttpServletRequest request, String id, Long timestamp) {
isTrue(allowVisit(request, Authorization.VIEW, getFunctionInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(functionService.backupInfo(id, timestamp));
}
@RequestMapping("/function/backups")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<List<Long>> backups(String id) {
public JsonBean<List<Long>> backups(HttpServletRequest request, String id) {
isTrue(allowVisit(request, Authorization.VIEW, getFunctionInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(functionService.backupList(id));
}
@RequestMapping("/function/move")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.SAVE)
public JsonBean<Boolean> move(String id, String groupId) {
@Valid(readonly = false)
public JsonBean<Boolean> move(HttpServletRequest request, String id, String groupId) {
isTrue(allowVisit(request, Authorization.SAVE, getFunctionInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.moveFunction(id, groupId));
}
@RequestMapping("/function/save")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.SAVE)
public JsonBean<String> save(@RequestBody FunctionInfo functionInfo) {
@Valid(readonly = false)
public JsonBean<String> save(HttpServletRequest request, @RequestBody FunctionInfo functionInfo) {
isTrue(allowVisit(request, Authorization.SAVE, functionInfo), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.saveFunction(functionInfo));
}
@RequestMapping("/function/delete")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.DELETE)
public JsonBean<Boolean> delete(String id) {
@Valid(readonly = false)
public JsonBean<Boolean> delete(HttpServletRequest request, String id) {
isTrue(allowVisit(request, Authorization.DELETE, getFunctionInfo(id)), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.deleteFunction(id));
}
public FunctionInfo getFunctionInfo(String id){
FunctionInfo functionInfo = magicAPIService.getFunctionInfo(id);
notNull(functionInfo, FUNCTION_NOT_FOUND);
return functionInfo;
}
}

View File

@@ -10,7 +10,9 @@ import org.ssssssss.magicapi.model.Group;
import org.ssssssss.magicapi.model.JsonBean;
import org.ssssssss.magicapi.provider.MagicAPIService;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
public class MagicGroupController extends MagicController implements MagicExceptionHandler {
@@ -26,8 +28,11 @@ public class MagicGroupController extends MagicController implements MagicExcept
*/
@RequestMapping("/group/delete")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.DELETE)
public JsonBean<Boolean> deleteGroup(String groupId) {
@Valid(readonly = false)
public JsonBean<Boolean> deleteGroup(HttpServletRequest request, String groupId) {
Group group = magicAPIService.getGroup(groupId);
notNull(group, GROUP_NOT_FOUND);
isTrue(allowVisit(request, Authorization.DELETE, group), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.deleteGroup(groupId));
}
@@ -36,8 +41,9 @@ public class MagicGroupController extends MagicController implements MagicExcept
*/
@RequestMapping("/group/update")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.SAVE)
public synchronized JsonBean<Boolean> groupUpdate(@RequestBody Group group) {
@Valid(readonly = false)
public synchronized JsonBean<Boolean> groupUpdate(HttpServletRequest request, @RequestBody Group group) {
isTrue(allowVisit(request, Authorization.SAVE, group), PERMISSION_INVALID);
if (magicAPIService.updateGroup(group)) {
return new JsonBean<>(true);
}
@@ -49,9 +55,12 @@ public class MagicGroupController extends MagicController implements MagicExcept
*/
@RequestMapping("/group/list")
@ResponseBody
@Valid(authorization = Authorization.VIEW)
public JsonBean<List<Group>> groupList(String type) {
return new JsonBean<>(magicAPIService.groupList(type));
public JsonBean<List<Group>> groupList(HttpServletRequest request, String type) {
return new JsonBean<>(magicAPIService.groupList(type)
.stream()
.filter(it -> allowVisit(request, Authorization.VIEW, it))
.collect(Collectors.toList())
);
}
/**
@@ -59,8 +68,9 @@ public class MagicGroupController extends MagicController implements MagicExcept
*/
@RequestMapping("/group/create")
@ResponseBody
@Valid(readonly = false, authorization = Authorization.SAVE)
public JsonBean<String> createGroup(@RequestBody Group group) {
@Valid(readonly = false)
public JsonBean<String> createGroup(HttpServletRequest request, @RequestBody Group group) {
isTrue(allowVisit(request, Authorization.SAVE, group), PERMISSION_INVALID);
return new JsonBean<>(magicAPIService.createGroup(group));
}
}

View File

@@ -1,5 +1,32 @@
package org.ssssssss.magicapi.interceptor;
public enum Authorization {
NONE, SAVE, VIEW, DELETE, DOWNLOAD, UPLOAD, PUSH, DATASOURCE_SAVE, DATASOURCE_VIEW, DATASOURCE_DELETE
/**
* 无实际意义
*/
NONE,
/**
* 执行保存动作
*/
SAVE,
/**
* 执行查看详情、列表动作
*/
VIEW,
/**
* 执行删除动作
*/
DELETE,
/**
* 执行导出动作
*/
DOWNLOAD,
/**
* 执行上传动作
*/
UPLOAD,
/**
* 执行推送动作
*/
PUSH
}

View File

@@ -1,6 +1,10 @@
package org.ssssssss.magicapi.interceptor;
import org.ssssssss.magicapi.exception.MagicLoginException;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.model.DataSourceInfo;
import org.ssssssss.magicapi.model.FunctionInfo;
import org.ssssssss.magicapi.model.Group;
import javax.servlet.http.HttpServletRequest;
@@ -44,4 +48,32 @@ public interface AuthorizationInterceptor {
default boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization) {
return true;
}
/**
* 是否拥有对该接口的增删改权限
*/
default boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, ApiInfo apiInfo) {
return allowVisit(magicUser, request, authorization);
}
/**
* 是否拥有对该函数的增删改权限
*/
default boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, FunctionInfo functionInfo) {
return allowVisit(magicUser, request, authorization);
}
/**
* 是否拥有对该分组的增删改权限
*/
default boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, Group group) {
return allowVisit(magicUser, request, authorization);
}
/**
* 是否拥有对该数据源的增删改权限
*/
default boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, DataSourceInfo dataSourceInfo) {
return allowVisit(magicUser, request, authorization);
}
}

View File

@@ -0,0 +1,10 @@
package org.ssssssss.magicapi.model;
import java.util.HashMap;
public class DataSourceInfo extends HashMap<String, String> {
public String getId() {
return get("id");
}
}

View File

@@ -73,6 +73,8 @@ public interface JsonCodeConstants {
JsonCode API_NOT_FOUND = new JsonCode(1001, "api not found");
JsonCode FUNCTION_NOT_FOUND = new JsonCode(1002, "function not found");
JsonCode DATASOURCE_KEY_REQUIRED = new JsonCode(0, "数据源Key不能为空");
JsonCode DATASOURCE_KEY_EXISTS = new JsonCode(0, "数据源%s已存在或名称重复");

View File

@@ -130,6 +130,12 @@ public interface MagicAPIService extends MagicModule {
*/
List<Group> groupList(String type);
/**
* 查询分组详情
* @param id 分组ID
*/
Group getGroup(String id);
/**
* 注册数据源
*/
@@ -140,12 +146,12 @@ public interface MagicAPIService extends MagicModule {
*
* @param id 数据源id
*/
Map<String, String> getDataSource(String id);
DataSourceInfo getDataSource(String id);
/**
* 数据源列表
*/
List<Map<String, Object>> datasourceList();
List<DataSourceInfo> datasourceList();
/**
* 测试数据源
@@ -153,7 +159,7 @@ public interface MagicAPIService extends MagicModule {
* @param properties 数据源属性
* @return 返回错误说明,连接正常返回 null
*/
String testDataSource(Map<String, String> properties);
String testDataSource(DataSourceInfo properties);
/**
* 保存数据源
@@ -161,7 +167,7 @@ public interface MagicAPIService extends MagicModule {
* @param properties 数据源属性
* @return 返回数据源ID
*/
String saveDataSource(Map<String, String> properties);
String saveDataSource(DataSourceInfo properties);
/**
* 删除数据源

View File

@@ -390,6 +390,15 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
return groupServiceProvider.groupList(type);
}
@Override
public Group getGroup(String id) {
Resource groupResource = groupServiceProvider.getGroupResource(id);
if(groupResource != null && (groupResource = groupResource.getResource(GROUP_METABASE)).exists()){
return groupServiceProvider.readGroup(groupResource);
}
return null;
}
@Override
public void registerAllDataSource() {
datasourceResource.readAll();
@@ -406,7 +415,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
}
}
private String registerDataSource(Map<String, String> properties) {
private String registerDataSource(DataSourceInfo properties) {
if (properties != null) {
String key = properties.get("key");
String name = properties.getOrDefault("name", key);
@@ -419,27 +428,27 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
}
@Override
public Map<String, String> getDataSource(String id) {
public DataSourceInfo getDataSource(String id) {
Resource resource = this.datasourceResource.getResource(id + ".json");
byte[] bytes = resource.read();
isTrue(bytes != null && bytes.length > 0, DATASOURCE_NOT_FOUND);
TypeFactory factory = TypeFactory.defaultInstance();
return JsonUtils.readValue(bytes, factory.constructMapType(LinkedHashMap.class, String.class, String.class));
return JsonUtils.readValue(bytes, DataSourceInfo.class);
}
@Override
public List<Map<String, Object>> datasourceList() {
public List<DataSourceInfo> datasourceList() {
return magicDynamicDataSource.datasourceNodes().stream().map(it -> {
Map<String, Object> row = new HashMap<>();
row.put("id", it.getId()); // id为空的则认为是不可修改的
row.put("key", it.getKey()); // 如果为null 说明是主数据源
row.put("name", it.getName());
return row;
DataSourceInfo info = new DataSourceInfo();
info.put("id", it.getId()); // id为空的则认为是不可修改的
info.put("key", it.getKey()); // 如果为null 说明是主数据源
info.put("name", it.getName());
return info;
}).collect(Collectors.toList());
}
@Override
public String testDataSource(Map<String, String> properties) {
public String testDataSource(DataSourceInfo properties) {
DataSource dataSource = null;
try {
dataSource = createDataSource(properties);
@@ -454,7 +463,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
}
@Override
public String saveDataSource(Map<String, String> properties) {
public String saveDataSource(DataSourceInfo properties) {
String key = properties.get("key");
// 校验key是否符合规则
notBlank(key, DATASOURCE_KEY_REQUIRED);
@@ -746,7 +755,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
}
// copy from DataSourceBuilder
private DataSource createDataSource(Map<String, String> properties) {
private DataSource createDataSource(DataSourceInfo properties) {
Class<? extends DataSource> dataSourceType = getDataSourceType(properties.get("type"));
if (!properties.containsKey("driverClassName")
&& properties.containsKey("url")) {