From 957aebe695de9c04d862bb43d9a938ebee41f6bb Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 21 Jan 2025 09:21:46 -0500 Subject: [PATCH] Working! --- pkg/rbac/conditions.go | 2 +- pkg/rbac/rbac.go | 7 ++++++- pkg/rest/calls.go | 5 +++-- pkg/rest/incidents.go | 15 +++++++++----- pkg/rest/share.go | 45 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/pkg/rbac/conditions.go b/pkg/rbac/conditions.go index 91a0c3c..ddebd69 100644 --- a/pkg/rbac/conditions.go +++ b/pkg/rbac/conditions.go @@ -54,7 +54,7 @@ func (c *CallInIncidentCondition) Check(r *restrict.AccessRequest) error { return restrict.NewConditionNotSatisfiedError(c, r, errors.New("call ID is not UUID")) } - inCall, err := database.FromCtx(ctx).CallInIncident(ctx, incID, incID) + inCall, err := database.FromCtx(ctx).CallInIncident(ctx, incID, callID) if err != nil { return restrict.NewConditionNotSatisfiedError(c, r, err) } diff --git a/pkg/rbac/rbac.go b/pkg/rbac/rbac.go index d1f9ec4..172b634 100644 --- a/pkg/rbac/rbac.go +++ b/pkg/rbac/rbac.go @@ -139,7 +139,12 @@ func (r *rbac) Check(ctx context.Context, res restrict.Resource, opts ...CheckOp Context: o.context, } - return sub, r.access.Authorize(req) + err := r.access.Authorize(req) + if err != nil { + return nil, err + } + + return sub, nil } type PublicSubject struct { diff --git a/pkg/rest/calls.go b/pkg/rest/calls.go index 285a761..ea5b6c0 100644 --- a/pkg/rest/calls.go +++ b/pkg/rest/calls.go @@ -11,6 +11,7 @@ import ( "dynatron.me/x/stillbox/internal/forms" "dynatron.me/x/stillbox/pkg/calls/callstore" "dynatron.me/x/stillbox/pkg/database" + "dynatron.me/x/stillbox/pkg/shares" "github.com/go-chi/chi/v5" "github.com/google/uuid" @@ -101,7 +102,7 @@ func (ca *callsAPI) getAudio(p getAudioParams, w http.ResponseWriter, r *http.Re _, _ = w.Write(call.AudioBlob) } -func (ca *callsAPI) shareCallRoute(id uuid.UUID, w http.ResponseWriter, r *http.Request) { +func (ca *callsAPI) shareCallRoute(id uuid.UUID, _ *shares.Share, w http.ResponseWriter, r *http.Request) { p := getAudioParams{ CallID: &id, } @@ -109,7 +110,7 @@ func (ca *callsAPI) shareCallRoute(id uuid.UUID, w http.ResponseWriter, r *http. ca.getAudio(p, w, r) } -func (ca *callsAPI) shareCallDLRoute(id uuid.UUID, w http.ResponseWriter, r *http.Request) { +func (ca *callsAPI) shareCallDLRoute(id uuid.UUID, _ *shares.Share, w http.ResponseWriter, r *http.Request) { p := getAudioParams{ CallID: &id, Download: common.PtrTo("download"), diff --git a/pkg/rest/incidents.go b/pkg/rest/incidents.go index c7effb5..0da9412 100644 --- a/pkg/rest/incidents.go +++ b/pkg/rest/incidents.go @@ -12,6 +12,7 @@ import ( "dynatron.me/x/stillbox/internal/jsontypes" "dynatron.me/x/stillbox/pkg/incidents" "dynatron.me/x/stillbox/pkg/incidents/incstore" + "dynatron.me/x/stillbox/pkg/shares" "dynatron.me/x/stillbox/pkg/talkgroups/tgstore" "github.com/go-chi/chi/v5" @@ -93,10 +94,10 @@ func (ia *incidentsAPI) getIncidentRoute(w http.ResponseWriter, r *http.Request) return } - ia.getIncident(id, w, r) + ia.getIncident(id, nil, w, r) } -func (ia *incidentsAPI) getIncident(id uuid.UUID, w http.ResponseWriter, r *http.Request) { +func (ia *incidentsAPI) getIncident(id uuid.UUID, share *shares.Share, w http.ResponseWriter, r *http.Request) { ctx := r.Context() incs := incstore.FromCtx(ctx) inc, err := incs.Incident(ctx, id) @@ -194,10 +195,10 @@ func (ia *incidentsAPI) getCallsM3URoute(w http.ResponseWriter, r *http.Request) return } - ia.getCallsM3U(id, w, r) + ia.getCallsM3U(id, nil, w, r) } -func (ia *incidentsAPI) getCallsM3U(id uuid.UUID, w http.ResponseWriter, r *http.Request) { +func (ia *incidentsAPI) getCallsM3U(id uuid.UUID, share *shares.Share, w http.ResponseWriter, r *http.Request) { ctx := r.Context() incs := incstore.FromCtx(ctx) tgst := tgstore.FromCtx(ctx) @@ -211,6 +212,10 @@ func (ia *incidentsAPI) getCallsM3U(id uuid.UUID, w http.ResponseWriter, r *http b := new(bytes.Buffer) callUrl := common.PtrTo(*ia.baseURL) + urlRoot := "/api/call" + if share != nil { + urlRoot = fmt.Sprintf("/share/%s/%s/call/", share.Type, share.ID) + } b.WriteString("#EXTM3U\n\n") for _, c := range inc.Calls { @@ -224,7 +229,7 @@ func (ia *incidentsAPI) getCallsM3U(id uuid.UUID, w http.ResponseWriter, r *http from = fmt.Sprintf(" from %d", c.Source) } - callUrl.Path = "/api/call/" + c.ID.String() + callUrl.Path = urlRoot + c.ID.String() fmt.Fprintf(b, "#EXTINF:%d,%s%s (%s)\n%s\n\n", c.Duration.Seconds(), diff --git a/pkg/rest/share.go b/pkg/rest/share.go index aee47aa..8406533 100644 --- a/pkg/rest/share.go +++ b/pkg/rest/share.go @@ -29,14 +29,23 @@ const ( func (rt ShareRequestType) IsValid() bool { switch rt { - case ShareRequestCall, ShareRequestIncident, ShareRequestIncidentM3U: + case ShareRequestCall, ShareRequestCallDL, ShareRequestIncident, ShareRequestIncidentM3U: return true } return false } -type HandlerFunc func(uuid.UUID, http.ResponseWriter, *http.Request) +func (rt ShareRequestType) IsValidSubtype() bool { + switch rt { + case ShareRequestCall, ShareRequestCallDL: + return true + } + + return false +} + +type HandlerFunc func(id uuid.UUID, share *shares.Share, w http.ResponseWriter, r *http.Request) type ShareHandlers map[ShareRequestType]HandlerFunc type shareAPI struct { baseURL *url.URL @@ -63,6 +72,7 @@ func (sa *shareAPI) RootRouter() http.Handler { r := chi.NewMux() r.Get("/{type}/{shareId:[A-Za-z0-9_-]{20,}}", sa.routeShare) + r.Get("/{type}/{shareId:[A-Za-z0-9_-]{20,}}/{subType}/{subID}", sa.routeShare) return r } @@ -92,8 +102,10 @@ func (sa *shareAPI) routeShare(w http.ResponseWriter, r *http.Request) { shs := shares.FromCtx(ctx) params := struct { - Type string `param:"type"` - ID string `param:"shareId"` + Type string `param:"type"` + ID string `param:"shareId"` + SubType *string `param:"subType"` + SubID *string `param:"subID"` }{} err := decodeParams(¶ms, r) @@ -124,7 +136,30 @@ func (sa *shareAPI) routeShare(w http.ResponseWriter, r *http.Request) { ctx = rbac.CtxWithSubject(ctx, sh) r = r.WithContext(ctx) - sa.shnd[rType](sh.EntityID, w, r) + if params.SubType != nil { + if params.SubID == nil { + // probably can't happen + wErr(w, r, autoError(ErrBadShare)) + return + } + + subT := ShareRequestType(*params.SubType) + if !subT.IsValidSubtype() { + wErr(w, r, autoError(ErrBadShare)) + return + } + + subIDU, err := uuid.Parse(*params.SubID) + if err != nil { + wErr(w, r, badRequest(err)) + return + } + + sa.shnd[subT](subIDU, sh, w, r) + return + } + + sa.shnd[rType](sh.EntityID, sh, w, r) } func (sa *shareAPI) deleteShare(w http.ResponseWriter, r *http.Request) {