permit.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. package permit
  2. import (
  3. "net/url"
  4. mng "go-common/app/admin/main/manager/api"
  5. "go-common/library/ecode"
  6. "go-common/library/log"
  7. bm "go-common/library/net/http/blademaster"
  8. "go-common/library/net/metadata"
  9. "go-common/library/net/rpc/warden"
  10. "github.com/pkg/errors"
  11. )
  12. const (
  13. _verifyURI = "/api/session/verify"
  14. _permissionURI = "/x/admin/manager/permission"
  15. _sessIDKey = "_AJSESSIONID"
  16. _sessUIDKey = "uid" // manager user_id
  17. _sessUnKey = "username" // LDAP username
  18. _defaultDomain = ".bilibili.co"
  19. _defaultCookieName = "mng-go"
  20. _defaultCookieLifeTime = 2592000
  21. // CtxPermissions will be set into ctx.
  22. CtxPermissions = "permissions"
  23. )
  24. // permissions .
  25. type permissions struct {
  26. UID int64 `json:"uid"`
  27. Perms []string `json:"perms"`
  28. }
  29. // Permit is an auth middleware.
  30. type Permit struct {
  31. verifyURI string
  32. permissionURI string
  33. dashboardCaller string
  34. dsClient *bm.Client // dashboard client
  35. maClient *bm.Client // manager-admin client
  36. sm *SessionManager // user Session
  37. mng.PermitClient // mng grpc client
  38. }
  39. //Verify only export Verify function because of less configure
  40. type Verify interface {
  41. Verify() bm.HandlerFunc
  42. }
  43. // Config identify config.
  44. type Config struct {
  45. DsHTTPClient *bm.ClientConfig // dashboard client config. appkey can not reuse.
  46. MaHTTPClient *bm.ClientConfig // manager-admin client config
  47. Session *SessionConfig
  48. ManagerHost string
  49. DashboardHost string
  50. DashboardCaller string
  51. }
  52. // Config2 .
  53. type Config2 struct {
  54. MngClient *warden.ClientConfig
  55. Session *SessionConfig
  56. }
  57. // New new an auth service.
  58. func New(c *Config) *Permit {
  59. a := &Permit{
  60. dashboardCaller: c.DashboardCaller,
  61. verifyURI: c.DashboardHost + _verifyURI,
  62. permissionURI: c.ManagerHost + _permissionURI,
  63. dsClient: bm.NewClient(c.DsHTTPClient),
  64. maClient: bm.NewClient(c.MaHTTPClient),
  65. sm: newSessionManager(c.Session),
  66. }
  67. return a
  68. }
  69. // New2 .
  70. func New2(c *warden.ClientConfig) *Permit {
  71. permitClient, err := mng.NewClient(c)
  72. if err != nil {
  73. panic(errors.WithMessage(err, "Failed to dial mng rpc server"))
  74. }
  75. return &Permit{
  76. PermitClient: permitClient,
  77. sm: &SessionManager{},
  78. }
  79. }
  80. // NewVerify new a verify service.
  81. func NewVerify(c *Config) Verify {
  82. a := &Permit{
  83. verifyURI: c.DashboardHost + _verifyURI,
  84. dsClient: bm.NewClient(c.DsHTTPClient),
  85. dashboardCaller: c.DashboardCaller,
  86. sm: newSessionManager(c.Session),
  87. }
  88. return a
  89. }
  90. // Verify2 check whether the user has logged in.
  91. func (p *Permit) Verify2() bm.HandlerFunc {
  92. return func(ctx *bm.Context) {
  93. sid, username, err := p.login2(ctx)
  94. if err != nil {
  95. ctx.JSON(nil, ecode.Unauthorized)
  96. ctx.Abort()
  97. return
  98. }
  99. ctx.Set(_sessUnKey, username)
  100. p.sm.setHTTPCookie(ctx, _defaultCookieName, sid)
  101. }
  102. }
  103. // Verify return bm HandlerFunc which check whether the user has logged in.
  104. func (p *Permit) Verify() bm.HandlerFunc {
  105. return func(ctx *bm.Context) {
  106. si, err := p.login(ctx)
  107. if err != nil {
  108. ctx.JSON(nil, ecode.Unauthorized)
  109. ctx.Abort()
  110. return
  111. }
  112. p.sm.SessionRelease(ctx, si)
  113. }
  114. }
  115. // Permit return bm HandlerFunc which check whether the user has logged in and has the access permission of the location.
  116. // If `permit` is empty,it will allow any logged in request.
  117. func (p *Permit) Permit(permit string) bm.HandlerFunc {
  118. return func(ctx *bm.Context) {
  119. var (
  120. si *Session
  121. uid int64
  122. perms []string
  123. err error
  124. )
  125. si, err = p.login(ctx)
  126. if err != nil {
  127. ctx.JSON(nil, ecode.Unauthorized)
  128. ctx.Abort()
  129. return
  130. }
  131. defer p.sm.SessionRelease(ctx, si)
  132. uid, perms, err = p.permissions(ctx, si.Get(_sessUnKey).(string))
  133. if err == nil {
  134. si.Set(_sessUIDKey, uid)
  135. ctx.Set(_sessUIDKey, uid)
  136. if md, ok := metadata.FromContext(ctx); ok {
  137. md[metadata.Uid] = uid
  138. }
  139. }
  140. if len(perms) > 0 {
  141. ctx.Set(CtxPermissions, perms)
  142. }
  143. if !p.permit(permit, perms) {
  144. ctx.JSON(nil, ecode.AccessDenied)
  145. ctx.Abort()
  146. return
  147. }
  148. }
  149. }
  150. // login check whether the user has logged in.
  151. func (p *Permit) login(ctx *bm.Context) (si *Session, err error) {
  152. si = p.sm.SessionStart(ctx)
  153. if si.Get(_sessUnKey) == nil {
  154. var username string
  155. if username, err = p.verify(ctx); err != nil {
  156. return
  157. }
  158. si.Set(_sessUnKey, username)
  159. }
  160. ctx.Set(_sessUnKey, si.Get(_sessUnKey))
  161. if md, ok := metadata.FromContext(ctx); ok {
  162. md[metadata.Username] = si.Get(_sessUnKey)
  163. }
  164. return
  165. }
  166. // Permit2 same function as permit function but reply on grpc.
  167. func (p *Permit) Permit2(permit string) bm.HandlerFunc {
  168. return func(ctx *bm.Context) {
  169. sid, username, err := p.login2(ctx)
  170. if err != nil {
  171. ctx.JSON(nil, ecode.Unauthorized)
  172. ctx.Abort()
  173. return
  174. }
  175. p.sm.setHTTPCookie(ctx, _defaultCookieName, sid)
  176. ctx.Set(_sessUnKey, username)
  177. if md, ok := metadata.FromContext(ctx); ok {
  178. md[metadata.Username] = username
  179. }
  180. reply, err := p.Permissions(ctx, &mng.PermissionReq{Username: username})
  181. if err != nil {
  182. if ecode.NothingFound.Equal(err) && permit != "" {
  183. ctx.JSON(nil, ecode.AccessDenied)
  184. ctx.Abort()
  185. }
  186. return
  187. }
  188. ctx.Set(_sessUIDKey, reply.Uid)
  189. if md, ok := metadata.FromContext(ctx); ok {
  190. md[metadata.Uid] = reply.Uid
  191. }
  192. if len(reply.Perms) > 0 {
  193. ctx.Set(CtxPermissions, reply.Perms)
  194. }
  195. if !p.permit(permit, reply.Perms) {
  196. ctx.JSON(nil, ecode.AccessDenied)
  197. ctx.Abort()
  198. return
  199. }
  200. }
  201. }
  202. // login2 .
  203. func (p *Permit) login2(ctx *bm.Context) (sid, uname string, err error) {
  204. var dsbsid, mngsid string
  205. dsbck, err := ctx.Request.Cookie(_sessIDKey)
  206. if err == nil {
  207. dsbsid = dsbck.Value
  208. }
  209. if dsbsid == "" {
  210. err = ecode.Unauthorized
  211. return
  212. }
  213. mngck, err := ctx.Request.Cookie(_defaultCookieName)
  214. if err == nil {
  215. mngsid = mngck.Value
  216. }
  217. reply, err := p.Login(ctx, &mng.LoginReq{Mngsid: mngsid, Dsbsid: dsbsid})
  218. if err != nil {
  219. log.Error("mng rpc Login error(%v)", err)
  220. return
  221. }
  222. sid = reply.Sid
  223. uname = reply.Username
  224. return
  225. }
  226. func (p *Permit) verify(ctx *bm.Context) (username string, err error) {
  227. var (
  228. sid string
  229. r = ctx.Request
  230. )
  231. session, err := r.Cookie(_sessIDKey)
  232. if err == nil {
  233. sid = session.Value
  234. }
  235. if sid == "" {
  236. err = ecode.Unauthorized
  237. return
  238. }
  239. username, err = p.verifyDashboard(ctx, sid)
  240. return
  241. }
  242. // permit check whether user has the access permission of the location.
  243. func (p *Permit) permit(permit string, permissions []string) bool {
  244. if permit == "" {
  245. return true
  246. }
  247. for _, p := range permissions {
  248. if p == permit {
  249. // access the permit
  250. return true
  251. }
  252. }
  253. return false
  254. }
  255. // verifyDashboard check whether the user is valid from Dashboard.
  256. func (p *Permit) verifyDashboard(ctx *bm.Context, sid string) (username string, err error) {
  257. params := url.Values{}
  258. params.Set("session_id", sid)
  259. params.Set("encrypt", "md5")
  260. params.Set("caller", p.dashboardCaller)
  261. var res struct {
  262. Code int `json:"code"`
  263. UserName string `json:"username"`
  264. }
  265. if err = p.dsClient.Get(ctx, p.verifyURI, metadata.String(ctx, metadata.RemoteIP), params, &res); err != nil {
  266. log.Error("dashboard get verify Session url(%s) error(%v)", p.verifyURI+"?"+params.Encode(), err)
  267. return
  268. }
  269. if ecode.Int(res.Code) != ecode.OK {
  270. log.Error("dashboard get verify Session url(%s) error(%v)", p.verifyURI+"?"+params.Encode(), res.Code)
  271. err = ecode.Int(res.Code)
  272. return
  273. }
  274. username = res.UserName
  275. return
  276. }
  277. // permissions get user's permisssions from manager-admin.
  278. func (p *Permit) permissions(ctx *bm.Context, username string) (uid int64, perms []string, err error) {
  279. params := url.Values{}
  280. params.Set(_sessUnKey, username)
  281. var res struct {
  282. Code int `json:"code"`
  283. Data permissions `json:"data"`
  284. }
  285. if err = p.maClient.Get(ctx, p.permissionURI, metadata.String(ctx, metadata.RemoteIP), params, &res); err != nil {
  286. log.Error("dashboard get permissions url(%s) error(%v)", p.permissionURI+"?"+params.Encode(), err)
  287. return
  288. }
  289. if ecode.Int(res.Code) != ecode.OK {
  290. log.Error("dashboard get permissions url(%s) error(%v)", p.permissionURI+"?"+params.Encode(), res.Code)
  291. err = ecode.Int(res.Code)
  292. return
  293. }
  294. perms = res.Data.Perms
  295. uid = res.Data.UID
  296. return
  297. }