322 lines
8.1 KiB
Go
322 lines
8.1 KiB
Go
package permit
|
|
|
|
import (
|
|
"net/url"
|
|
|
|
mng "go-common/app/admin/main/manager/api"
|
|
"go-common/library/ecode"
|
|
"go-common/library/log"
|
|
bm "go-common/library/net/http/blademaster"
|
|
"go-common/library/net/metadata"
|
|
"go-common/library/net/rpc/warden"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
_verifyURI = "/api/session/verify"
|
|
_permissionURI = "/x/admin/manager/permission"
|
|
_sessIDKey = "_AJSESSIONID"
|
|
_sessUIDKey = "uid" // manager user_id
|
|
_sessUnKey = "username" // LDAP username
|
|
_defaultDomain = ".bilibili.co"
|
|
_defaultCookieName = "mng-go"
|
|
_defaultCookieLifeTime = 2592000
|
|
// CtxPermissions will be set into ctx.
|
|
CtxPermissions = "permissions"
|
|
)
|
|
|
|
// permissions .
|
|
type permissions struct {
|
|
UID int64 `json:"uid"`
|
|
Perms []string `json:"perms"`
|
|
}
|
|
|
|
// Permit is an auth middleware.
|
|
type Permit struct {
|
|
verifyURI string
|
|
permissionURI string
|
|
dashboardCaller string
|
|
dsClient *bm.Client // dashboard client
|
|
maClient *bm.Client // manager-admin client
|
|
|
|
sm *SessionManager // user Session
|
|
|
|
mng.PermitClient // mng grpc client
|
|
}
|
|
|
|
//Verify only export Verify function because of less configure
|
|
type Verify interface {
|
|
Verify() bm.HandlerFunc
|
|
}
|
|
|
|
// Config identify config.
|
|
type Config struct {
|
|
DsHTTPClient *bm.ClientConfig // dashboard client config. appkey can not reuse.
|
|
MaHTTPClient *bm.ClientConfig // manager-admin client config
|
|
Session *SessionConfig
|
|
ManagerHost string
|
|
DashboardHost string
|
|
DashboardCaller string
|
|
}
|
|
|
|
// Config2 .
|
|
type Config2 struct {
|
|
MngClient *warden.ClientConfig
|
|
Session *SessionConfig
|
|
}
|
|
|
|
// New new an auth service.
|
|
func New(c *Config) *Permit {
|
|
a := &Permit{
|
|
dashboardCaller: c.DashboardCaller,
|
|
verifyURI: c.DashboardHost + _verifyURI,
|
|
permissionURI: c.ManagerHost + _permissionURI,
|
|
dsClient: bm.NewClient(c.DsHTTPClient),
|
|
maClient: bm.NewClient(c.MaHTTPClient),
|
|
sm: newSessionManager(c.Session),
|
|
}
|
|
return a
|
|
}
|
|
|
|
// New2 .
|
|
func New2(c *warden.ClientConfig) *Permit {
|
|
permitClient, err := mng.NewClient(c)
|
|
if err != nil {
|
|
panic(errors.WithMessage(err, "Failed to dial mng rpc server"))
|
|
}
|
|
return &Permit{
|
|
PermitClient: permitClient,
|
|
sm: &SessionManager{},
|
|
}
|
|
}
|
|
|
|
// NewVerify new a verify service.
|
|
func NewVerify(c *Config) Verify {
|
|
a := &Permit{
|
|
verifyURI: c.DashboardHost + _verifyURI,
|
|
dsClient: bm.NewClient(c.DsHTTPClient),
|
|
dashboardCaller: c.DashboardCaller,
|
|
sm: newSessionManager(c.Session),
|
|
}
|
|
return a
|
|
}
|
|
|
|
// Verify2 check whether the user has logged in.
|
|
func (p *Permit) Verify2() bm.HandlerFunc {
|
|
return func(ctx *bm.Context) {
|
|
sid, username, err := p.login2(ctx)
|
|
if err != nil {
|
|
ctx.JSON(nil, ecode.Unauthorized)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
ctx.Set(_sessUnKey, username)
|
|
p.sm.setHTTPCookie(ctx, _defaultCookieName, sid)
|
|
}
|
|
}
|
|
|
|
// Verify return bm HandlerFunc which check whether the user has logged in.
|
|
func (p *Permit) Verify() bm.HandlerFunc {
|
|
return func(ctx *bm.Context) {
|
|
si, err := p.login(ctx)
|
|
if err != nil {
|
|
ctx.JSON(nil, ecode.Unauthorized)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
p.sm.SessionRelease(ctx, si)
|
|
}
|
|
}
|
|
|
|
// Permit return bm HandlerFunc which check whether the user has logged in and has the access permission of the location.
|
|
// If `permit` is empty,it will allow any logged in request.
|
|
func (p *Permit) Permit(permit string) bm.HandlerFunc {
|
|
return func(ctx *bm.Context) {
|
|
var (
|
|
si *Session
|
|
uid int64
|
|
perms []string
|
|
err error
|
|
)
|
|
si, err = p.login(ctx)
|
|
if err != nil {
|
|
ctx.JSON(nil, ecode.Unauthorized)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
defer p.sm.SessionRelease(ctx, si)
|
|
uid, perms, err = p.permissions(ctx, si.Get(_sessUnKey).(string))
|
|
if err == nil {
|
|
si.Set(_sessUIDKey, uid)
|
|
ctx.Set(_sessUIDKey, uid)
|
|
if md, ok := metadata.FromContext(ctx); ok {
|
|
md[metadata.Uid] = uid
|
|
}
|
|
}
|
|
if len(perms) > 0 {
|
|
ctx.Set(CtxPermissions, perms)
|
|
}
|
|
if !p.permit(permit, perms) {
|
|
ctx.JSON(nil, ecode.AccessDenied)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// login check whether the user has logged in.
|
|
func (p *Permit) login(ctx *bm.Context) (si *Session, err error) {
|
|
si = p.sm.SessionStart(ctx)
|
|
if si.Get(_sessUnKey) == nil {
|
|
var username string
|
|
if username, err = p.verify(ctx); err != nil {
|
|
return
|
|
}
|
|
si.Set(_sessUnKey, username)
|
|
}
|
|
ctx.Set(_sessUnKey, si.Get(_sessUnKey))
|
|
if md, ok := metadata.FromContext(ctx); ok {
|
|
md[metadata.Username] = si.Get(_sessUnKey)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Permit2 same function as permit function but reply on grpc.
|
|
func (p *Permit) Permit2(permit string) bm.HandlerFunc {
|
|
return func(ctx *bm.Context) {
|
|
sid, username, err := p.login2(ctx)
|
|
if err != nil {
|
|
ctx.JSON(nil, ecode.Unauthorized)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
p.sm.setHTTPCookie(ctx, _defaultCookieName, sid)
|
|
ctx.Set(_sessUnKey, username)
|
|
if md, ok := metadata.FromContext(ctx); ok {
|
|
md[metadata.Username] = username
|
|
}
|
|
reply, err := p.Permissions(ctx, &mng.PermissionReq{Username: username})
|
|
if err != nil {
|
|
if ecode.NothingFound.Equal(err) && permit != "" {
|
|
ctx.JSON(nil, ecode.AccessDenied)
|
|
ctx.Abort()
|
|
}
|
|
return
|
|
}
|
|
ctx.Set(_sessUIDKey, reply.Uid)
|
|
if md, ok := metadata.FromContext(ctx); ok {
|
|
md[metadata.Uid] = reply.Uid
|
|
}
|
|
if len(reply.Perms) > 0 {
|
|
ctx.Set(CtxPermissions, reply.Perms)
|
|
}
|
|
if !p.permit(permit, reply.Perms) {
|
|
ctx.JSON(nil, ecode.AccessDenied)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// login2 .
|
|
func (p *Permit) login2(ctx *bm.Context) (sid, uname string, err error) {
|
|
var dsbsid, mngsid string
|
|
dsbck, err := ctx.Request.Cookie(_sessIDKey)
|
|
if err == nil {
|
|
dsbsid = dsbck.Value
|
|
}
|
|
if dsbsid == "" {
|
|
err = ecode.Unauthorized
|
|
return
|
|
}
|
|
mngck, err := ctx.Request.Cookie(_defaultCookieName)
|
|
if err == nil {
|
|
mngsid = mngck.Value
|
|
}
|
|
reply, err := p.Login(ctx, &mng.LoginReq{Mngsid: mngsid, Dsbsid: dsbsid})
|
|
if err != nil {
|
|
log.Error("mng rpc Login error(%v)", err)
|
|
return
|
|
}
|
|
sid = reply.Sid
|
|
uname = reply.Username
|
|
return
|
|
}
|
|
|
|
func (p *Permit) verify(ctx *bm.Context) (username string, err error) {
|
|
var (
|
|
sid string
|
|
r = ctx.Request
|
|
)
|
|
session, err := r.Cookie(_sessIDKey)
|
|
if err == nil {
|
|
sid = session.Value
|
|
}
|
|
if sid == "" {
|
|
err = ecode.Unauthorized
|
|
return
|
|
}
|
|
username, err = p.verifyDashboard(ctx, sid)
|
|
return
|
|
}
|
|
|
|
// permit check whether user has the access permission of the location.
|
|
func (p *Permit) permit(permit string, permissions []string) bool {
|
|
if permit == "" {
|
|
return true
|
|
}
|
|
for _, p := range permissions {
|
|
if p == permit {
|
|
// access the permit
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// verifyDashboard check whether the user is valid from Dashboard.
|
|
func (p *Permit) verifyDashboard(ctx *bm.Context, sid string) (username string, err error) {
|
|
params := url.Values{}
|
|
params.Set("session_id", sid)
|
|
params.Set("encrypt", "md5")
|
|
params.Set("caller", p.dashboardCaller)
|
|
var res struct {
|
|
Code int `json:"code"`
|
|
UserName string `json:"username"`
|
|
}
|
|
if err = p.dsClient.Get(ctx, p.verifyURI, metadata.String(ctx, metadata.RemoteIP), params, &res); err != nil {
|
|
log.Error("dashboard get verify Session url(%s) error(%v)", p.verifyURI+"?"+params.Encode(), err)
|
|
return
|
|
}
|
|
if ecode.Int(res.Code) != ecode.OK {
|
|
log.Error("dashboard get verify Session url(%s) error(%v)", p.verifyURI+"?"+params.Encode(), res.Code)
|
|
err = ecode.Int(res.Code)
|
|
return
|
|
}
|
|
username = res.UserName
|
|
return
|
|
}
|
|
|
|
// permissions get user's permisssions from manager-admin.
|
|
func (p *Permit) permissions(ctx *bm.Context, username string) (uid int64, perms []string, err error) {
|
|
params := url.Values{}
|
|
params.Set(_sessUnKey, username)
|
|
var res struct {
|
|
Code int `json:"code"`
|
|
Data permissions `json:"data"`
|
|
}
|
|
if err = p.maClient.Get(ctx, p.permissionURI, metadata.String(ctx, metadata.RemoteIP), params, &res); err != nil {
|
|
log.Error("dashboard get permissions url(%s) error(%v)", p.permissionURI+"?"+params.Encode(), err)
|
|
return
|
|
}
|
|
if ecode.Int(res.Code) != ecode.OK {
|
|
log.Error("dashboard get permissions url(%s) error(%v)", p.permissionURI+"?"+params.Encode(), res.Code)
|
|
err = ecode.Int(res.Code)
|
|
return
|
|
}
|
|
perms = res.Data.Perms
|
|
uid = res.Data.UID
|
|
return
|
|
}
|