From b615edcf09fa6ffc5fe1818467868805b6e6f8f2 Mon Sep 17 00:00:00 2001 From: Sun Jian <921465802@qq.com> Date: Fri, 18 Feb 2022 19:30:24 +0800 Subject: [PATCH] support workflow operation permission --- sqle/api/controller/v1/task.go | 14 ++- sqle/api/controller/v1/workflow.go | 40 +++++--- sqle/errors/errors.go | 6 ++ sqle/model/instance.go | 143 +++++++++++++++++++++++++++++ sqle/model/workflow.go | 18 ++++ sqle/model/workflow_list.go | 25 ++++- 6 files changed, 228 insertions(+), 18 deletions(-) diff --git a/sqle/api/controller/v1/task.go b/sqle/api/controller/v1/task.go index 423009a4a3..380ad1cc55 100644 --- a/sqle/api/controller/v1/task.go +++ b/sqle/api/controller/v1/task.go @@ -216,10 +216,18 @@ func checkCurrentUserCanAccessTask(c echo.Context, task *model.Task) error { if err != nil { return err } - if !access { - return ErrTaskNoAccess + if access { + return nil + } + + ok, err := s.CheckUserHasOpToInstance(user, task.Instance, []uint{model.OP_WORKFLOW_VIEW_OTHERS}) + if err != nil { + return err + } + if ok { + return nil } - return nil + return ErrTaskNoAccess } // @Summary 获取Sql审核任务信息 diff --git a/sqle/api/controller/v1/workflow.go b/sqle/api/controller/v1/workflow.go index dc80afd71b..7978cf2630 100644 --- a/sqle/api/controller/v1/workflow.go +++ b/sqle/api/controller/v1/workflow.go @@ -2,11 +2,12 @@ package v1 import ( "fmt" - "github.com/actiontech/sqle/sqle/driver" "net/http" "strconv" "time" + "github.com/actiontech/sqle/sqle/driver" + "github.com/actiontech/sqle/sqle/api/controller" "github.com/actiontech/sqle/sqle/errors" "github.com/actiontech/sqle/sqle/log" @@ -609,7 +610,7 @@ type WorkflowStepResV1 struct { Reason string `json:"reason,omitempty"` } -func checkCurrentUserCanAccessWorkflow(c echo.Context, workflow *model.Workflow) error { +func checkCurrentUserCanAccessWorkflow(c echo.Context, workflow *model.Workflow, ops []uint) error { if controller.GetUserName(c) == model.DefaultAdminUser { return nil } @@ -622,10 +623,23 @@ func checkCurrentUserCanAccessWorkflow(c echo.Context, workflow *model.Workflow) if err != nil { return err } - if !access { - return ErrWorkflowNoAccess + if access { + return nil } - return nil + if len(ops) > 0 { + instance, err := s.GetInstanceByWorkflowID(workflow.ID) + if err != nil { + return err + } + ok, err := s.CheckUserHasOpToInstance(user, instance, ops) + if err != nil { + return err + } + if ok { + return nil + } + } + return ErrWorkflowNoAccess } func convertWorkflowToRes(workflow *model.Workflow, task *model.Task) *WorkflowResV1 { @@ -763,7 +777,7 @@ func GetWorkflow(c echo.Context) error { } err = checkCurrentUserCanAccessWorkflow(c, &model.Workflow{ Model: model.Model{ID: uint(id)}, - }) + }, []uint{model.OP_WORKFLOW_VIEW_OTHERS}) if err != nil { return controller.JSONBaseErrorReq(c, err) } @@ -904,7 +918,7 @@ func GetWorkflows(c echo.Context) error { "offset": offset, } s := model.GetStorage() - workflows, count, err := s.GetWorkflowsByReq(data) + workflows, count, err := s.GetWorkflowsByReq(data, user) if err != nil { return controller.JSONBaseErrorReq(c, err) } @@ -970,7 +984,7 @@ func ApproveWorkflow(c echo.Context) error { } err = checkCurrentUserCanAccessWorkflow(c, &model.Workflow{ Model: model.Model{ID: uint(id)}, - }) + }, []uint{}) if err != nil { return controller.JSONBaseErrorReq(c, err) } @@ -1052,7 +1066,7 @@ func RejectWorkflow(c echo.Context) error { } err = checkCurrentUserCanAccessWorkflow(c, &model.Workflow{ Model: model.Model{ID: uint(id)}, - }) + }, []uint{}) if err != nil { return controller.JSONBaseErrorReq(c, err) } @@ -1116,7 +1130,7 @@ func CancelWorkflow(c echo.Context) error { } err = checkCurrentUserCanAccessWorkflow(c, &model.Workflow{ Model: model.Model{ID: uint(id)}, - }) + }, []uint{}) if err != nil { return controller.JSONBaseErrorReq(c, err) } @@ -1227,7 +1241,7 @@ func UpdateWorkflow(c echo.Context) error { } err = checkCurrentUserCanAccessWorkflow(c, &model.Workflow{ Model: model.Model{ID: uint(id)}, - }) + }, []uint{}) if err != nil { return controller.JSONBaseErrorReq(c, err) } @@ -1354,7 +1368,7 @@ func UpdateWorkflowSchedule(c echo.Context) error { } err = checkCurrentUserCanAccessWorkflow(c, &model.Workflow{ Model: model.Model{ID: uint(id)}, - }) + }, []uint{}) if err != nil { return controller.JSONBaseErrorReq(c, err) } @@ -1415,7 +1429,7 @@ func ExecuteTaskOnWorkflow(c echo.Context) error { } err = checkCurrentUserCanAccessWorkflow(c, &model.Workflow{ Model: model.Model{ID: uint(id)}, - }) + }, []uint{}) if err != nil { return controller.JSONBaseErrorReq(c, err) } diff --git a/sqle/errors/errors.go b/sqle/errors/errors.go index 91a8280376..bacb368de8 100644 --- a/sqle/errors/errors.go +++ b/sqle/errors/errors.go @@ -12,6 +12,8 @@ const ( HttpRequestFormatError ErrorCode = 2001 + ErrAccessDeniedError ErrorCode = 3001 + LoginAuthFail ErrorCode = 4001 UserDisabled ErrorCode = 4005 TaskNotExist ErrorCode = 4006 @@ -94,3 +96,7 @@ func HttpRequestFormatErrWrapper(err error) error { func ConnectStorageErrWrapper(err error) error { return New(ConnectStorageError, err) } + +func NewAccessDeniedErr(format string, a ...interface{}) error { + return New(ErrAccessDeniedError, fmt.Errorf(format, a...)) +} diff --git a/sqle/model/instance.go b/sqle/model/instance.go index 5368102622..dda3db95b0 100644 --- a/sqle/model/instance.go +++ b/sqle/model/instance.go @@ -183,3 +183,146 @@ func (s *Storage) GetInstanceNamesByWorkflowTemplateId(id uint) ([]string, error } return names, nil } + +func (s *Storage) CheckUserHasOpToInstance(user *User, instance *Instance, ops []uint) (bool, error) { + query := ` +SELECT instances.id +FROM instances +LEFT JOIN instance_role ON instance_role.instance_id = instances.id +LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +LEFT JOIN role_operations ON role_operations.role_id = roles.id +LEFT JOIN user_role ON user_role.role_id = roles.id +LEFT JOIN users ON users.id = user_role.user_id AND users.stat = 0 +WHERE +instances.deleted_at IS NULL +AND instances.id = ? +AND users.id = ? +AND role_operations.op_code IN (?) +GROUP BY instances.id +UNION +SELECT instances.id +FROM instances +LEFT JOIN instance_role ON instance_role.instance_id = instances.id +LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +LEFT JOIN role_operations ON role_operations.role_id = roles.id +JOIN user_group_roles ON roles.id = user_group_roles.role_id +JOIN user_groups ON user_groups.id = user_group_roles.user_group_id AND user_groups.deleted_at IS NULL +JOIN user_group_users ON user_groups.id = user_group_users.user_group_id +JOIN users ON users.id = user_group_users.user_id AND users.deleted_at IS NULL AND users.stat=0 +WHERE +instances.deleted_at IS NULL +AND instances.id = ? +AND users.id = ? +AND role_operations.op_code IN (?) +GROUP BY instances.id +` + var instances []*Instance + err := s.db.Raw(query, instance.ID, user.ID, ops, instance.ID, user.ID, ops).Scan(&instances).Error + if err != nil { + return false, errors.ConnectStorageErrWrapper(err) + } + return len(instances) > 0, nil +} + +func (s *Storage) GetUserCanOpInstances(user *User, ops []uint) (instances []*Instance, err error) { + query := ` +SELECT instances.id +FROM instances +LEFT JOIN instance_role ON instance_role.instance_id = instances.id +LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +LEFT JOIN role_operations ON role_operations.role_id = roles.id +LEFT JOIN user_role ON user_role.role_id = roles.id +LEFT JOIN users ON users.id = user_role.user_id AND users.stat = 0 +WHERE +instances.deleted_at IS NULL +AND users.id = ? +AND role_operations.op_code IN (?) +GROUP BY instances.id +UNION +SELECT instances.id +FROM instances +LEFT JOIN instance_role ON instance_role.instance_id = instances.id +LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +LEFT JOIN role_operations ON role_operations.role_id = roles.id +JOIN user_group_roles ON roles.id = user_group_roles.role_id +JOIN user_groups ON user_groups.id = user_group_roles.user_group_id AND user_groups.deleted_at IS NULL +JOIN user_group_users ON user_groups.id = user_group_users.user_group_id +JOIN users ON users.id = user_group_users.user_id AND users.deleted_at IS NULL AND users.stat=0 +WHERE +instances.deleted_at IS NULL +AND users.id = ? +AND role_operations.op_code IN (?) +GROUP BY instances.id +` + err = s.db.Raw(query, user.ID, ops, user.ID, ops).Scan(&instances).Error + if err != nil { + return nil, errors.ConnectStorageErrWrapper(err) + } + return +} + +func getInstanceIDsFromInstances(instances []*Instance) (ids []uint) { + ids = make([]uint, len(instances)) + for i := range instances { + ids[i] = instances[i].ID + } + return ids +} + +//SELECT instances.id +//FROM instances +//LEFT JOIN instance_role ON instance_role.instance_id = instances.id +//LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +//LEFT JOIN role_operations ON role_operations.role_id = roles.id +//LEFT JOIN user_role ON user_role.role_id = roles.id +//LEFT JOIN users ON users.id = user_role.user_id AND users.stat = 0 +//WHERE +//instances.deleted_at IS NULL +//AND users.id = 5 +//AND role_operations.op_code IN (20200) +//GROUP BY instances.id +//UNION +//SELECT instances.id +//FROM instances +//LEFT JOIN instance_role ON instance_role.instance_id = instances.id +//LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +//LEFT JOIN role_operations ON role_operations.role_id = roles.id +//JOIN user_group_roles ON roles.id = user_group_roles.role_id +//JOIN user_groups ON user_groups.id = user_group_roles.user_group_id AND user_groups.deleted_at IS NULL +//JOIN user_group_users ON user_groups.id = user_group_users.user_group_id +//JOIN users ON users.id = user_group_users.user_id AND users.deleted_at IS NULL AND users.stat=0 +//WHERE +//instances.deleted_at IS NULL +//AND users.id = 5 +//AND role_operations.op_code IN (20200) +//GROUP BY instances.id + +//SELECT instances.id +//FROM instances +//LEFT JOIN instance_role ON instance_role.instance_id = instances.id +//LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +//LEFT JOIN role_operations ON role_operations.role_id = roles.id +//LEFT JOIN user_role ON user_role.role_id = roles.id +//LEFT JOIN users ON users.id = user_role.user_id AND users.stat = 0 +//WHERE +//instances.deleted_at IS NULL +//AND instances.id = 5 +//AND users.id = 4 +//AND role_operations.op_code IN (20200) +//GROUP BY instances.id +//UNION +//SELECT instances.id +//FROM instances +//LEFT JOIN instance_role ON instance_role.instance_id = instances.id +//LEFT JOIN roles ON roles.id = instance_role.role_id AND roles.deleted_at IS NULL AND roles.stat = 0 +//LEFT JOIN role_operations ON role_operations.role_id = roles.id +//JOIN user_group_roles ON roles.id = user_group_roles.role_id +//JOIN user_groups ON user_groups.id = user_group_roles.user_group_id AND user_groups.deleted_at IS NULL +//JOIN user_group_users ON user_groups.id = user_group_users.user_group_id +//JOIN users ON users.id = user_group_users.user_id AND users.deleted_at IS NULL AND users.stat=0 +//WHERE +//instances.deleted_at IS NULL +//AND instances.id = 5 +//AND users.id = 4 +//AND role_operations.op_code IN (20200) +//GROUP BY instances.id diff --git a/sqle/model/workflow.go b/sqle/model/workflow.go index 999630615c..7a774e6e40 100644 --- a/sqle/model/workflow.go +++ b/sqle/model/workflow.go @@ -601,3 +601,21 @@ func (s *Storage) TaskWorkflowIsRunning(taskIds []uint) (bool, error) { err := s.db.Where("status = ? AND task_id IN (?)", WorkflowStatusRunning, taskIds).Find(&workflowRecords).Error return len(workflowRecords) > 0, errors.New(errors.ConnectStorageError, err) } + +func (s *Storage) GetInstanceByWorkflowID(workflowID uint) (*Instance, error) { + query := ` +SELECT instances.id +FROM workflows AS w +LEFT JOIN workflow_records AS wr ON wr.id = w.workflow_record_id +LEFT JOIN tasks ON tasks.id = wr.task_id +LEFT JOIN instances ON instances.id = tasks.instance_id +WHERE +w.id = ? +LIMIT 1` + instance := &Instance{} + err := s.db.Raw(query, workflowID).Scan(instance).Error + if err != nil { + return nil, errors.ConnectStorageErrWrapper(err) + } + return instance, err +} diff --git a/sqle/model/workflow_list.go b/sqle/model/workflow_list.go index 11ced51cf1..da0f2f08af 100644 --- a/sqle/model/workflow_list.go +++ b/sqle/model/workflow_list.go @@ -3,6 +3,8 @@ package model import ( "database/sql" "time" + + "github.com/actiontech/sqle/sqle/utils" ) type WorkflowListDetail struct { @@ -68,9 +70,15 @@ WHERE w.deleted_at IS NULL {{- if .check_user_can_access }} -AND (w.create_user_id = :current_user_id +AND ( +w.create_user_id = :current_user_id OR curr_ass_user.id = :current_user_id OR all_ass_user.id = :current_user_id + +{{- if .viewable_instance_ids }} +OR inst.id IN (:viewable_instance_ids) +{{- end }} + ) {{- end }} @@ -121,9 +129,22 @@ AND inst.name = :filter_task_instance_name ` -func (s *Storage) GetWorkflowsByReq(data map[string]interface{}) ( +func (s *Storage) GetWorkflowsByReq(data map[string]interface{}, user *User) ( result []*WorkflowListDetail, count uint64, err error) { + // get workflow ids only for user can access by OP_WORKFLOW_VIEW_OTHERS + var ids []uint + { + instances, err := s.GetUserCanOpInstances(user, []uint{OP_WORKFLOW_VIEW_OTHERS}) + if err != nil { + return result, 0, err + } + ids = getInstanceIDsFromInstances(instances) + } + if len(ids) > 0 { + data["viewable_instance_ids"] = utils.JoinUintSliceToString(ids, ", ") + } + err = s.getListResult(workflowsQueryBodyTpl, workflowsQueryTpl, data, &result) if err != nil { return result, 0, err