blob: 4eea40ace965ad3b73468ee6612a3bab06005152 [file] [log] [blame]
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package perm
// Features:
// - Resolves remote Group files if necessary.
// - Blocks mutations to Store until it has had a chance to prove that either
// there is no Group file and hence writes are free for all, or until the
// Group file has been fully loaded. This prevents a window of vulnerability
// where all writes would be allowed until the initial load is completed.
//
// TODOs:
// - Cache references so we don't need to retrieve the contents every time.
// - Poll more frequently if there is no control Group set up, so the StoreServer
// updates faster when creating a new one for the first time.
// - Poll more frequently if the DirServer is unreachable (speeds up boot time).
import (
"upspin.io/errors"
"upspin.io/upspin"
)
// WrapStore wraps the given StoreServer with a StoreServer that checks access
// permissions. It will only start polling the store permissions after the
// ready channel is closed.
func WrapStore(cfg upspin.Config, ready <-chan struct{}, store upspin.StoreServer) upspin.StoreServer {
const op errors.Op = "serverutil/perm.WrapStore"
p := newPerm(op, cfg, ready, cfg.UserName(), nil, nil, noop, retry, nil)
return p.WrapStore(store)
}
// WrapStore wraps the given StoreServer with a StoreServer that checks access
// permissions using Perm.
func (p *Perm) WrapStore(store upspin.StoreServer) upspin.StoreServer {
return &storeWrapper{
StoreServer: store,
user: p.cfg.UserName(),
perm: p,
}
}
// storeWrapper performs permission checking for StoreServer implementations.
type storeWrapper struct {
upspin.StoreServer
user upspin.UserName // set by Dial
perm *Perm
}
// Put implements upspin.StoreServer.
func (s *storeWrapper) Put(data []byte) (*upspin.Refdata, error) {
const op errors.Op = "store/perm.Put"
if !s.perm.IsWriter(s.user) {
return nil, errors.E(op, s.user, errors.Permission, errors.Errorf("user not authorized"))
}
return s.StoreServer.Put(data)
}
// Delete implements upspin.StoreServer.
func (s *storeWrapper) Delete(ref upspin.Reference) error {
const op errors.Op = "store/perm.Delete"
if s.perm.targetUser != s.user {
return errors.E(op, s.user, errors.Permission, errors.Errorf("user not authorized"))
}
return s.StoreServer.Delete(ref)
}
// Dial implements upspin.Service.
func (s *storeWrapper) Dial(cfg upspin.Config, e upspin.Endpoint) (upspin.Service, error) {
const op errors.Op = "store/perm.Dial"
service, err := s.StoreServer.Dial(cfg, e)
if err != nil {
return nil, errors.E(op, err)
}
newS := *s
newS.user = cfg.UserName()
newS.StoreServer = service.(upspin.StoreServer)
return &newS, nil
}