This is an automated email from the ASF dual-hosted git repository.

benjobs pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampark.git


The following commit(s) were added to refs/heads/dev by this push:
     new a9e778b80 [Improve] Add more constraints the user permission (#2634)
a9e778b80 is described below

commit a9e778b80b1d7e757da11530061b399723d3a012
Author: zhoulii <[email protected]>
AuthorDate: Sun Apr 16 17:54:25 2023 +0800

    [Improve] Add more constraints the user permission (#2634)
    
    Co-authored-by: zhoulii <[email protected]>
---
 .../console/core/annotation/CheckApp.java          |  29 ++++++
 .../console/core/annotation/CheckTeam.java         |  29 ++++++
 .../console/core/annotation/CheckUser.java         |  29 ++++++
 .../console/core/aspect/StreamParkAspect.java      | 108 +++++++++++++++++++++
 .../ApplicationBuildPipelineController.java        |   2 +
 .../core/controller/ApplicationController.java     |  13 +++
 .../system/controller/MemberController.java        |   4 +
 .../console/system/controller/UserController.java  |   2 +
 8 files changed, 216 insertions(+)

diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckApp.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckApp.java
new file mode 100644
index 000000000..ae683b6aa
--- /dev/null
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckApp.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.streampark.console.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CheckApp {
+  String value() default "";
+}
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckTeam.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckTeam.java
new file mode 100644
index 000000000..7c69b6046
--- /dev/null
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckTeam.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.streampark.console.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CheckTeam {
+  String value() default "";
+}
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckUser.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckUser.java
new file mode 100644
index 000000000..b3dd6dce7
--- /dev/null
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/annotation/CheckUser.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.streampark.console.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CheckUser {
+  String value() default "";
+}
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/aspect/StreamParkAspect.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/aspect/StreamParkAspect.java
index d6dbc5676..ce06b9009 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/aspect/StreamParkAspect.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/aspect/StreamParkAspect.java
@@ -22,9 +22,19 @@ package org.apache.streampark.console.core.aspect;
 import org.apache.streampark.console.base.domain.RestResponse;
 import org.apache.streampark.console.base.exception.ApiAlertException;
 import org.apache.streampark.console.core.annotation.ApiAccess;
+import org.apache.streampark.console.core.annotation.CheckApp;
+import org.apache.streampark.console.core.annotation.CheckTeam;
+import org.apache.streampark.console.core.annotation.CheckUser;
+import org.apache.streampark.console.core.entity.Application;
+import org.apache.streampark.console.core.enums.UserType;
+import org.apache.streampark.console.core.service.ApplicationService;
+import org.apache.streampark.console.core.service.CommonService;
 import org.apache.streampark.console.core.task.FlinkRESTAPIWatcher;
 import org.apache.streampark.console.system.entity.AccessToken;
+import org.apache.streampark.console.system.entity.User;
+import org.apache.streampark.console.system.service.MemberService;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.SecurityUtils;
 
 import lombok.extern.slf4j.Slf4j;
@@ -34,6 +44,11 @@ import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Pointcut;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
 import org.springframework.stereotype.Component;
 
 import java.util.Objects;
@@ -44,6 +59,9 @@ import java.util.Objects;
 public class StreamParkAspect {
 
   @Autowired private FlinkRESTAPIWatcher flinkRESTAPIWatcher;
+  @Autowired private CommonService commonService;
+  @Autowired private MemberService memberService;
+  @Autowired private ApplicationService applicationService;
 
   @Pointcut(
       "execution(public"
@@ -78,4 +96,94 @@ public class StreamParkAspect {
     flinkRESTAPIWatcher.init();
     return target;
   }
+
+  
@Pointcut("@annotation(org.apache.streampark.console.core.annotation.CheckUser)")
+  public void checkUser() {}
+
+  @Around("checkUser()")
+  public RestResponse checkUser(ProceedingJoinPoint joinPoint) throws 
Throwable {
+    MethodSignature methodSignature = (MethodSignature) 
joinPoint.getSignature();
+    CheckUser checkUser = 
methodSignature.getMethod().getAnnotation(CheckUser.class);
+    String spELString = checkUser.value();
+
+    Long paramUserId = getId(joinPoint, methodSignature, spELString);
+    User currentUser = commonService.getCurrentUser();
+    if (currentUser == null
+        || (currentUser.getUserType() != UserType.ADMIN
+            && !currentUser.getUserId().equals(paramUserId))) {
+      throw new ApiAlertException(
+          "Permission denied, only ADMIN user or user himself can access this 
permission");
+    }
+
+    return (RestResponse) joinPoint.proceed();
+  }
+
+  
@Pointcut("@annotation(org.apache.streampark.console.core.annotation.CheckTeam)")
+  public void checkTeam() {}
+
+  @Around("checkTeam()")
+  public RestResponse checkTeam(ProceedingJoinPoint joinPoint) throws 
Throwable {
+    MethodSignature methodSignature = (MethodSignature) 
joinPoint.getSignature();
+    CheckTeam checkTeam = 
methodSignature.getMethod().getAnnotation(CheckTeam.class);
+    String spELString = checkTeam.value();
+
+    Long paramTeamId = getId(joinPoint, methodSignature, spELString);
+    User currentUser = commonService.getCurrentUser();
+    if (currentUser == null
+        || (currentUser.getUserType() != UserType.ADMIN
+            && memberService.findByUserName(paramTeamId, 
currentUser.getUsername()) == null)) {
+      throw new ApiAlertException(
+          "Permission denied, only ADMIN user or user belongs to this team can 
access this permission");
+    }
+
+    return (RestResponse) joinPoint.proceed();
+  }
+
+  
@Pointcut("@annotation(org.apache.streampark.console.core.annotation.CheckApp)")
+  public void checkApp() {}
+
+  @Around("checkApp()")
+  public RestResponse checkApp(ProceedingJoinPoint joinPoint) throws Throwable 
{
+    MethodSignature methodSignature = (MethodSignature) 
joinPoint.getSignature();
+    CheckApp checkApp = 
methodSignature.getMethod().getAnnotation(CheckApp.class);
+    String spELString = checkApp.value();
+
+    Long paramAppId = getId(joinPoint, methodSignature, spELString);
+    Application app = applicationService.getById(paramAppId);
+    User currentUser = commonService.getCurrentUser();
+    if (currentUser == null
+        || (app != null
+            && currentUser.getUserType() != UserType.ADMIN
+            && memberService.findByUserName(app.getTeamId(), 
currentUser.getUsername()) == null)) {
+      throw new ApiAlertException(
+          "Permission denied, only ADMIN user or user belongs to this team can 
access this permission");
+    }
+
+    return (RestResponse) joinPoint.proceed();
+  }
+
+  private Long getId(
+      ProceedingJoinPoint joinPoint, MethodSignature methodSignature, String 
spELString) {
+    SpelExpressionParser parser = new SpelExpressionParser();
+    Expression expression = parser.parseExpression(spELString);
+    EvaluationContext context = new StandardEvaluationContext();
+    Object[] args = joinPoint.getArgs();
+    DefaultParameterNameDiscoverer discoverer = new 
DefaultParameterNameDiscoverer();
+    String[] parameterNames = 
discoverer.getParameterNames(methodSignature.getMethod());
+    for (int i = 0; i < parameterNames.length; i++) {
+      context.setVariable(parameterNames[i], args[i]);
+    }
+    Object value = expression.getValue(context);
+
+    if (value == null || StringUtils.isBlank(value.toString())) {
+      return null;
+    }
+
+    try {
+      return Long.parseLong(value.toString());
+    } catch (NumberFormatException e) {
+      throw new ApiAlertException(
+          "Wrong use of annotation on method " + methodSignature.getName(), e);
+    }
+  }
 }
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationBuildPipelineController.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationBuildPipelineController.java
index 69ca3b28a..1d4927b61 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationBuildPipelineController.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationBuildPipelineController.java
@@ -21,6 +21,7 @@ import 
org.apache.streampark.console.base.domain.ApiDocConstant;
 import org.apache.streampark.console.base.domain.RestResponse;
 import org.apache.streampark.console.base.exception.ApiAlertException;
 import org.apache.streampark.console.core.annotation.ApiAccess;
+import org.apache.streampark.console.core.annotation.CheckApp;
 import org.apache.streampark.console.core.bean.AppBuildDockerResolvedDetail;
 import org.apache.streampark.console.core.entity.AppBuildPipeline;
 import org.apache.streampark.console.core.entity.Application;
@@ -90,6 +91,7 @@ public class ApplicationBuildPipelineController {
         schema = @Schema(defaultValue = "false", implementation = 
boolean.class))
   })
   @ApiAccess
+  @CheckApp("#appId")
   @PostMapping(value = "build")
   @RequiresPermissions("app:create")
   public RestResponse buildApplication(Long appId, boolean forceBuild) {
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationController.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationController.java
index 73ca135d1..0639fb392 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationController.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ApplicationController.java
@@ -25,6 +25,8 @@ import org.apache.streampark.console.base.domain.RestResponse;
 import org.apache.streampark.console.base.exception.InternalException;
 import org.apache.streampark.console.core.annotation.ApiAccess;
 import org.apache.streampark.console.core.annotation.AppUpdated;
+import org.apache.streampark.console.core.annotation.CheckApp;
+import org.apache.streampark.console.core.annotation.CheckTeam;
 import org.apache.streampark.console.core.bean.AppControl;
 import org.apache.streampark.console.core.entity.Application;
 import org.apache.streampark.console.core.entity.ApplicationBackUp;
@@ -89,6 +91,7 @@ public class ApplicationController {
 
   @Operation(summary = "Create application")
   @ApiAccess
+  @CheckTeam("#app.teamId")
   @PostMapping("create")
   @RequiresPermissions("app:create")
   public RestResponse create(Application app) throws IOException {
@@ -114,6 +117,7 @@ public class ApplicationController {
     @Parameter(name = "args", description = "new application args", in = 
ParameterIn.QUERY)
   })
   @ApiAccess
+  @CheckApp("#app.id")
   @PostMapping(value = "copy")
   @RequiresPermissions("app:copy")
   public RestResponse copy(@Parameter(hidden = true) Application app) throws 
IOException {
@@ -127,6 +131,7 @@ public class ApplicationController {
 
   @Operation(summary = "Update application")
   @AppUpdated
+  @CheckApp("#app.id")
   @PostMapping("update")
   @RequiresPermissions("app:update")
   public RestResponse update(Application app) {
@@ -189,6 +194,7 @@ public class ApplicationController {
 
   @Operation(summary = "Revoke application")
   @AppUpdated
+  @CheckApp("#app.id")
   @PostMapping("revoke")
   @RequiresPermissions("app:release")
   public RestResponse revoke(Application app) {
@@ -227,6 +233,7 @@ public class ApplicationController {
         schema = @Schema(implementation = boolean.class, defaultValue = 
"false"))
   })
   @ApiAccess
+  @CheckApp("#app.id")
   @PostMapping(value = "start")
   @RequiresPermissions("app:start")
   public RestResponse start(@Parameter(hidden = true) Application app) {
@@ -271,6 +278,7 @@ public class ApplicationController {
         example = "false",
         schema = @Schema(implementation = boolean.class, defaultValue = 
"false"))
   })
+  @CheckApp("#app.id")
   @PostMapping(value = "cancel")
   @RequiresPermissions("app:cancel")
   public RestResponse cancel(@Parameter(hidden = true) Application app) throws 
Exception {
@@ -281,6 +289,7 @@ public class ApplicationController {
   @Operation(summary = "Clean application")
   @AppUpdated
   @ApiAccess
+  @CheckApp("#app.id")
   @PostMapping("clean")
   @RequiresPermissions("app:clean")
   public RestResponse clean(Application app) {
@@ -290,6 +299,7 @@ public class ApplicationController {
 
   /** force stop(stop normal start or in progress) */
   @Operation(summary = "Force stop application")
+  @CheckApp("#app.id")
   @PostMapping("forcedStop")
   @RequiresPermissions("app:cancel")
   public RestResponse forcedStop(Application app) {
@@ -346,6 +356,7 @@ public class ApplicationController {
   }
 
   @Operation(summary = "Delete application operation log")
+  @CheckApp("#applicationLog.appId")
   @PostMapping("deleteOperationLog")
   @RequiresPermissions("app:delete")
   public RestResponse deleteOperationLog(ApplicationLog applicationLog) {
@@ -354,6 +365,7 @@ public class ApplicationController {
   }
 
   @Operation(summary = "Delete application")
+  @CheckApp("#app.id")
   @PostMapping("delete")
   @RequiresPermissions("app:delete")
   public RestResponse delete(Application app) throws InternalException {
@@ -362,6 +374,7 @@ public class ApplicationController {
   }
 
   @Operation(summary = "Backup application when deleted")
+  @CheckApp("#backUp.appId")
   @PostMapping("deletebak")
   public RestResponse deleteBak(ApplicationBackUp backUp) throws 
InternalException {
     Boolean deleted = backUpService.delete(backUp.getId());
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/MemberController.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/MemberController.java
index c92df03f8..462c38431 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/MemberController.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/MemberController.java
@@ -19,6 +19,7 @@ package org.apache.streampark.console.system.controller;
 
 import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.base.domain.RestResponse;
+import org.apache.streampark.console.core.annotation.CheckTeam;
 import org.apache.streampark.console.system.entity.Member;
 import org.apache.streampark.console.system.entity.Team;
 import org.apache.streampark.console.system.entity.User;
@@ -81,6 +82,7 @@ public class MemberController {
   }
 
   @Operation(summary = "Create member")
+  @CheckTeam("#member.teamId")
   @PostMapping("post")
   @RequiresPermissions("member:add")
   public RestResponse create(@Valid Member member) {
@@ -89,6 +91,7 @@ public class MemberController {
   }
 
   @Operation(summary = "Delete member")
+  @CheckTeam("#member.teamId")
   @DeleteMapping("delete")
   @RequiresPermissions("member:delete")
   public RestResponse delete(Member member) {
@@ -97,6 +100,7 @@ public class MemberController {
   }
 
   @Operation(summary = "Update member")
+  @CheckTeam("#member.teamId")
   @PutMapping("update")
   @RequiresPermissions("member:update")
   public RestResponse update(Member member) {
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/UserController.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/UserController.java
index 202c513b7..0985c3d0f 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/UserController.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/system/controller/UserController.java
@@ -21,6 +21,7 @@ import org.apache.streampark.console.base.domain.ResponseCode;
 import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.base.domain.RestResponse;
 import org.apache.streampark.console.base.exception.ApiAlertException;
+import org.apache.streampark.console.core.annotation.CheckUser;
 import org.apache.streampark.console.core.enums.LoginType;
 import org.apache.streampark.console.core.enums.UserType;
 import org.apache.streampark.console.core.service.CommonService;
@@ -114,6 +115,7 @@ public class UserController {
   }
 
   @Operation(summary = "Update password")
+  @CheckUser("#user.userId")
   @PutMapping("password")
   public RestResponse updatePassword(User user) throws Exception {
     userService.updatePassword(user);

Reply via email to