github.com/infraboard/keyauth@v0.8.1/client/interceptor/http.go (about)

     1  package interceptor
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"sync"
     8  
     9  	"github.com/gin-gonic/gin"
    10  	"github.com/infraboard/mcube/exception"
    11  	"github.com/infraboard/mcube/http/response"
    12  	"github.com/infraboard/mcube/logger"
    13  	"github.com/infraboard/mcube/logger/zap"
    14  	httpb "github.com/infraboard/mcube/pb/http"
    15  	"github.com/rs/xid"
    16  
    17  	"github.com/infraboard/keyauth/apps/micro"
    18  	"github.com/infraboard/keyauth/apps/permission"
    19  	"github.com/infraboard/keyauth/apps/token"
    20  	"github.com/infraboard/keyauth/apps/user/types"
    21  	"github.com/infraboard/keyauth/client"
    22  	"github.com/infraboard/keyauth/common/header"
    23  )
    24  
    25  type PermissionCheckMode int
    26  
    27  const (
    28  	// PRBAC_MODE 基于策略的权限校验
    29  	PRBAC_MODE PermissionCheckMode = 1
    30  	// ACL_MODE 基于用户类型的权限校验
    31  	ACL_MODE = 2
    32  )
    33  
    34  // NewInternalAuther 内部使用的auther
    35  func NewHTTPAuther(c *client.Client) *HTTPAuther {
    36  	return &HTTPAuther{
    37  		keyauth: c,
    38  		l:       zap.L().Named("Http Interceptor"),
    39  		mode:    PRBAC_MODE,
    40  		allows:  []string{},
    41  	}
    42  }
    43  
    44  // internal todo
    45  type HTTPAuther struct {
    46  	l       logger.Logger
    47  	keyauth *client.Client
    48  	mode    PermissionCheckMode
    49  	svr     *micro.Micro
    50  	lock    sync.Mutex
    51  	allows  []string
    52  }
    53  
    54  func (a *HTTPAuther) SetPermissionCheckMode(m PermissionCheckMode) {
    55  	a.mode = m
    56  }
    57  
    58  func (a *HTTPAuther) SetAllows(allows ...fmt.Stringer) {
    59  	for _, v := range allows {
    60  		a.allows = append(a.allows, v.String())
    61  	}
    62  }
    63  
    64  func (a *HTTPAuther) Auth(r *http.Request, entry httpb.Entry) (
    65  	authInfo interface{}, err error) {
    66  	var tk *token.Token
    67  
    68  	// 从请求中获取access token
    69  	acessToken := r.Header.Get(header.OAuthTokenHeader)
    70  
    71  	if entry.AuthEnable {
    72  		// 校验身份
    73  		tk, err = a.ValidateIdentity(r.Context(), acessToken)
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  
    78  		// namesapce检查
    79  		if entry.RequiredNamespace && tk.NamespaceId == "" {
    80  			return nil, exception.NewBadRequest("namespace required!")
    81  		}
    82  
    83  		// 权限检查
    84  		if entry.PermissionEnable {
    85  			err = a.CheckPermission(r.Context(), tk, entry)
    86  			if err != nil {
    87  				return nil, err
    88  			}
    89  		}
    90  	}
    91  
    92  	// 设置RequestID
    93  	if r.Header.Get(header.RequestIdHeader) == "" {
    94  		r.Header.Set(header.RequestIdHeader, xid.New().String())
    95  	}
    96  
    97  	return tk, nil
    98  }
    99  
   100  // Gin Auth Middleware
   101  func (a *HTTPAuther) GinAuthHandlerFunc() gin.HandlerFunc {
   102  	return func(c *gin.Context) {
   103  		// 从请求中获取access token
   104  		acessToken := c.GetHeader(header.OAuthTokenHeader)
   105  
   106  		// 校验身份
   107  		tk, err := a.ValidateIdentity(c.Request.Context(), acessToken)
   108  		if err != nil {
   109  			response.Failed(c.Writer, err)
   110  			return
   111  		}
   112  		c.Set("token", tk)
   113  		c.Next()
   114  	}
   115  }
   116  
   117  // Gin Perm Middleware
   118  func (a *HTTPAuther) PermHandlerFunc() gin.HandlerFunc {
   119  	return func(c *gin.Context) {
   120  		obj := c.MustGet("token")
   121  
   122  		tk, ok := obj.(*token.Token)
   123  		if !ok {
   124  			response.Failed(c.Writer, fmt.Errorf("auth middleware first"))
   125  			return
   126  		}
   127  
   128  		e := httpb.Entry{
   129  			Method: c.Request.Method,
   130  			Path:   c.FullPath(),
   131  			Allow:  a.allows,
   132  		}
   133  
   134  		// 权限检查
   135  		err := a.CheckPermission(c.Request.Context(), tk, e)
   136  		if err != nil {
   137  			response.Failed(c.Writer, err)
   138  			return
   139  		}
   140  		c.Next()
   141  	}
   142  }
   143  
   144  func (a *HTTPAuther) ValidateIdentity(ctx context.Context, accessToken string) (*token.Token, error) {
   145  	a.l.Debug("start token identity check ...")
   146  
   147  	if accessToken == "" {
   148  		return nil, exception.NewBadRequest("token required")
   149  	}
   150  
   151  	req := token.NewValidateTokenRequest()
   152  	req.AccessToken = accessToken
   153  	tk, err := a.keyauth.Token().ValidateToken(ctx, req)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	a.l.Debugf("token check ok, username: %s", tk.Account)
   159  	return tk, nil
   160  }
   161  
   162  func (a *HTTPAuther) CheckPermission(ctx context.Context, tk *token.Token, e httpb.Entry) error {
   163  	if tk == nil {
   164  		return exception.NewUnauthorized("validate permission need token")
   165  	}
   166  
   167  	// 如果是超级管理员不做权限校验, 直接放行
   168  	if tk.UserType.IsIn(types.UserType_SUPPER) {
   169  		a.l.Debugf("[%s] supper admin skip permission check!", tk.Account)
   170  		return nil
   171  	}
   172  
   173  	switch a.mode {
   174  	case ACL_MODE:
   175  		return a.ValidatePermissionByACL(ctx, tk, e)
   176  	case PRBAC_MODE:
   177  		return a.ValidatePermissionByPRBAC(ctx, tk, e)
   178  	default:
   179  		return fmt.Errorf("only support acl and prbac")
   180  	}
   181  }
   182  
   183  func (a *HTTPAuther) ValidatePermissionByACL(ctx context.Context, tk *token.Token, e httpb.Entry) error {
   184  	// 检查是否是允许的类型
   185  	if len(e.Allow) > 0 {
   186  		a.l.Debugf("[%s] start check permission to keyauth ...", tk.Account)
   187  		if !e.IsAllow(tk.UserType) {
   188  			return exception.NewPermissionDeny("no permission, allow: %s, but current: %s", e.Allow, tk.UserType)
   189  		}
   190  		a.l.Debugf("[%s] permission check passed", tk.Account)
   191  	}
   192  
   193  	return nil
   194  }
   195  
   196  func (a *HTTPAuther) ValidatePermissionByPRBAC(ctx context.Context, tk *token.Token, e httpb.Entry) error {
   197  	svr, err := a.GetClientService(ctx)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	req := permission.NewCheckPermissionRequest()
   203  	req.Account = tk.Account
   204  	req.NamespaceId = tk.NamespaceId
   205  	req.ServiceId = svr.Id
   206  	req.Path = e.UniquePath()
   207  	_, err = a.keyauth.Permission().CheckPermission(ctx, req)
   208  	if err != nil {
   209  		return exception.NewPermissionDeny(err.Error())
   210  	}
   211  	a.l.Debugf("[%s] permission check passed", tk.Account)
   212  	return nil
   213  }
   214  
   215  func (a *HTTPAuther) GetClientService(ctx context.Context) (*micro.Micro, error) {
   216  	if a.svr != nil {
   217  		return a.svr, nil
   218  	}
   219  	a.lock.Lock()
   220  	defer a.lock.Unlock()
   221  
   222  	req := micro.NewDescribeServiceRequestWithClientID(a.keyauth.GetClientID())
   223  	ins, err := a.keyauth.Micro().DescribeService(ctx, req)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	a.svr = ins
   228  	return ins, nil
   229  }
   230  
   231  // SetLogger todo
   232  func (a *HTTPAuther) SetLogger(l logger.Logger) {
   233  	a.l = l
   234  }