Skip to content

Commit

Permalink
dashboard: Improve the ACL checking mechanism (alibaba#1042)
Browse files Browse the repository at this point in the history
* Add `@AuthAction` annotation support
  • Loading branch information
lkxiaolou authored and sczyh30 committed Dec 2, 2019
1 parent 99a6d80 commit d2d27e6
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 166 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.auth;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface AuthAction {

AuthService.PrivilegeType value();

String targetName() default "app";

String message() default "No privilege";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.auth;

import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

@Component
public class AuthInterceptor implements HandlerInterceptor {

@Autowired
private AuthService<HttpServletRequest> authService;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
Method method = ((HandlerMethod) handler).getMethod();

AuthAction authAction = method.getAnnotation(AuthAction.class);
if (authAction != null) {
AuthService.AuthUser authUser = authService.getAuthUser(request);
if (authUser == null) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
String target = request.getParameter(authAction.targetName());

if (!authUser.authTarget(target, authAction.value())) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
}
}

return true;
}

private void responseNoPrivilegeMsg(HttpServletResponse response, String message) throws IOException {
Result result = Result.ofFail(-1, message);
response.addHeader("Content-Type", "application/json;charset=UTF-8");
response.getOutputStream().write(JSON.toJSONBytes(result));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
package com.alibaba.csp.sentinel.dashboard.config;

import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import com.alibaba.csp.sentinel.dashboard.auth.AuthInterceptor;
import com.alibaba.csp.sentinel.dashboard.filter.AuthFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
Expand All @@ -40,6 +42,14 @@ public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthFilter authFilter;

@Autowired
private AuthInterceptor authInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
Expand Down Expand Up @@ -60,16 +57,11 @@ public class AuthorityRuleController {
@Autowired
private RuleRepository<AuthorityRuleEntity, Long> repository;

@Autowired
private AuthService<HttpServletRequest> authService;

@GetMapping("/rules")
public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(HttpServletRequest request,
@RequestParam String app,
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(app, PrivilegeType.READ_RULE);
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
Expand Down Expand Up @@ -119,10 +111,8 @@ private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity) {
}

@PostMapping("/rule")
public Result<AuthorityRuleEntity> apiAddAuthorityRule(HttpServletRequest request,
@RequestBody AuthorityRuleEntity entity) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<AuthorityRuleEntity> apiAddAuthorityRule(@RequestBody AuthorityRuleEntity entity) {
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
Expand All @@ -144,11 +134,9 @@ public Result<AuthorityRuleEntity> apiAddAuthorityRule(HttpServletRequest reques
}

@PutMapping("/rule/{id}")
public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(HttpServletRequest request,
@PathVariable("id") Long id,
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,
@RequestBody AuthorityRuleEntity entity) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);
if (id == null || id <= 0) {
return Result.ofFail(-1, "Invalid id");
}
Expand Down Expand Up @@ -176,16 +164,15 @@ public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(HttpServletRequest req
}

@DeleteMapping("/rule/{id}")
public Result<Long> apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) {
AuthUser authUser = authService.getAuthUser(request);
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {
if (id == null) {
return Result.ofFail(-1, "id cannot be null");
}
AuthorityRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}
authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);
try {
repository.delete(id);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
Expand Down Expand Up @@ -54,14 +51,10 @@ public class DegradeController {
@Autowired
private SentinelApiClient sentinelApiClient;

@Autowired
private AuthService<HttpServletRequest> authService;

@ResponseBody
@RequestMapping("/rules.json")
public Result<List<DegradeRuleEntity>> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(app, PrivilegeType.READ_RULE);
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<DegradeRuleEntity>> queryMachineRules(String app, String ip, Integer port) {

if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app can't be null or empty");
Expand All @@ -84,12 +77,9 @@ public Result<List<DegradeRuleEntity>> queryMachineRules(HttpServletRequest requ

@ResponseBody
@RequestMapping("/new.json")
public Result<DegradeRuleEntity> add(HttpServletRequest request,
String app, String ip, Integer port, String limitApp, String resource,
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> add(String app, String ip, Integer port, String limitApp, String resource,
Double count, Integer timeWindow, Integer grade) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(app, PrivilegeType.WRITE_RULE);

if (StringUtil.isBlank(app)) {
return Result.ofFail(-1, "app can't be null or empty");
}
Expand Down Expand Up @@ -143,10 +133,9 @@ public Result<DegradeRuleEntity> add(HttpServletRequest request,

@ResponseBody
@RequestMapping("/save.json")
public Result<DegradeRuleEntity> updateIfNotNull(HttpServletRequest request,
Long id, String app, String limitApp, String resource,
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> updateIfNotNull(Long id, String app, String limitApp, String resource,
Double count, Integer timeWindow, Integer grade) {
AuthUser authUser = authService.getAuthUser(request);
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
Expand All @@ -159,7 +148,7 @@ public Result<DegradeRuleEntity> updateIfNotNull(HttpServletRequest request,
if (entity == null) {
return Result.ofFail(-1, "id " + id + " dose not exist");
}
authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);

if (StringUtil.isNotBlank(app)) {
entity.setApp(app.trim());
}
Expand Down Expand Up @@ -195,8 +184,8 @@ public Result<DegradeRuleEntity> updateIfNotNull(HttpServletRequest request,

@ResponseBody
@RequestMapping("/delete.json")
public Result<Long> delete(HttpServletRequest request, Long id) {
AuthUser authUser = authService.getAuthUser(request);
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> delete(Long id) {
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
Expand All @@ -205,7 +194,7 @@ public Result<Long> delete(HttpServletRequest request, Long id) {
if (oldEntity == null) {
return Result.ofSuccess(null);
}
authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);

try {
repository.delete(id);
} catch (Throwable throwable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.util.StringUtil;

Expand Down Expand Up @@ -57,19 +54,15 @@ public class FlowControllerV1 {

@Autowired
private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
@Autowired
private AuthService<HttpServletRequest> authService;

@Autowired
private SentinelApiClient sentinelApiClient;

@GetMapping("/rules")
public Result<List<FlowRuleEntity>> apiQueryMachineRules(HttpServletRequest request,
@RequestParam String app,
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(app, PrivilegeType.READ_RULE);

if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app can't be null or empty");
Expand Down Expand Up @@ -138,10 +131,8 @@ private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {
}

@PostMapping("/rule")
public Result<FlowRuleEntity> apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);

@AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity) {
Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
Expand All @@ -165,14 +156,12 @@ public Result<FlowRuleEntity> apiAddFlowRule(HttpServletRequest request, @Reques
}

@PutMapping("/save.json")
public Result<FlowRuleEntity> updateIfNotNull(HttpServletRequest request, Long id, String app,
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> updateIfNotNull(Long id, String app,
String limitApp, String resource, Integer grade,
Double count, Integer strategy, String refResource,
Integer controlBehavior, Integer warmUpPeriodSec,
Integer maxQueueingTimeMs) {
AuthUser authUser = authService.getAuthUser(request);
authUser.authTarget(app, PrivilegeType.WRITE_RULE);

if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
Expand Down Expand Up @@ -246,16 +235,17 @@ public Result<FlowRuleEntity> updateIfNotNull(HttpServletRequest request, Long i
}

@DeleteMapping("/delete.json")
public Result<Long> delete(HttpServletRequest request, Long id) {
AuthUser authUser = authService.getAuthUser(request);
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<Long> delete(Long id) {

if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
FlowRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}
authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);

try {
repository.delete(id);
} catch (Exception e) {
Expand Down
Loading

0 comments on commit d2d27e6

Please sign in to comment.