feat(09-11): add permissioned admin metadata and settings
This commit is contained in:
108
cabana/http.go
108
cabana/http.go
@@ -58,6 +58,9 @@ func Activate(app *backpack.App, plugins []party.Plugin) (*Routes, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := compileContributions(reg, plugins); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if app == nil {
|
||||
return nil, errors.New("cabana: app is nil")
|
||||
}
|
||||
@@ -69,7 +72,7 @@ func Activate(app *backpack.App, plugins []party.Plugin) (*Routes, error) {
|
||||
}
|
||||
}
|
||||
bl := adminBlacklist(app)
|
||||
guard := bouncer.NewBackendJWTGuard(secret, lazyBackendUsers{app: app}, bl, writeUnauthenticated)
|
||||
guard := bouncer.NewBackendJWTGuard(secret, lazyBackendUsers{app: app, reg: reg}, bl, writeUnauthenticated)
|
||||
if _, err := guards.Middleware("backend"); err != nil {
|
||||
if err := guards.Register("summercms.cabana", "backend", guard); err != nil {
|
||||
return nil, err
|
||||
@@ -102,6 +105,7 @@ func writeUnauthenticated(w http.ResponseWriter, _ error) {
|
||||
|
||||
type lazyBackendUsers struct {
|
||||
app *backpack.App
|
||||
reg *Registry
|
||||
}
|
||||
|
||||
func (p lazyBackendUsers) FindByID(ctx context.Context, id uint) (*bouncer.Principal, error) {
|
||||
@@ -112,7 +116,7 @@ func (p lazyBackendUsers) FindByID(ctx context.Context, id uint) (*bouncer.Princ
|
||||
if !ok || db == nil {
|
||||
return nil, errors.New("cabana: database is not configured")
|
||||
}
|
||||
return (BackendUsers{DB: db}).FindByID(ctx, id)
|
||||
return (BackendUsers{DB: db, Registry: p.reg}).FindByID(ctx, id)
|
||||
}
|
||||
|
||||
func (s *service) mount(r pact.Router) {
|
||||
@@ -124,6 +128,14 @@ func (s *service) mount(r pact.Router) {
|
||||
r.GroupRaw("/_admin/api/v1", []string{"backend"}, func(g pact.Router) {
|
||||
g.Post("/auth/logout", s.logout)
|
||||
g.Get("/auth/me", s.me)
|
||||
g.Get("/navigation", s.navigation)
|
||||
g.Get("/settings", s.settingsList)
|
||||
g.Get("/settings/{code}/schema", s.settingsSchema)
|
||||
constrainSetting(g)
|
||||
g.Get("/settings/{code}", s.settingsGet)
|
||||
constrainSetting(g)
|
||||
g.Put("/settings/{code}", s.settingsPut)
|
||||
constrainSetting(g)
|
||||
g.Get("/{vendor}/{plugin}/{controller}/schema/list", s.listSchema)
|
||||
constrainController(g)
|
||||
g.Get("/{vendor}/{plugin}/{controller}/schema/form", s.formSchema)
|
||||
@@ -164,6 +176,96 @@ func constrainRelation(g pact.Router) {
|
||||
g.Where("name", "[A-Za-z_][A-Za-z0-9_]*")
|
||||
}
|
||||
|
||||
func constrainSetting(g pact.Router) {
|
||||
g.Where("code", "[A-Za-z_][A-Za-z0-9_-]*")
|
||||
}
|
||||
|
||||
func (s *service) navigation(w http.ResponseWriter, r *http.Request) {
|
||||
principal, ok := bouncer.User(r.Context())
|
||||
if !ok || principal == nil || !principal.Backend {
|
||||
WriteError(w, http.StatusUnauthorized, "unauthenticated", msgUnauthenticated)
|
||||
return
|
||||
}
|
||||
navigation, _ := s.reg.Metadata(r.Context(), principal, s.translator())
|
||||
WriteData(w, http.StatusOK, navigation, map[string]any{"locale": schemaLocale(r.Context(), s.translator())})
|
||||
}
|
||||
|
||||
func (s *service) settingsList(w http.ResponseWriter, r *http.Request) {
|
||||
principal, ok := bouncer.User(r.Context())
|
||||
if !ok || principal == nil || !principal.Backend {
|
||||
WriteError(w, http.StatusUnauthorized, "unauthenticated", msgUnauthenticated)
|
||||
return
|
||||
}
|
||||
_, settings := s.reg.Metadata(r.Context(), principal, s.translator())
|
||||
WriteData(w, http.StatusOK, settings, map[string]any{"locale": schemaLocale(r.Context(), s.translator())})
|
||||
}
|
||||
|
||||
func (s *service) settingsSchema(w http.ResponseWriter, r *http.Request) {
|
||||
s.protectSetting(w, r, func(setting *CompiledSetting) {
|
||||
view, err := setting.Form.Localize(r.Context(), s.translator(), nil)
|
||||
if err != nil {
|
||||
WriteError(w, http.StatusInternalServerError, "error", msgServerError)
|
||||
return
|
||||
}
|
||||
WriteData(w, http.StatusOK, view, map[string]any{"locale": view.Meta.Locale})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *service) settingsGet(w http.ResponseWriter, r *http.Request) {
|
||||
s.protectSetting(w, r, func(setting *CompiledSetting) {
|
||||
db, err := s.db()
|
||||
if err != nil {
|
||||
WriteError(w, http.StatusInternalServerError, "error", msgServerError)
|
||||
return
|
||||
}
|
||||
result, err := (SettingsService{DB: db}).Get(r.Context(), setting)
|
||||
if err != nil {
|
||||
writeCRUDError(w, err)
|
||||
return
|
||||
}
|
||||
WriteData(w, http.StatusOK, result, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *service) settingsPut(w http.ResponseWriter, r *http.Request) {
|
||||
s.protectSetting(w, r, func(setting *CompiledSetting) {
|
||||
body, err := decodeObject(r)
|
||||
if err != nil {
|
||||
writeCRUDError(w, err)
|
||||
return
|
||||
}
|
||||
db, err := s.db()
|
||||
if err != nil {
|
||||
WriteError(w, http.StatusInternalServerError, "error", msgServerError)
|
||||
return
|
||||
}
|
||||
result, err := (SettingsService{DB: db}).Put(r.Context(), setting, body)
|
||||
if err != nil {
|
||||
writeCRUDError(w, err)
|
||||
return
|
||||
}
|
||||
WriteData(w, http.StatusOK, result, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *service) protectSetting(w http.ResponseWriter, r *http.Request, fn func(*CompiledSetting)) {
|
||||
principal, ok := bouncer.User(r.Context())
|
||||
if !ok || principal == nil || !principal.Backend {
|
||||
WriteError(w, http.StatusUnauthorized, "unauthenticated", msgUnauthenticated)
|
||||
return
|
||||
}
|
||||
setting, exists := s.reg.Setting(r.PathValue("code"))
|
||||
if !exists {
|
||||
WriteError(w, http.StatusNotFound, "not_found", msgNotFound)
|
||||
return
|
||||
}
|
||||
if !Allows(principal, setting.Item.Permissions) {
|
||||
WriteError(w, http.StatusForbidden, "forbidden", msgForbidden)
|
||||
return
|
||||
}
|
||||
fn(setting)
|
||||
}
|
||||
|
||||
func (s *service) relationSchema(w http.ResponseWriter, r *http.Request) {
|
||||
s.protect(w, r, func(cc *CompiledController) {
|
||||
cr, err := relationOf(cc, r.PathValue("name"))
|
||||
@@ -495,7 +597,7 @@ func (s *service) protect(w http.ResponseWriter, r *http.Request, fn func(*Compi
|
||||
return
|
||||
}
|
||||
principal, _ := bouncer.User(r.Context())
|
||||
if principal == nil {
|
||||
if principal == nil || !principal.Backend {
|
||||
WriteError(w, http.StatusUnauthorized, "unauthenticated", msgUnauthenticated)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user