swirl/security/perm.go
2021-12-15 17:26:45 +08:00

108 lines
2.1 KiB
Go

package security
import (
"net/http"
"time"
"github.com/cuigh/auxo/cache"
"github.com/cuigh/auxo/log"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/biz"
)
type Authorizer struct {
ub biz.UserBiz
perms *cache.Value
logger log.Logger
}
func NewAuthorizer(ub biz.UserBiz, rb biz.RoleBiz) web.Filter {
v := cache.Value{
TTL: 5 * time.Minute,
Load: func() (interface{}, error) { return loadPerms(rb) },
}
return &Authorizer{
ub: ub,
perms: &v,
logger: log.Get("security"),
}
}
// Apply implements `web.Filter` interface.
func (a *Authorizer) Apply(next web.HandlerFunc) web.HandlerFunc {
return func(ctx web.Context) error {
auth := ctx.Handler().Authorize()
// allow anonymous
if auth == "" || auth == web.AuthAnonymous {
return next(ctx)
}
user := ctx.User()
if user == nil || user.Anonymous() {
return web.NewError(http.StatusUnauthorized, "You are not logged in")
}
if auth != web.AuthAuthenticated && !a.check(user, auth) {
return web.NewError(http.StatusForbidden, "You do not have access to this resource")
}
return next(ctx)
}
}
func (a *Authorizer) check(user web.User, auth string) bool {
u, err := a.ub.FindByID(user.ID())
if err != nil {
a.logger.Errorf("failed to query user '%s': %s", user.ID(), err)
return false
}
if u == nil || u.Status == biz.UserStatusBlocked {
return false
} else if u.Admin {
return true
} else if auth == web.AuthAdministrator {
return u.Admin
}
v, err := a.perms.Get(true)
if err != nil {
a.logger.Error("failed to load role perms: ", err)
return false
}
perms := v.(map[string]PermSet)
for _, r := range u.Roles {
if set, ok := perms[r]; ok {
if set.Contains(auth) {
return true
}
}
}
return false
}
func loadPerms(rb biz.RoleBiz) (interface{}, error) {
roles, err := rb.Search("")
if err != nil {
return nil, err
}
perms := make(map[string]PermSet)
for _, role := range roles {
set := make(PermSet)
for _, p := range role.Perms {
set[p] = struct{}{}
}
perms[role.ID] = set
}
return perms, nil
}
type PermSet map[string]struct{}
func (s PermSet) Contains(perm string) (ok bool) {
_, ok = s[perm]
return
}