package store
import (
bolt "go.etcd.io/bbolt"
)
// Role is a level of access granted to a principal on a repo.
type Role string
const (
RoleRead Role = "read"
RoleWrite Role = "write"
RoleAdmin Role = "admin"
)
func (r Role) Valid() bool {
switch r {
case RoleRead, RoleWrite, RoleAdmin:
return true
}
return false
}
// Collaborator grants a role to a principal (local or federated) on a repo.
type Collaborator struct {
Principal string `json:"principal"` // "<username>@<domain>"
Role Role `json:"role"`
}
// ACL is the access control list for a single repo.
type ACL struct {
Repo string `json:"repo"`
Owner string `json:"owner"`
Collaborators []Collaborator `json:"collaborators"`
}
func (s *Store) GetACL(repo string) (ACL, error) {
var a ACL
err := s.db.View(func(tx *bolt.Tx) error {
return getJSON(tx, bucketACL, repo, &a)
})
return a, err
}
func (s *Store) PutACL(a ACL) error {
return s.db.Update(func(tx *bolt.Tx) error {
return putJSON(tx, bucketACL, a.Repo, a)
})
}
// UpsertCollaborator adds or updates a collaborator's role on a repo's ACL.
func (s *Store) UpsertCollaborator(repo string, c Collaborator) error {
return s.db.Update(func(tx *bolt.Tx) error {
var a ACL
if err := getJSON(tx, bucketACL, repo, &a); err != nil {
if err != ErrNotFound {
return err
}
a = ACL{Repo: repo}
}
replaced := false
for i, existing := range a.Collaborators {
if existing.Principal == c.Principal {
a.Collaborators[i] = c
replaced = true
break
}
}
if !replaced {
a.Collaborators = append(a.Collaborators, c)
}
return putJSON(tx, bucketACL, repo, a)
})
}
// RemoveCollaborator removes a principal from a repo's ACL.
func (s *Store) RemoveCollaborator(repo, principal string) error {
return s.db.Update(func(tx *bolt.Tx) error {
var a ACL
if err := getJSON(tx, bucketACL, repo, &a); err != nil {
return err
}
kept := a.Collaborators[:0]
for _, c := range a.Collaborators {
if c.Principal != principal {
kept = append(kept, c)
}
}
a.Collaborators = kept
return putJSON(tx, bucketACL, repo, a)
})
}
func (s *Store) DeleteACL(repo string) error {
return s.db.Update(func(tx *bolt.Tx) error {
return deleteKey(tx, bucketACL, repo)
})
}