+ * This method will use return value to represent whether user has the specific + * privileges to the target, but to throw a RuntimeException to represent no auth + * is also a good way. + *
+ * + * @param target the target to check + * @param privilegeType the privilege type to check + * @return if current user has the specific privileges to the target, return true, + * otherwise return false. + */ + boolean authTarget(String target, PrivilegeType privilegeType); + + /** + * Check whether current user is a super-user. + * + * @return if current user is super user return true, else return false. + */ + boolean isSuperUser(); + + /** + * Get current user's nick name. + * + * @return current user's nick name. + */ + String getNickName(); + + /** + * Get current user's login name. + * + * @return current user's login name. + */ + String getLoginName(); + + /** + * Get current user's ID. + * + * @return ID of current user + */ + String getId(); + } +} diff --git a/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthorizationInterceptor.java b/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthorizationInterceptor.java new file mode 100644 index 000000000..0df32a68a --- /dev/null +++ b/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthorizationInterceptor.java @@ -0,0 +1,29 @@ +/* + * 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 org.springframework.web.servlet.HandlerInterceptor; + +/** + * The web interceptor for privilege-based authorization. + * + * @author lkxiaolou + * @author wxq + * @since 1.7.1 + */ +public interface AuthorizationInterceptor extends HandlerInterceptor { + +} diff --git a/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultAuthorizationInterceptor.java b/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultAuthorizationInterceptor.java new file mode 100644 index 000000000..131e64692 --- /dev/null +++ b/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultAuthorizationInterceptor.java @@ -0,0 +1,75 @@ +/* + * 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.web.method.HandlerMethod; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * The web interceptor for privilege-based authorization. + *
+ * move from old {@link AuthorizationInterceptor}.
+ *
+ * @author lkxiaolou
+ * @author wxq
+ * @since 1.7.1
+ */
+public class DefaultAuthorizationInterceptor implements AuthorizationInterceptor {
+
+ private final AuthService The Servlet filter for authentication. Note: some urls are excluded as they needn't auth, such as:
+ * The excluded urls and urlSuffixes could be configured in {@code application.properties} file.
+ *
+ * @author cdfive
+ * @since 1.6.0
+ */
+public class DefaultLoginAuthenticationFilter implements LoginAuthenticationFilter {
+
+ private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
+
+ private static final String URL_SUFFIX_DOT = ".";
+
+ /**
+ * Some urls which needn't auth, such as /auth/login, /registry/machine and so on.
+ */
+ @Value("#{'${auth.filter.exclude-urls}'.split(',')}")
+ private List The Servlet filter for authentication. Note: some urls are excluded as they needn't auth, such as:
+ * The excluded urls and urlSuffixes could be configured in {@code application.properties} file.
+ *
+ * @author cdfive
+ * @author wxq
+ * @since 1.6.0
+ */
+public interface LoginAuthenticationFilter extends Filter {
+
+}
diff --git a/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java b/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java
new file mode 100644
index 000000000..10c386b0f
--- /dev/null
+++ b/ruoyi-visual/ruoyi-sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java
@@ -0,0 +1,73 @@
+/*
+ * 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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author cdfive
+ * @since 1.6.0
+ */
+public class SimpleWebAuthServiceImpl implements AuthService Dashboard local config support.
+ * Dashboard supports configuration loading by several ways by order:
+ *
+ *
+ *
+ * > fetchItemsAsync(String ip, int port, String api, String type, Class
> fetchParamFlowRulesOfMachine(String app, String ip, int port) {
+ try {
+ AssertUtil.notEmpty(app, "Bad app name");
+ AssertUtil.notEmpty(ip, "Bad machine IP");
+ AssertUtil.isTrue(port > 0, "Bad machine port");
+ return fetchItemsAsync(ip, port, GET_PARAM_RULE_PATH, null, ParamFlowRule.class)
+ .thenApply(rules -> rules.stream()
+ .map(e -> ParamFlowRuleEntity.fromParamFlowRule(app, ip, port, e))
+ .collect(Collectors.toList())
+ );
+ } catch (Exception e) {
+ logger.error("Error when fetching parameter flow rules", e);
+ return AsyncUtils.newFailedFuture(e);
+ }
+ }
+
+ /**
+ * Fetch all authority rules from provided machine.
+ *
+ * @param app application name
+ * @param ip machine client IP
+ * @param port machine client port
+ * @return all retrieved authority rules
+ * @since 0.2.1
+ */
+ public List
> fetchApis(String app, String ip, int port) {
+ if (StringUtil.isBlank(ip) || port <= 0) {
+ return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter"));
+ }
+
+ try {
+ return executeCommand(ip, port, FETCH_GATEWAY_API_PATH, false)
+ .thenApply(r -> {
+ List
> fetchGatewayFlowRules(String app, String ip, int port) {
+ if (StringUtil.isBlank(ip) || port <= 0) {
+ return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter"));
+ }
+
+ try {
+ return executeCommand(ip, port, FETCH_GATEWAY_FLOW_RULE_PATH, false)
+ .thenApply(r -> {
+ List
+ * 1. System.properties
+ * 2. Env
+ * > queryApps(HttpServletRequest request) {
+ return Result.ofSuccess(appManagement.getAppNames());
+ }
+
+ @GetMapping("/briefinfos.json")
+ public Result
> queryAppInfos(HttpServletRequest request) {
+ List
> getMachinesByApp(@PathVariable("app") String app) {
+ AppInfo appInfo = appManagement.getDetailApp(app);
+ if (appInfo == null) {
+ return Result.ofSuccess(null);
+ }
+ List
> apiQueryAllRulesForMachine(@RequestParam String app,
+ @RequestParam String ip,
+ @RequestParam Integer port) {
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app cannot be null or empty");
+ }
+ if (StringUtil.isEmpty(ip)) {
+ return Result.ofFail(-1, "ip cannot be null or empty");
+ }
+ if (port == null || port <= 0) {
+ return Result.ofFail(-1, "Invalid parameter: port");
+ }
+ try {
+ List
> apiQueryMachineRules(String app, String ip, Integer port) {
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app can't be null or empty");
+ }
+ if (StringUtil.isEmpty(ip)) {
+ return Result.ofFail(-1, "ip can't be null or empty");
+ }
+ if (port == null) {
+ return Result.ofFail(-1, "port can't be null");
+ }
+ try {
+ List
> apiQueryMachineRules(@RequestParam String app,
+ @RequestParam String ip,
+ @RequestParam Integer port) {
+
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app can't be null or empty");
+ }
+ if (StringUtil.isEmpty(ip)) {
+ return Result.ofFail(-1, "ip can't be null or empty");
+ }
+ if (port == null) {
+ return Result.ofFail(-1, "port can't be null");
+ }
+ try {
+ List
> apiQueryAllRulesForMachine(@RequestParam String app,
+ @RequestParam String ip,
+ @RequestParam Integer port) {
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app cannot be null or empty");
+ }
+ if (StringUtil.isEmpty(ip)) {
+ return Result.ofFail(-1, "ip cannot be null or empty");
+ }
+ if (port == null || port <= 0) {
+ return Result.ofFail(-1, "Invalid parameter: port");
+ }
+ if (!checkIfSupported(app, ip, port)) {
+ return unsupportedVersion();
+ }
+ try {
+ return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)
+ .thenApply(repository::saveAll)
+ .thenApply(Result::ofSuccess)
+ .get();
+ } catch (ExecutionException ex) {
+ logger.error("Error when querying parameter flow rules", ex.getCause());
+ if (isNotSupported(ex.getCause())) {
+ return unsupportedVersion();
+ } else {
+ return Result.ofThrowable(-1, ex.getCause());
+ }
+ } catch (Throwable throwable) {
+ logger.error("Error when querying parameter flow rules", throwable);
+ return Result.ofFail(-1, throwable.getMessage());
+ }
+ }
+
+ private boolean isNotSupported(Throwable ex) {
+ return ex instanceof CommandNotFoundException;
+ }
+
+ @PostMapping("/rule")
+ @AuthAction(AuthService.PrivilegeType.WRITE_RULE)
+ public Result
> fetchResourceChainListOfMachine(String ip, Integer port, String type,
+ String searchKey) {
+ if (StringUtil.isEmpty(ip) || port == null) {
+ return Result.ofFail(-1, "invalid param, give ip, port");
+ }
+ final String ROOT = "root";
+ final String DEFAULT = "default";
+ if (StringUtil.isEmpty(type)) {
+ type = ROOT;
+ }
+ if (ROOT.equalsIgnoreCase(type) || DEFAULT.equalsIgnoreCase(type)) {
+ List
> apiQueryMachineRules(String app, String ip,
+ Integer port) {
+ Result
> checkResult = checkBasicParams(app, ip, port);
+ if (checkResult != null) {
+ return checkResult;
+ }
+ try {
+ List
> apiGetClusterServerStateOfApp(@PathVariable String app) {
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app cannot be null or empty");
+ }
+ try {
+ return clusterConfigService.getClusterUniversalState(app)
+ .thenApply(ClusterEntityUtils::wrapToAppClusterServerState)
+ .thenApply(Result::ofSuccess)
+ .get();
+ } catch (ExecutionException ex) {
+ logger.error("Error when fetching cluster server state of app: " + app, ex.getCause());
+ return errorResponse(ex);
+ } catch (Throwable throwable) {
+ logger.error("Error when fetching cluster server state of app: " + app, throwable);
+ return Result.ofFail(-1, throwable.getMessage());
+ }
+ }
+
+ @GetMapping("/client_state/{app}")
+ public Result
> apiGetClusterClientStateOfApp(@PathVariable String app) {
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app cannot be null or empty");
+ }
+ try {
+ return clusterConfigService.getClusterUniversalState(app)
+ .thenApply(ClusterEntityUtils::wrapToAppClusterClientState)
+ .thenApply(Result::ofSuccess)
+ .get();
+ } catch (ExecutionException ex) {
+ logger.error("Error when fetching cluster token client state of app: " + app, ex.getCause());
+ return errorResponse(ex);
+ } catch (Throwable throwable) {
+ logger.error("Error when fetching cluster token client state of app: " + app, throwable);
+ return Result.ofFail(-1, throwable.getMessage());
+ }
+ }
+
+ @GetMapping("/state/{app}")
+ public Result
> apiGetClusterStateOfApp(@PathVariable String app) {
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app cannot be null or empty");
+ }
+ try {
+ return clusterConfigService.getClusterUniversalState(app)
+ .thenApply(Result::ofSuccess)
+ .get();
+ } catch (ExecutionException ex) {
+ logger.error("Error when fetching cluster state of app: " + app, ex.getCause());
+ return errorResponse(ex);
+ } catch (Throwable throwable) {
+ logger.error("Error when fetching cluster state of app: " + app, throwable);
+ return Result.ofFail(-1, throwable.getMessage());
+ }
+ }
+
+ private boolean isNotSupported(Throwable ex) {
+ return ex instanceof CommandNotFoundException;
+ }
+
+ private boolean checkIfSupported(String app, String ip, int port) {
+ try {
+ return Optional.ofNullable(appManagement.getDetailApp(app))
+ .flatMap(e -> e.getMachine(ip, port))
+ .flatMap(m -> VersionUtils.parseVersion(m.getVersion())
+ .map(v -> v.greaterOrEqual(version140)))
+ .orElse(true);
+ // If error occurred or cannot retrieve machine info, return true.
+ } catch (Exception ex) {
+ return true;
+ }
+ }
+
+ private Result
> queryApis(String app, String ip, Integer port) {
+
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app can't be null or empty");
+ }
+ if (StringUtil.isEmpty(ip)) {
+ return Result.ofFail(-1, "ip can't be null or empty");
+ }
+ if (port == null) {
+ return Result.ofFail(-1, "port can't be null");
+ }
+
+ try {
+ List
> queryFlowRules(String app, String ip, Integer port) {
+
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app can't be null or empty");
+ }
+ if (StringUtil.isEmpty(ip)) {
+ return Result.ofFail(-1, "ip can't be null or empty");
+ }
+ if (port == null) {
+ return Result.ofFail(-1, "port can't be null");
+ }
+
+ try {
+ List
> ruleProvider;
+ @Autowired
+ @Qualifier("flowRuleDefaultPublisher")
+ private DynamicRulePublisher
> rulePublisher;
+
+ @GetMapping("/rules")
+ @AuthAction(PrivilegeType.READ_RULE)
+ public Result
> apiQueryMachineRules(@RequestParam String app) {
+
+ if (StringUtil.isEmpty(app)) {
+ return Result.ofFail(-1, "app can't be null or empty");
+ }
+ try {
+ List