2018-02-24 04:26:38 +00:00
|
|
|
package security
|
2017-09-26 12:50:09 +00:00
|
|
|
|
2018-02-24 04:26:38 +00:00
|
|
|
import (
|
2021-12-06 12:24:22 +00:00
|
|
|
"net/http"
|
|
|
|
"time"
|
2018-02-24 04:26:38 +00:00
|
|
|
|
2021-12-06 12:24:22 +00:00
|
|
|
"github.com/cuigh/auxo/cache"
|
|
|
|
"github.com/cuigh/auxo/log"
|
2018-02-24 04:26:38 +00:00
|
|
|
"github.com/cuigh/auxo/net/web"
|
|
|
|
"github.com/cuigh/swirl/biz"
|
|
|
|
)
|
|
|
|
|
2021-12-06 12:24:22 +00:00
|
|
|
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"),
|
2018-02-24 04:26:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 12:24:22 +00:00
|
|
|
// Apply implements `web.Filter` interface.
|
|
|
|
func (a *Authorizer) Apply(next web.HandlerFunc) web.HandlerFunc {
|
2018-02-24 04:26:38 +00:00
|
|
|
return func(ctx web.Context) error {
|
2021-12-06 12:24:22 +00:00
|
|
|
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")
|
2018-02-24 04:26:38 +00:00
|
|
|
}
|
|
|
|
return next(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 12:24:22 +00:00
|
|
|
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
|
2017-09-26 12:50:09 +00:00
|
|
|
}
|
|
|
|
|
2021-12-06 12:24:22 +00:00
|
|
|
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
|
2017-09-26 12:50:09 +00:00
|
|
|
}
|
|
|
|
|
2021-12-06 12:24:22 +00:00
|
|
|
type PermSet map[string]struct{}
|
|
|
|
|
|
|
|
func (s PermSet) Contains(perm string) (ok bool) {
|
|
|
|
_, ok = s[perm]
|
|
|
|
return
|
2017-09-26 12:50:09 +00:00
|
|
|
}
|