Skip to content

Commit

Permalink
refactor(be): create middleware to check permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
fiftin committed Jul 8, 2023
1 parent d7a575a commit 87d9835
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 36 deletions.
58 changes: 32 additions & 26 deletions api/projects/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package projects
import (
"github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/db"
"github.com/gorilla/mux"
"net/http"

"github.com/gorilla/context"
Expand All @@ -22,7 +23,7 @@ func ProjectMiddleware(next http.Handler) http.Handler {
return
}

// check if user it project's team
// check if user in project's team
_, err = helpers.Store(r).GetProjectUser(projectID, user.ID)

if err != nil {
Expand All @@ -42,31 +43,36 @@ func ProjectMiddleware(next http.Handler) http.Handler {
})
}

// MustBeAdmin ensures that the user has administrator rights
func MustBeAdmin(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
project := context.Get(r, "project").(db.Project)
user := context.Get(r, "user").(*db.User)

projectUser, err := helpers.Store(r).GetProjectUser(project.ID, user.ID)

if err == db.ErrNotFound {
w.WriteHeader(http.StatusForbidden)
return
}

if err != nil {
helpers.WriteError(w, err)
return
}

if projectUser.Role != db.ProjectOwner {
w.WriteHeader(http.StatusForbidden)
return
}

next.ServeHTTP(w, r)
})
// GetMustCanMiddlewareFor ensures that the user has administrator rights
func GetMustCanMiddlewareFor(permissions db.ProjectUserPermission) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
project := context.Get(r, "project").(db.Project)
user := context.Get(r, "user").(*db.User)

if !user.Admin {
// check if user in project's team
projectUser, err := helpers.Store(r).GetProjectUser(project.ID, user.ID)

if err == db.ErrNotFound {
w.WriteHeader(http.StatusForbidden)
return
}

if err != nil {
helpers.WriteError(w, err)
return
}

if r.Method != "GET" && r.Method != "HEAD" && !projectUser.Can(permissions) {
w.WriteHeader(http.StatusForbidden)
return
}
}

next.ServeHTTP(w, r)
})
}
}

// GetProject returns a project details
Expand Down
26 changes: 21 additions & 5 deletions api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,20 @@ func Route() *mux.Router {
projectGet.Use(projects.ProjectMiddleware)
projectGet.Methods("GET", "HEAD").HandlerFunc(projects.GetProject)

//
// Start and Stop tasks
projectTaskStart := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
projectTaskStart.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanRunProjectTasks))
projectTaskStart.Path("/tasks").HandlerFunc(projects.AddTask).Methods("POST")

projectTaskStop := authenticatedAPI.PathPrefix("/tasks").Subrouter()
projectTaskStop.Use(projects.ProjectMiddleware, projects.GetTaskMiddleware, projects.GetMustCanMiddlewareFor(db.CanRunProjectTasks))
projectTaskStop.HandleFunc("/{task_id}/stop", projects.StopTask).Methods("POST")

//
// Project resources CRUD
projectUserAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
projectUserAPI.Use(projects.ProjectMiddleware)
projectUserAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanManageProjectResources))

projectUserAPI.Path("/events").HandlerFunc(getAllEvents).Methods("GET", "HEAD")
projectUserAPI.HandleFunc("/events/last", getLastEvents).Methods("GET", "HEAD")
Expand All @@ -145,7 +157,6 @@ func Route() *mux.Router {

projectUserAPI.Path("/tasks").HandlerFunc(projects.GetAllTasks).Methods("GET", "HEAD")
projectUserAPI.HandleFunc("/tasks/last", projects.GetLastTasks).Methods("GET", "HEAD")
projectUserAPI.Path("/tasks").HandlerFunc(projects.AddTask).Methods("POST")

projectUserAPI.Path("/templates").HandlerFunc(projects.GetTemplates).Methods("GET", "HEAD")
projectUserAPI.Path("/templates").HandlerFunc(projects.AddTemplate).Methods("POST")
Expand All @@ -157,13 +168,17 @@ func Route() *mux.Router {
projectUserAPI.Path("/views").HandlerFunc(projects.AddView).Methods("POST")
projectUserAPI.Path("/views/positions").HandlerFunc(projects.SetViewPositions).Methods("POST")

//
// Updating and deleting project
projectAdminAPI := authenticatedAPI.Path("/project/{project_id}").Subrouter()
projectAdminAPI.Use(projects.ProjectMiddleware, projects.MustBeAdmin)
projectAdminAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanUpdateProject))
projectAdminAPI.Methods("PUT").HandlerFunc(projects.UpdateProject)
projectAdminAPI.Methods("DELETE").HandlerFunc(projects.DeleteProject)

//
// Manage project users
projectAdminUsersAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
projectAdminUsersAPI.Use(projects.ProjectMiddleware, projects.MustBeAdmin)
projectAdminUsersAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanManageProjectUsers))
projectAdminUsersAPI.Path("/users").HandlerFunc(projects.AddUser).Methods("POST")

projectUserManagement := projectAdminUsersAPI.PathPrefix("/users").Subrouter()
Expand All @@ -173,6 +188,8 @@ func Route() *mux.Router {
projectUserManagement.HandleFunc("/{user_id}", projects.UpdateUser).Methods("PUT")
projectUserManagement.HandleFunc("/{user_id}", projects.RemoveUser).Methods("DELETE")

//
// Project resources CRUD (continue)
projectKeyManagement := projectUserAPI.PathPrefix("/keys").Subrouter()
projectKeyManagement.Use(projects.KeyMiddleware)

Expand Down Expand Up @@ -222,7 +239,6 @@ func Route() *mux.Router {
projectTaskManagement.HandleFunc("/{task_id}/output", projects.GetTaskOutput).Methods("GET", "HEAD")
projectTaskManagement.HandleFunc("/{task_id}", projects.GetTask).Methods("GET", "HEAD")
projectTaskManagement.HandleFunc("/{task_id}", projects.RemoveTask).Methods("DELETE")
projectTaskManagement.HandleFunc("/{task_id}/stop", projects.StopTask).Methods("POST")

projectScheduleManagement := projectUserAPI.PathPrefix("/schedules").Subrouter()
projectScheduleManagement.Use(projects.SchedulesMiddleware)
Expand Down
13 changes: 8 additions & 5 deletions db/ProjectUser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@ type ProjectUserRole string

const (
ProjectOwner ProjectUserRole = "owner"
ProjectManager ProjectUserRole = "manager"
ProjectTaskRunner ProjectUserRole = "task_runner"
ProjectGuest ProjectUserRole = "guest"
)

type ProjectUserPermission int

const (
ProjectUserCanRunTask ProjectUserPermission = 1 << iota
ProjectCanEditProjectSettings
ProjectCanRunTasks
CanRunProjectTasks ProjectUserPermission = 1 << iota
CanUpdateProject
CanManageProjectResources
CanManageProjectUsers
)

var rolePermissions = map[ProjectUserRole]ProjectUserPermission{
ProjectOwner: ProjectUserCanRunTask | ProjectCanEditProjectSettings | ProjectCanRunTasks,
ProjectTaskRunner: ProjectCanRunTasks,
ProjectOwner: CanRunProjectTasks | CanUpdateProject | CanManageProjectResources,
ProjectManager: CanRunProjectTasks | CanManageProjectResources,
ProjectTaskRunner: CanRunProjectTasks,
ProjectGuest: 0,
}

Expand Down

0 comments on commit 87d9835

Please sign in to comment.