Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the ACL checking mechanism of Sentinel dashboard #1042

Merged
merged 5 commits into from
Dec 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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