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 }