You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
8.1 KiB
321 lines
8.1 KiB
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 |
|
}
|
|
|