diff --git a/http/api/report.go b/http/api/report.go index 025f0f94..0d5166c3 100644 --- a/http/api/report.go +++ b/http/api/report.go @@ -122,6 +122,7 @@ func (r *ProcessReport) Marshal() app.Report { type ProcessReportSearchResult struct { ProcessID string `json:"id"` + Domain string `json:"domain"` Reference string `json:"reference"` ExitState string `json:"exit_state"` CreatedAt int64 `json:"created_at" format:"int64"` diff --git a/http/handler/api/cluster_process.go b/http/handler/api/cluster_process.go index 755238a7..818f36a8 100644 --- a/http/handler/api/cluster_process.go +++ b/http/handler/api/cluster_process.go @@ -22,7 +22,6 @@ import ( // @Tags v16.?.? // @ID cluster-3-get-all-processes // @Produce json -// @Param domain query string false "Domain to act on" // @Param filter query string false "Comma separated list of fields (config, state, report, metadata) that will be part of the output. If empty, all fields will be part of the output." // @Param reference query string false "Return only these process that have this reference value. If empty, the reference will be ignored." // @Param id query string false "Comma separated list of process ids to list. Overrides the reference. If empty all IDs will be returned." @@ -40,16 +39,15 @@ func (h *ClusterHandler) ProcessList(c echo.Context) error { wantids := strings.FieldsFunc(util.DefaultQuery(c, "id", ""), func(r rune) bool { return r == rune(',') }) - domain := util.DefaultQuery(c, "domain", "") idpattern := util.DefaultQuery(c, "idpattern", "") refpattern := util.DefaultQuery(c, "refpattern", "") ownerpattern := util.DefaultQuery(c, "ownerpattern", "") domainpattern := util.DefaultQuery(c, "domainpattern", "") procs := h.proxy.ProcessList(node.ProcessListOptions{ - ID: wantids, - Filter: filter.Slice(), - Domain: domain, + ID: wantids, + Filter: filter.Slice(), + //Domain: domain, Reference: reference, IDPattern: idpattern, RefPattern: refpattern, @@ -60,7 +58,7 @@ func (h *ClusterHandler) ProcessList(c echo.Context) error { pmap := map[app.ProcessID]api.Process{} for _, p := range procs { - if !h.iam.Enforce(ctxuser, domain, "process", p.ID, "read") { + if !h.iam.Enforce(ctxuser, p.Domain, "process", p.ID, "read") { continue } @@ -72,10 +70,10 @@ func (h *ClusterHandler) ProcessList(c echo.Context) error { // Here we have to add those processes that are in the cluster DB and couldn't be deployed { processes := h.cluster.Store().ProcessList() - filtered := h.getFilteredStoreProcesses(processes, wantids, domain, reference, idpattern, refpattern, ownerpattern, domainpattern) + filtered := h.getFilteredStoreProcesses(processes, wantids, reference, idpattern, refpattern, ownerpattern, domainpattern) for _, p := range filtered { - if !h.iam.Enforce(ctxuser, domain, "process", p.Config.ID, "read") { + if !h.iam.Enforce(ctxuser, p.Config.Domain, "process", p.Config.ID, "read") { continue } @@ -105,7 +103,7 @@ func (h *ClusterHandler) ProcessList(c echo.Context) error { return c.JSON(http.StatusOK, processes) } -func (h *ClusterHandler) getFilteredStoreProcesses(processes []store.Process, wantids []string, _, reference, idpattern, refpattern, ownerpattern, domainpattern string) []store.Process { +func (h *ClusterHandler) getFilteredStoreProcesses(processes []store.Process, wantids []string, reference, idpattern, refpattern, ownerpattern, domainpattern string) []store.Process { filtered := []store.Process{} count := 0 @@ -252,7 +250,6 @@ func (h *ClusterHandler) ProcessGet(c echo.Context) error { // @Router /api/v3/cluster/process [post] func (h *ClusterHandler) ProcessAdd(c echo.Context) error { ctxuser := util.DefaultContext(c, "user", "") - superuser := util.DefaultContext(c, "superuser", false) process := api.ProcessConfig{ ID: shortuuid.New(), @@ -269,12 +266,6 @@ func (h *ClusterHandler) ProcessAdd(c echo.Context) error { return api.Err(http.StatusForbidden, "", "API user %s is not allowed to write this process in domain %s", ctxuser, process.Domain) } - if !superuser { - if !h.iam.Enforce(process.Owner, process.Domain, "process", process.ID, "write") { - return api.Err(http.StatusForbidden, "", "user %s is not allowed to write this process in domain %s", process.Owner, process.Domain) - } - } - if process.Type != "ffmpeg" { return api.Err(http.StatusBadRequest, "", "unsupported process type: supported process types are: ffmpeg") } @@ -314,7 +305,6 @@ func (h *ClusterHandler) ProcessAdd(c echo.Context) error { // @Router /api/v3/cluster/process/{id} [put] func (h *ClusterHandler) ProcessUpdate(c echo.Context) error { ctxuser := util.DefaultContext(c, "user", "") - superuser := util.DefaultContext(c, "superuser", false) domain := util.DefaultQuery(c, "domain", "") id := util.PathParam(c, "id") @@ -348,12 +338,6 @@ func (h *ClusterHandler) ProcessUpdate(c echo.Context) error { return api.Err(http.StatusForbidden, "", "API user %s is not allowed to write this process", ctxuser) } - if !superuser { - if !h.iam.Enforce(process.Owner, process.Domain, "process", process.ID, "write") { - return api.Err(http.StatusForbidden, "", "user %s is not allowed to write this process", process.Owner) - } - } - config, metadata := process.Marshal() if err := h.cluster.ProcessUpdate("", pid, config); err != nil { diff --git a/http/handler/api/iam.go b/http/handler/api/iam.go index f2a9d71e..2dcb5f03 100644 --- a/http/handler/api/iam.go +++ b/http/handler/api/iam.go @@ -292,13 +292,13 @@ func (h *IAMHandler) UpdateIdentityPolicies(c echo.Context) error { // @Router /api/v3/iam/user [get] func (h *IAMHandler) ListIdentities(c echo.Context) error { ctxuser := util.DefaultContext(c, "user", "") - domain := util.DefaultQuery(c, "domain", "$none") + domain := util.DefaultQuery(c, "domain", "") identities := h.iam.ListIdentities() - users := make([]api.IAMUser, len(identities)+1) + users := []api.IAMUser{} - for i, iamuser := range identities { + for _, iamuser := range identities { if !h.iam.Enforce(ctxuser, domain, "iam", iamuser.Name, "read") { continue } @@ -311,7 +311,9 @@ func (h *IAMHandler) ListIdentities(c echo.Context) error { policies := h.iam.ListPolicies(iamuser.Name, "", nil, "", nil) - users[i].Marshal(iamuser, policies) + user := api.IAMUser{} + user.Marshal(iamuser, policies) + users = append(users, user) } anon := identity.User{ @@ -320,7 +322,9 @@ func (h *IAMHandler) ListIdentities(c echo.Context) error { policies := h.iam.ListPolicies("$anon", "", nil, "", nil) - users[len(users)-1].Marshal(anon, policies) + user := api.IAMUser{} + user.Marshal(anon, policies) + users = append(users, user) return c.JSON(http.StatusOK, users) } diff --git a/http/handler/api/process.go b/http/handler/api/process.go index d8b15bdc..f7f595ea 100644 --- a/http/handler/api/process.go +++ b/http/handler/api/process.go @@ -50,7 +50,6 @@ func NewProcess(restream restream.Restreamer, iam iam.IAM) *ProcessHandler { // @Router /api/v3/process [post] func (h *ProcessHandler) Add(c echo.Context) error { ctxuser := util.DefaultContext(c, "user", "") - superuser := util.DefaultContext(c, "superuser", false) process := api.ProcessConfig{ ID: shortuuid.New(), @@ -67,12 +66,6 @@ func (h *ProcessHandler) Add(c echo.Context) error { return api.Err(http.StatusForbidden, "", "You are not allowed to write this process, check the domain and process ID") } - if !superuser { - if !h.iam.Enforce(process.Owner, process.Domain, "process", process.ID, "write") { - return api.Err(http.StatusForbidden, "", "The owner '%s' is not allowed to write this process", process.Owner) - } - } - if process.Type != "ffmpeg" { return api.Err(http.StatusBadRequest, "", "unsupported process type, supported process types are: ffmpeg") } @@ -107,7 +100,6 @@ func (h *ProcessHandler) Add(c echo.Context) error { // @Tags v16.7.2 // @ID process-3-get-all // @Produce json -// @Param domain query string false "Domain to act on" // @Param filter query string false "Comma separated list of fields (config, state, report, metadata) that will be part of the output. If empty, all fields will be part of the output." // @Param reference query string false "Return only these process that have this reference value. If empty, the reference will be ignored." // @Param id query string false "Comma separated list of process ids to list. Overrides the reference. If empty all IDs will be returned." @@ -125,7 +117,6 @@ func (h *ProcessHandler) GetAll(c echo.Context) error { wantids := strings.FieldsFunc(util.DefaultQuery(c, "id", ""), func(r rune) bool { return r == rune(',') }) - domain := util.DefaultQuery(c, "domain", "") idpattern := util.DefaultQuery(c, "idpattern", "") refpattern := util.DefaultQuery(c, "refpattern", "") ownerpattern := util.DefaultQuery(c, "ownerpattern", "") @@ -157,19 +148,19 @@ func (h *ProcessHandler) GetAll(c echo.Context) error { wg := sync.WaitGroup{} - for i := 0; i < 8; /*runtime.NumCPU()*/ i++ { + for range 8 { wg.Add(1) go func(idChan <-chan app.ProcessID) { defer wg.Done() for id := range idChan { - if !h.iam.Enforce(ctxuser, domain, "process", id.ID, "read") { + process, err := h.getProcess(id, filter) + if err != nil { continue } - process, err := h.getProcess(id, filter) - if err != nil { + if !h.iam.Enforce(ctxuser, process.Domain, "process", id.ID, "read") { continue } @@ -218,7 +209,7 @@ func (h *ProcessHandler) Get(c echo.Context) error { domain := util.DefaultQuery(c, "domain", "") if !h.iam.Enforce(ctxuser, domain, "process", id, "read") { - return api.Err(http.StatusForbidden, "Forbidden") + return api.Err(http.StatusForbidden, "") } tid := app.ProcessID{ @@ -251,7 +242,6 @@ func (h *ProcessHandler) Get(c echo.Context) error { // @Router /api/v3/process/{id} [delete] func (h *ProcessHandler) Delete(c echo.Context) error { ctxuser := util.DefaultContext(c, "user", "") - superuser := util.DefaultContext(c, "superuser", false) id := util.PathParam(c, "id") domain := util.DefaultQuery(c, "domain", "") @@ -260,10 +250,8 @@ func (h *ProcessHandler) Delete(c echo.Context) error { Domain: domain, } - if !superuser { - if !h.iam.Enforce(ctxuser, domain, "process", id, "write") { - return api.Err(http.StatusForbidden, "") - } + if !h.iam.Enforce(ctxuser, domain, "process", id, "write") { + return api.Err(http.StatusForbidden, "") } if err := h.restream.StopProcess(tid); err != nil { @@ -296,7 +284,6 @@ func (h *ProcessHandler) Delete(c echo.Context) error { // @Router /api/v3/process/{id} [put] func (h *ProcessHandler) Update(c echo.Context) error { ctxuser := util.DefaultContext(c, "user", "") - superuser := util.DefaultContext(c, "superuser", false) domain := util.DefaultQuery(c, "domain", "") id := util.PathParam(c, "id") @@ -333,12 +320,6 @@ func (h *ProcessHandler) Update(c echo.Context) error { return api.Err(http.StatusForbidden, "", "You are not allowed to write this process: %s", process.ID) } - if !superuser { - if !h.iam.Enforce(process.Owner, process.Domain, "process", process.ID, "write") { - return api.Err(http.StatusForbidden, "", "The owner '%s' is not allowed to write this process: %s", process.Owner, process.ID) - } - } - config, metadata := process.Marshal() if err := h.restream.UpdateProcess(tid, config); err != nil { @@ -397,15 +378,16 @@ func (h *ProcessHandler) Command(c echo.Context) error { } var err error - if command.Command == "start" { + switch command.Command { + case "start": err = h.restream.StartProcess(tid) - } else if command.Command == "stop" { + case "stop": err = h.restream.StopProcess(tid) - } else if command.Command == "restart" { + case "restart": err = h.restream.RestartProcess(tid) - } else if command.Command == "reload" { + case "reload": err = h.restream.ReloadProcess(tid) - } else { + default: return api.Err(http.StatusBadRequest, "", "unknown command provided: known commands are: start, stop, reload, restart") } @@ -673,6 +655,7 @@ func (h *ProcessHandler) SetReport(c echo.Context) error { // @Security ApiKeyAuth // @Router /api/v3/report/process [get] func (h *ProcessHandler) SearchReportHistory(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") idpattern := util.DefaultQuery(c, "idpattern", "") refpattern := util.DefaultQuery(c, "refpattern", "") state := util.DefaultQuery(c, "state", "") @@ -701,13 +684,20 @@ func (h *ProcessHandler) SearchReportHistory(c echo.Context) error { result := h.restream.SearchProcessLogHistory(idpattern, refpattern, state, from, to) - response := make([]api.ProcessReportSearchResult, len(result)) - for i, b := range result { - response[i].ProcessID = b.ProcessID - response[i].Reference = b.Reference - response[i].ExitState = b.ExitState - response[i].CreatedAt = b.CreatedAt.Unix() - response[i].ExitedAt = b.ExitedAt.Unix() + response := []api.ProcessReportSearchResult{} + for _, b := range result { + if !h.iam.Enforce(ctxuser, b.Domain, "process", b.ProcessID, "read") { + continue + } + + response = append(response, api.ProcessReportSearchResult{ + ProcessID: b.ProcessID, + Domain: b.Domain, + Reference: b.Reference, + ExitState: b.ExitState, + CreatedAt: b.CreatedAt.Unix(), + ExitedAt: b.ExitedAt.Unix(), + }) } return c.JSON(http.StatusOK, response) @@ -843,43 +833,6 @@ func (h *ProcessHandler) ValidateConfig(c echo.Context) error { return c.JSON(http.StatusOK, process) } -// Skills returns the detected FFmpeg capabilities -// @Summary FFmpeg capabilities -// @Description List all detected FFmpeg capabilities. -// @Tags v16.7.2 -// @ID skills-3 -// @Produce json -// @Success 200 {object} api.Skills -// @Security ApiKeyAuth -// @Router /api/v3/skills [get] -func (h *ProcessHandler) Skills(c echo.Context) error { - skills := h.restream.Skills() - - apiskills := api.Skills{} - apiskills.Unmarshal(skills) - - return c.JSON(http.StatusOK, apiskills) -} - -// ReloadSkills will refresh the FFmpeg capabilities -// @Summary Refresh FFmpeg capabilities -// @Description Refresh the available FFmpeg capabilities. -// @Tags v16.7.2 -// @ID skills-3-reload -// @Produce json -// @Success 200 {object} api.Skills -// @Security ApiKeyAuth -// @Router /api/v3/skills/reload [get] -func (h *ProcessHandler) ReloadSkills(c echo.Context) error { - h.restream.ReloadSkills() - skills := h.restream.Skills() - - apiskills := api.Skills{} - apiskills.Unmarshal(skills) - - return c.JSON(http.StatusOK, apiskills) -} - // GetProcessMetadata returns the metadata stored with a process // @Summary Retrieve JSON metadata stored with a process under a key // @Description Retrieve the previously stored JSON metadata under the given key. If the key is empty, all metadata will be returned. @@ -1028,6 +981,43 @@ func (h *ProcessHandler) SetMetadata(c echo.Context) error { return c.JSON(http.StatusOK, data) } +// Skills returns the detected FFmpeg capabilities +// @Summary FFmpeg capabilities +// @Description List all detected FFmpeg capabilities. +// @Tags v16.7.2 +// @ID skills-3 +// @Produce json +// @Success 200 {object} api.Skills +// @Security ApiKeyAuth +// @Router /api/v3/skills [get] +func (h *ProcessHandler) Skills(c echo.Context) error { + skills := h.restream.Skills() + + apiskills := api.Skills{} + apiskills.Unmarshal(skills) + + return c.JSON(http.StatusOK, apiskills) +} + +// ReloadSkills will refresh the FFmpeg capabilities +// @Summary Refresh FFmpeg capabilities +// @Description Refresh the available FFmpeg capabilities. +// @Tags v16.7.2 +// @ID skills-3-reload +// @Produce json +// @Success 200 {object} api.Skills +// @Security ApiKeyAuth +// @Router /api/v3/skills/reload [get] +func (h *ProcessHandler) ReloadSkills(c echo.Context) error { + h.restream.ReloadSkills() + skills := h.restream.Skills() + + apiskills := api.Skills{} + apiskills.Unmarshal(skills) + + return c.JSON(http.StatusOK, apiskills) +} + type filter struct { config bool state bool