feat(auth): add user access filtering rules

This commit is contained in:
2020-08-09 11:59:03 +02:00
parent a30d6b76d3
commit f55bce9ee2
8 changed files with 174 additions and 1 deletions

71
internal/auth/auth.go Normal file
View File

@ -0,0 +1,71 @@
package auth
import (
"sync"
"forge.cadoles.com/Cadoles/guesstimate/internal/model"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/pkg/errors"
)
var (
ErrUnexpectedRuleResult = errors.New("unexpected rule result")
)
type Service struct {
rules []*vm.Program
mutex sync.RWMutex
}
func (s *Service) LoadRules(rawRules ...string) error {
rules := make([]*vm.Program, 0, len(rawRules))
for _, rr := range rawRules {
r, err := expr.Compile(rr)
if err != nil {
return errors.WithStack(err)
}
rules = append(rules, r)
}
s.mutex.Lock()
s.rules = rules
s.mutex.Unlock()
return nil
}
func (s *Service) Authorize(user *model.User) (bool, error) {
s.mutex.RLock()
defer s.mutex.RUnlock()
env := map[string]interface{}{
"user": user,
}
for _, r := range s.rules {
result, err := expr.Run(r, env)
if err != nil {
return false, errors.WithStack(err)
}
authorized, ok := result.(bool)
if !ok {
return false, errors.WithStack(ErrUnexpectedRuleResult)
}
if !authorized {
return false, nil
}
}
return true, nil
}
func NewService() *Service {
return &Service{
rules: make([]*vm.Program, 0),
}
}

20
internal/auth/provider.go Normal file
View File

@ -0,0 +1,20 @@
package auth
import (
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/service"
)
func ServiceProvider(rules []string) service.Provider {
srv := NewService()
err := srv.LoadRules(rules...)
return func(ctn *service.Container) (interface{}, error) {
if err != nil {
return nil, errors.WithStack(err)
}
return srv, nil
}
}

33
internal/auth/service.go Normal file
View File

@ -0,0 +1,33 @@
package auth
import (
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/service"
)
const ServiceName service.Name = "auth"
// From retrieves the auth service in the given container.
func From(container *service.Container) (*Service, error) {
service, err := container.Service(ServiceName)
if err != nil {
return nil, errors.Wrapf(err, "error while retrieving '%s' service", ServiceName)
}
srv, ok := service.(*Service)
if !ok {
return nil, errors.Errorf("retrieved service is not a valid '%s' service", ServiceName)
}
return srv, nil
}
// Must retrieves the auth service in the given container or panic otherwise.
func Must(container *service.Container) *Service {
srv, err := From(container)
if err != nil {
panic(err)
}
return srv
}