appview/validator: limit label ops to collaborators #607

merged
opened by oppi.li targeting master from push-ttyzwxqtkpsl
Changed files
+33 -6
appview
+1 -1
appview/ingester.go
···
if !ok {
return fmt.Errorf("failed to find label def for key: %s, expected: %q", o.OperandKey, slices.Collect(maps.Keys(actx.Defs)))
}
-
if err := i.Validator.ValidateLabelOp(def, &o); err != nil {
return fmt.Errorf("failed to validate labelop: %w", err)
}
}
···
if !ok {
return fmt.Errorf("failed to find label def for key: %s, expected: %q", o.OperandKey, slices.Collect(maps.Keys(actx.Defs)))
}
+
if err := i.Validator.ValidateLabelOp(def, repo, &o); err != nil {
return fmt.Errorf("failed to validate labelop: %w", err)
}
}
+11 -1
appview/labels/labels.go
···
"tangled.org/core/appview/validator"
"tangled.org/core/appview/xrpcclient"
"tangled.org/core/log"
"tangled.org/core/tid"
)
···
db *db.DB
logger *slog.Logger
validator *validator.Validator
}
func New(
···
pages *pages.Pages,
db *db.DB,
validator *validator.Validator,
) *Labels {
logger := log.New("labels")
···
db: db,
logger: logger,
validator: validator,
}
}
···
repoAt := r.Form.Get("repo")
subjectUri := r.Form.Get("subject")
// find all the labels that this repo subscribes to
repoLabels, err := db.GetRepoLabels(l.db, db.FilterEq("repo_at", repoAt))
if err != nil {
···
for i := range labelOps {
def := actx.Defs[labelOps[i].OperandKey]
-
if err := l.validator.ValidateLabelOp(def, &labelOps[i]); err != nil {
fail(fmt.Sprintf("Invalid form data: %s", err), err)
return
}
···
"tangled.org/core/appview/validator"
"tangled.org/core/appview/xrpcclient"
"tangled.org/core/log"
+
"tangled.org/core/rbac"
"tangled.org/core/tid"
)
···
db *db.DB
logger *slog.Logger
validator *validator.Validator
+
enforcer *rbac.Enforcer
}
func New(
···
pages *pages.Pages,
db *db.DB,
validator *validator.Validator,
+
enforcer *rbac.Enforcer,
) *Labels {
logger := log.New("labels")
···
db: db,
logger: logger,
validator: validator,
+
enforcer: enforcer,
}
}
···
repoAt := r.Form.Get("repo")
subjectUri := r.Form.Get("subject")
+
repo, err := db.GetRepo(l.db, db.FilterEq("at_uri", repoAt))
+
if err != nil {
+
fail("Failed to get repository.", err)
+
return
+
}
+
// find all the labels that this repo subscribes to
repoLabels, err := db.GetRepoLabels(l.db, db.FilterEq("repo_at", repoAt))
if err != nil {
···
for i := range labelOps {
def := actx.Defs[labelOps[i].OperandKey]
+
if err := l.validator.ValidateLabelOp(def, repo, &labelOps[i]); err != nil {
fail(fmt.Sprintf("Invalid form data: %s", err), err)
return
}
+1 -1
appview/state/router.go
···
}
func (s *State) LabelsRouter(mw *middleware.Middleware) http.Handler {
-
ls := labels.New(s.oauth, s.pages, s.db, s.validator)
return ls.Router(mw)
}
···
}
func (s *State) LabelsRouter(mw *middleware.Middleware) http.Handler {
+
ls := labels.New(s.oauth, s.pages, s.db, s.validator, s.enforcer)
return ls.Router(mw)
}
+1 -1
appview/state/state.go
···
cache := cache.New(config.Redis.Addr)
sess := session.New(cache)
oauth := oauth.NewOAuth(config, sess)
-
validator := validator.New(d, res)
posthog, err := posthog.NewWithConfig(config.Posthog.ApiKey, posthog.Config{Endpoint: config.Posthog.Endpoint})
if err != nil {
···
cache := cache.New(config.Redis.Addr)
sess := session.New(cache)
oauth := oauth.NewOAuth(config, sess)
+
validator := validator.New(d, res, enforcer)
posthog, err := posthog.NewWithConfig(config.Posthog.ApiKey, posthog.Config{Endpoint: config.Posthog.Endpoint})
if err != nil {
+15 -1
appview/validator/label.go
···
return nil
}
-
func (v *Validator) ValidateLabelOp(labelDef *models.LabelDefinition, labelOp *models.LabelOp) error {
if labelDef == nil {
return fmt.Errorf("label definition is required")
}
if labelOp == nil {
return fmt.Errorf("label operation is required")
}
expectedKey := labelDef.AtUri().String()
if labelOp.OperandKey != expectedKey {
return fmt.Errorf("operand key %q does not match label definition URI %q", labelOp.OperandKey, expectedKey)
···
return nil
}
+
func (v *Validator) ValidateLabelOp(labelDef *models.LabelDefinition, repo *models.Repo, labelOp *models.LabelOp) error {
if labelDef == nil {
return fmt.Errorf("label definition is required")
}
+
if repo == nil {
+
return fmt.Errorf("repo is required")
+
}
if labelOp == nil {
return fmt.Errorf("label operation is required")
}
+
// validate permissions: only collaborators can apply labels currently
+
//
+
// TODO: introduce a repo:triage permission
+
ok, err := v.enforcer.IsPushAllowed(labelOp.Did, repo.Knot, repo.DidSlashRepo())
+
if err != nil {
+
return fmt.Errorf("failed to enforce permissions: %w", err)
+
}
+
if !ok {
+
return fmt.Errorf("unauhtorized label operation")
+
}
+
expectedKey := labelDef.AtUri().String()
if labelOp.OperandKey != expectedKey {
return fmt.Errorf("operand key %q does not match label definition URI %q", labelOp.OperandKey, expectedKey)
+4 -1
appview/validator/validator.go
···
"tangled.org/core/appview/db"
"tangled.org/core/appview/pages/markup"
"tangled.org/core/idresolver"
)
type Validator struct {
db *db.DB
sanitizer markup.Sanitizer
resolver *idresolver.Resolver
}
-
func New(db *db.DB, res *idresolver.Resolver) *Validator {
return &Validator{
db: db,
sanitizer: markup.NewSanitizer(),
resolver: res,
}
}
···
"tangled.org/core/appview/db"
"tangled.org/core/appview/pages/markup"
"tangled.org/core/idresolver"
+
"tangled.org/core/rbac"
)
type Validator struct {
db *db.DB
sanitizer markup.Sanitizer
resolver *idresolver.Resolver
+
enforcer *rbac.Enforcer
}
+
func New(db *db.DB, res *idresolver.Resolver, enforcer *rbac.Enforcer) *Validator {
return &Validator{
db: db,
sanitizer: markup.NewSanitizer(),
resolver: res,
+
enforcer: enforcer,
}
}