github.com/infraboard/keyauth@v0.8.1/apps/token/impl/token.go (about)

     1  package impl
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/infraboard/mcube/exception"
     9  	"github.com/infraboard/mcube/http/request"
    10  	"go.mongodb.org/mongo-driver/mongo"
    11  
    12  	"github.com/infraboard/keyauth/apps/application"
    13  	"github.com/infraboard/keyauth/apps/namespace"
    14  	"github.com/infraboard/keyauth/apps/policy"
    15  	"github.com/infraboard/keyauth/apps/session"
    16  	"github.com/infraboard/keyauth/apps/token"
    17  	"github.com/infraboard/keyauth/apps/user/types"
    18  	"github.com/infraboard/keyauth/apps/verifycode"
    19  )
    20  
    21  func (s *service) IssueToken(ctx context.Context, req *token.IssueTokenRequest) (*token.Token, error) {
    22  	// 连续登录失败检测
    23  	if err := s.loginBeforeCheck(ctx, req); err != nil {
    24  		return nil, exception.NewBadRequest("安全检测失败, %s", err)
    25  	}
    26  
    27  	// 颁发Token
    28  	tk, err := s.issuer.IssueToken(ctx, req)
    29  	if err != nil {
    30  		s.checker.UpdateFailedRetry(ctx, req)
    31  		return nil, err
    32  	}
    33  	tk.WithRemoteIP(req.GetRemoteIp())
    34  	tk.WithUerAgent(req.GetUserAgent())
    35  
    36  	// 安全登录检测
    37  	if err := s.securityCheck(ctx, req.VerifyCode, tk); err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	// 登录会话
    42  	if req.IsLoginRequest() {
    43  		sess, err := s.session.Login(ctx, tk)
    44  		if err != nil {
    45  			return nil, err
    46  		}
    47  		tk.SessionId = sess.Id
    48  		// 继承上次登录的Namespace
    49  		if tk.NamespaceId == "" {
    50  			tk.NamespaceId = sess.NamespaceId
    51  			tk.NamespaceName = sess.NamespaceName
    52  		}
    53  		// 如果还是没有设置, 使用默认空间
    54  		if tk.NamespaceId == "" {
    55  			s.setDefaultNamespace(ctx, tk)
    56  		}
    57  
    58  	}
    59  
    60  	// 保存入库
    61  	if err := s.saveToken(tk); err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return tk, nil
    66  }
    67  
    68  func (s *service) setDefaultNamespace(ctx context.Context, tk *token.Token) error {
    69  	req := namespace.NewQueryNamespaceRequest(request.NewDefaultPageRequest())
    70  	req.Domain = tk.Domain
    71  	req.Name = namespace.DefaultNamesapceName
    72  	nset, err := s.ns.QueryNamespace(ctx, req)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	if len(nset.Items) == 0 {
    77  		return fmt.Errorf("not default namespace")
    78  	}
    79  
    80  	ns := nset.Items[0]
    81  
    82  	tk.NamespaceId = ns.Id
    83  	tk.NamespaceName = ns.Name
    84  	return nil
    85  }
    86  
    87  func (s *service) loginBeforeCheck(ctx context.Context, req *token.IssueTokenRequest) error {
    88  	// 连续登录失败检测
    89  	if err := s.checker.MaxFailedRetryCheck(ctx, req); err != nil {
    90  		return exception.NewBadRequest("%s", err)
    91  	}
    92  
    93  	// IP保护检测
    94  	err := s.checker.IPProtectCheck(ctx, req)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	s.log.Debug("security check complete")
   100  	return nil
   101  }
   102  
   103  func (s *service) securityCheck(ctx context.Context, code string, tk *token.Token) error {
   104  	// 如果有校验码, 则直接通过校验码检测用户身份安全
   105  	if code != "" {
   106  		s.log.Debugf("verify code provided, check code ...")
   107  		_, err := s.code.CheckCode(ctx, verifycode.NewCheckCodeRequest(tk.Account, code))
   108  		if err != nil {
   109  			return exception.NewPermissionDeny("verify code invalidate, error, %s", err)
   110  		}
   111  		s.log.Debugf("verfiy code check passed")
   112  		return nil
   113  	}
   114  
   115  	// 异地登录检测
   116  	err := s.checker.OtherPlaceLoggedInChecK(ctx, tk)
   117  	if err != nil {
   118  		return exception.NewVerifyCodeRequiredError("异地登录检测失败: %s", err)
   119  	}
   120  
   121  	// 长时间未登录检测
   122  	err = s.checker.NotLoginDaysChecK(ctx, tk)
   123  	if err != nil {
   124  		return exception.NewVerifyCodeRequiredError("长时间未登录检测失败: %s", err)
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func (s *service) reuseToken(ctx context.Context, tk *token.Token) error {
   131  	// 刷新token过期的,不允许复用
   132  	if tk.CheckRefreshIsExpired() {
   133  		return exception.NewRefreshTokenExpired("refresh_token: %s expoired", tk.RefreshToken)
   134  	}
   135  
   136  	descApp := application.NewDescriptApplicationRequest()
   137  	descApp.Id = tk.ApplicationId
   138  	app, err := s.app.DescribeApplication(ctx, descApp)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	// access token延长一个过期周期
   143  	tk.AccessExpiredAt = time.Now().Add(time.Duration(app.AccessTokenExpireSecond)*time.Second).Unix() * 1000
   144  	// refresh token延长一个过期周期
   145  	tk.RefreshExpiredAt = time.Unix(tk.RefreshExpiredAt/1000, 0).Add(time.Duration(app.RefreshTokenExpireSecond)*time.Second).Unix() * 1000
   146  	return s.saveToken(tk)
   147  }
   148  
   149  func (s *service) ValidateToken(ctx context.Context, req *token.ValidateTokenRequest) (*token.Token, error) {
   150  	if err := req.Validate(); err != nil {
   151  		return nil, exception.NewBadRequest(err.Error())
   152  	}
   153  
   154  	tk, err := s.describeToken(newDescribeTokenRequest(req.MakeDescribeTokenRequest()))
   155  	if err != nil {
   156  		return nil, exception.NewUnauthorized(err.Error())
   157  	}
   158  
   159  	if tk.IsBlock {
   160  		return nil, s.makeBlockExcption(tk.BlockType, tk.BlockMessage())
   161  	}
   162  
   163  	// 校验Access Token是否过期
   164  	if req.AccessToken != "" {
   165  		if tk.CheckAccessIsExpired() {
   166  			// 如果Refresh还没有过期, 自动再续一个周期, 避免用户连续使用过程中导致访问中断
   167  			if err := s.reuseToken(ctx, tk); err != nil {
   168  				return nil, err
   169  			}
   170  		}
   171  	}
   172  
   173  	// 校验RefreshToken
   174  	if req.RefreshToken != "" {
   175  		if tk.CheckRefreshIsExpired() {
   176  			return nil, exception.NewRefreshTokenExpired("refresh_token: %s expoired", tk.RefreshToken)
   177  		}
   178  	}
   179  
   180  	tk.Desensitize()
   181  	return tk, nil
   182  }
   183  
   184  func (s *service) makeBlockExcption(bt token.BlockType, message string) exception.APIException {
   185  	switch bt {
   186  	case token.BlockType_OTHER_CLIENT_LOGGED_IN:
   187  		return exception.NewOtherClientsLoggedIn(message)
   188  	case token.BlockType_SESSION_TERMINATED:
   189  		return exception.NewSessionTerminated(message)
   190  	case token.BlockType_OTHER_PLACE_LOGGED_IN:
   191  		return exception.NewOtherPlaceLoggedIn(message)
   192  	case token.BlockType_OTHER_IP_LOGGED_IN:
   193  		return exception.NewOtherIPLoggedIn(message)
   194  	default:
   195  		return exception.NewInternalServerError("unknow block type: %s, message: %s", bt, message)
   196  	}
   197  }
   198  
   199  func (s *service) BlockToken(ctx context.Context, req *token.BlockTokenRequest) (*token.Token, error) {
   200  	tk, err := s.DescribeToken(ctx, token.NewDescribeTokenRequestWithAccessToken(req.AccessToken))
   201  	if err != nil {
   202  		return nil, fmt.Errorf("query session access token error, %s", err)
   203  	}
   204  
   205  	tk.IsBlock = true
   206  	tk.BlockType = req.BlockType
   207  	tk.BlockReason = req.BlockReason
   208  	tk.BlockAt = time.Now().UnixNano() / 1000000
   209  
   210  	if err := s.updateToken(tk); err != nil {
   211  		return nil, err
   212  	}
   213  	return tk, nil
   214  }
   215  
   216  func (s *service) ChangeNamespace(ctx context.Context, req *token.ChangeNamespaceRequest) (*token.Token, error) {
   217  	if err := req.Validate(); err != nil {
   218  		return nil, exception.NewBadRequest("validate change namespace error, %s", err)
   219  	}
   220  
   221  	tk, err := s.DescribeToken(ctx, token.NewDescribeTokenRequestWithAccessToken(req.Token))
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	_, err = s.ns.DescribeNamespace(ctx, namespace.NewNewDescriptNamespaceRequestWithID(req.Namespace))
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	if !tk.UserType.IsIn(types.UserType_DOMAIN_ADMIN, types.UserType_SUPPER) && !tk.HasNamespace(req.Namespace) {
   232  		return nil, exception.NewPermissionDeny("your has no permission to access namespace %s", req.Namespace)
   233  	}
   234  
   235  	tk.NamespaceId = req.Namespace
   236  	if err := s.updateToken(tk); err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	return tk, nil
   241  }
   242  
   243  func (s *service) DescribeToken(ctx context.Context, req *token.DescribeTokenRequest) (*token.Token, error) {
   244  	if err := req.Validate(); err != nil {
   245  		return nil, exception.NewBadRequest(err.Error())
   246  	}
   247  
   248  	tk, err := s.describeToken(newDescribeTokenRequest(req))
   249  	if err != nil {
   250  		return nil, exception.NewUnauthorized(err.Error())
   251  	}
   252  
   253  	// 查询用户可以访问的空间
   254  	query := policy.NewQueryPolicyRequest(request.NewPageRequest(policy.MaxUserPolicy, 1))
   255  	query.Account = tk.Account
   256  	ps, err := s.policy.QueryPolicy(ctx, query)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  	if ps.Total > policy.MaxUserPolicy {
   261  		s.log.Warnf("user policy large than max policy count %d, total: %d", policy.MaxUserPolicy, ps.Total)
   262  	}
   263  	tk.AvailableNamespace = ps.GetNamespace()
   264  	return tk, nil
   265  }
   266  
   267  func (s *service) QueryToken(ctx context.Context, req *token.QueryTokenRequest) (*token.Set, error) {
   268  	query := newQueryRequest(req)
   269  	resp, err := s.col.Find(context.TODO(), query.FindFilter(), query.FindOptions())
   270  
   271  	if err != nil {
   272  		return nil, exception.NewInternalServerError("find token error, error is %s", err)
   273  	}
   274  
   275  	tokenSet := token.NewTokenSet()
   276  	// 循环
   277  	for resp.Next(context.TODO()) {
   278  		tk := new(token.Token)
   279  		if err := resp.Decode(tk); err != nil {
   280  			return nil, exception.NewInternalServerError("decode token error, error is %s", err)
   281  		}
   282  		tokenSet.Add(tk)
   283  	}
   284  
   285  	// count
   286  	count, err := s.col.CountDocuments(context.TODO(), query.FindFilter())
   287  	if err != nil {
   288  		return nil, exception.NewInternalServerError("get token count error, error is %s", err)
   289  	}
   290  	tokenSet.Total = count
   291  
   292  	return tokenSet, nil
   293  
   294  }
   295  
   296  func (s *service) RevolkToken(ctx context.Context, req *token.RevolkTokenRequest) (*token.Token, error) {
   297  	if err := req.Validate(); err != nil {
   298  		return nil, exception.NewBadRequest(err.Error())
   299  	}
   300  
   301  	// 检测撤销token的客户端是否合法
   302  	app, err := s.issuer.CheckClient(ctx, req.ClientId, req.ClientSecret)
   303  	if err != nil {
   304  		return nil, exception.NewUnauthorized(err.Error())
   305  	}
   306  
   307  	// 检测被撤销token的合法性
   308  	descReq := newDescribeTokenRequest(req.MakeDescribeTokenRequest())
   309  	tk, err := s.describeToken(descReq)
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  
   314  	if err := tk.CheckTokenApplication(app.Id); err != nil {
   315  		return nil, exception.NewPermissionDeny(err.Error())
   316  	}
   317  
   318  	// 退出会话
   319  	if req.LogoutSession && tk.SessionId != "" {
   320  		logoutReq := session.NewLogoutRequest(tk.SessionId)
   321  		if _, err := s.session.Logout(ctx, logoutReq); err != nil {
   322  			return nil, exception.NewInternalServerError("logout session error, %s", err)
   323  		}
   324  	}
   325  
   326  	return tk, s.destoryToken(descReq)
   327  }
   328  
   329  func (s *service) destoryToken(req *describeTokenRequest) error {
   330  	resp, err := s.col.DeleteOne(context.TODO(), req.FindFilter())
   331  	if err != nil {
   332  		return exception.NewInternalServerError("delete token(%s) error, %s", req, err)
   333  	}
   334  
   335  	if resp.DeletedCount == 0 {
   336  		return exception.NewNotFound("token(%s) not found", req)
   337  	}
   338  
   339  	return nil
   340  }
   341  
   342  func (s *service) describeToken(req *describeTokenRequest) (*token.Token, error) {
   343  	tk := new(token.Token)
   344  
   345  	if err := s.col.FindOne(context.TODO(), req.FindFilter()).Decode(tk); err != nil {
   346  		if err == mongo.ErrNoDocuments {
   347  			return nil, exception.NewNotFound("token %s not found", req)
   348  		}
   349  
   350  		return nil, exception.NewInternalServerError("find token %s error, %s", req, err)
   351  	}
   352  
   353  	return tk, nil
   354  }
   355  
   356  func (s *service) DeleteToken(ctx context.Context, req *token.DeleteTokenRequest) (
   357  	*token.DeleteTokenResponse, error) {
   358  	if err := req.Validate(); err != nil {
   359  		return nil, err
   360  	}
   361  
   362  	deleteReq := newDeleteTokenRequest(req)
   363  	s.log.Debugf("delete token filter: %s", deleteReq.FindFilter())
   364  	resp, err := s.col.DeleteOne(context.TODO(), deleteReq.FindFilter())
   365  	if err != nil {
   366  		return nil, exception.NewInternalServerError("delete token(%s) error, %s", req, err)
   367  	}
   368  
   369  	if resp.DeletedCount == 0 {
   370  		return nil, exception.NewNotFound("token %s not found", req.AccessToken)
   371  	}
   372  
   373  	dr := token.NewDeleteTokenResponse()
   374  	dr.Message = fmt.Sprintf("delete %d token", resp.DeletedCount)
   375  	return dr, nil
   376  }