github.com/infraboard/keyauth@v0.8.1/apps/token/issuer/issuer.go (about) 1 package issuer 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "regexp" 8 "strings" 9 "time" 10 11 "github.com/infraboard/mcube/app" 12 "github.com/infraboard/mcube/exception" 13 "github.com/infraboard/mcube/http/request" 14 "github.com/infraboard/mcube/logger" 15 "github.com/infraboard/mcube/logger/zap" 16 "github.com/rs/xid" 17 18 wechatWork "github.com/infraboard/keyauth/apps/wxwork" 19 20 "github.com/infraboard/keyauth/apps/application" 21 "github.com/infraboard/keyauth/apps/domain" 22 "github.com/infraboard/keyauth/apps/provider" 23 "github.com/infraboard/keyauth/apps/provider/auth/ldap" 24 "github.com/infraboard/keyauth/apps/provider/auth/wxwork" 25 "github.com/infraboard/keyauth/apps/token" 26 "github.com/infraboard/keyauth/apps/user" 27 "github.com/infraboard/keyauth/apps/user/types" 28 "github.com/infraboard/keyauth/common/password" 29 ) 30 31 // NewTokenIssuer todo 32 func NewTokenIssuer() (Issuer, error) { 33 issuer := &issuer{ 34 user: app.GetGrpcApp(user.AppName).(user.ServiceServer), 35 domain: app.GetGrpcApp(domain.AppName).(domain.ServiceServer), 36 token: app.GetGrpcApp(token.AppName).(token.ServiceServer), 37 ldap: app.GetInternalApp(provider.AppName).(provider.LDAP), 38 wechatWork: app.GetInternalApp(wechatWork.AppName).(wechatWork.WechatWork), 39 app: app.GetGrpcApp(application.AppName).(application.ServiceServer), 40 emailRE: regexp.MustCompile(`([a-zA-Z0-9]+)@([a-zA-Z0-9\.]+)\.([a-zA-Z0-9]+)`), 41 log: zap.L().Named("Token Issuer"), 42 } 43 return issuer, nil 44 } 45 46 // TokenIssuer 基于该数据进行扩展 47 type issuer struct { 48 app application.ServiceServer 49 token token.ServiceServer 50 user user.ServiceServer 51 domain domain.ServiceServer 52 ldap provider.LDAP 53 wechatWork wechatWork.WechatWork 54 emailRE *regexp.Regexp 55 log logger.Logger 56 } 57 58 func (i *issuer) checkUserPass(ctx context.Context, user, pass string) (*user.User, error) { 59 u, err := i.getUser(ctx, user) 60 if err != nil { 61 return nil, err 62 } 63 64 if err := u.HashedPassword.CheckPassword(pass); err != nil { 65 return nil, err 66 } 67 return u, nil 68 } 69 70 func (i *issuer) checkUserPassExpired(ctx context.Context, u *user.User) error { 71 d, err := i.getDomain(ctx, u) 72 if err != nil { 73 return err 74 } 75 76 // 检测密码是否过期 77 err = d.SecuritySetting.PasswordSecurity.IsPasswordExpired(u.HashedPassword) 78 if err != nil { 79 return err 80 } 81 82 return nil 83 } 84 85 func (i *issuer) getUser(ctx context.Context, name string) (*user.User, error) { 86 req := user.NewDescriptAccountRequest() 87 req.Account = name 88 return i.user.DescribeAccount(ctx, req) 89 } 90 91 func (i *issuer) getDomain(ctx context.Context, u *user.User) (*domain.Domain, error) { 92 req := domain.NewDescribeDomainRequestWithName(u.Domain) 93 return i.domain.DescribeDomain(ctx, req) 94 } 95 96 func (i *issuer) setTokenDomain(ctx context.Context, tk *token.Token) error { 97 // 获取最近1个 98 req := domain.NewQueryDomainRequest(request.NewPageRequest(1, 1)) 99 domains, err := i.domain.QueryDomain(ctx, req) 100 if err != nil { 101 return err 102 } 103 104 if domains.Length() > 0 { 105 tk.Domain = domains.Items[0].Name 106 } 107 108 return nil 109 } 110 111 // IssueToken 颁发token 112 func (i *issuer) IssueToken(ctx context.Context, req *token.IssueTokenRequest) (*token.Token, error) { 113 if err := req.Validate(); err != nil { 114 return nil, err 115 } 116 117 // 校验应用端的合法性 118 app, err := i.CheckClient(ctx, req.ClientId, req.ClientSecret) 119 if err != nil { 120 return nil, exception.NewUnauthorized(err.Error()) 121 } 122 123 // 校验用户的身份 124 switch req.GrantType { 125 case token.GrantType_PASSWORD: 126 // 判断用户的密码是否正确 127 u, checkErr := i.checkUserPass(ctx, req.Username, req.Password) 128 if checkErr != nil { 129 i.log.Debugf("issue password token error, %s", checkErr) 130 return nil, exception.NewUnauthorized("user or password not connrect") 131 } 132 133 // 校验用的密码是否过期 134 if err := i.checkUserPassExpired(ctx, u); err != nil { 135 i.log.Debugf("issue password token error, %s", err) 136 if v, ok := err.(exception.APIException); ok { 137 v.WithData(u.Account) 138 } 139 return nil, err 140 } 141 142 // 颁发给用户一个token 143 tk := i.issueUserToken(app, u, token.GrantType_PASSWORD) 144 switch u.Type { 145 case types.UserType_SUPPER, types.UserType_PRIMARY: 146 err := i.setTokenDomain(ctx, tk) 147 if err != nil { 148 return nil, fmt.Errorf("set token domain error, %s", err) 149 } 150 tk.Domain = u.Domain 151 default: 152 tk.Domain = u.Domain 153 } 154 155 return tk, nil 156 case token.GrantType_REFRESH: 157 validateReq := token.NewValidateTokenRequest() 158 validateReq.RefreshToken = req.RefreshToken 159 tk, err := i.token.ValidateToken(context.Background(), validateReq) 160 if err != nil { 161 return nil, err 162 } 163 if tk.AccessToken != req.AccessToken { 164 return nil, exception.NewPermissionDeny("refresh_token's access_tken not connrect") 165 } 166 167 u, err := i.getUser(ctx, tk.Account) 168 if err != nil { 169 return nil, err 170 } 171 newTK := i.issueUserToken(app, u, token.GrantType_REFRESH) 172 newTK.Domain = tk.Domain 173 newTK.StartGrantType = tk.GetStartGrantType() 174 newTK.SessionId = tk.SessionId 175 newTK.NamespaceId = tk.NamespaceId 176 177 revolkReq := token.NewRevolkTokenRequest(app.ClientId, app.ClientSecret) 178 revolkReq.AccessToken = req.AccessToken 179 revolkReq.LogoutSession = false 180 181 if _, err := i.token.RevolkToken(ctx, revolkReq); err != nil { 182 return nil, err 183 } 184 return newTK, nil 185 case token.GrantType_ACCESS: 186 validateReq := token.NewValidateTokenRequest() 187 validateReq.AccessToken = req.AccessToken 188 tk, err := i.token.ValidateToken(context.Background(), validateReq) 189 if err != nil { 190 return nil, exception.NewUnauthorized(err.Error()) 191 } 192 193 u, err := i.getUser(ctx, tk.Account) 194 if err != nil { 195 return nil, err 196 } 197 newTK := i.issueUserToken(app, u, token.GrantType_ACCESS) 198 newTK.Domain = tk.Domain 199 newTK.AccessExpiredAt = req.AccessExpiredAt 200 newTK.RefreshExpiredAt = 4 * req.AccessExpiredAt 201 newTK.Description = req.Description 202 return newTK, nil 203 case token.GrantType_LDAP: 204 userName, dn, err := i.genBaseDN(req.Username) 205 if err != nil { 206 return nil, err 207 } 208 209 descReq := provider.NewDescribeLDAPConfigWithBaseDN(dn) 210 ldapConf, err := i.ldap.DescribeConfig(descReq) 211 if err != nil { 212 return nil, err 213 } 214 pv := ldap.NewProvider(ldapConf.Config) 215 ok, err := pv.CheckUserPassword(userName, req.Password) 216 if err != nil { 217 return nil, err 218 } 219 if !ok { 220 return nil, exception.NewUnauthorized("用户名或者密码不对") 221 } 222 u, err := i.syncLDAPUser(ctx, req.Username) 223 if err != nil { 224 return nil, err 225 } 226 newTK := i.issueUserToken(app, u, token.GrantType_LDAP) 227 newTK.Domain = ldapConf.Domain 228 return newTK, nil 229 case token.GrantType_WECHAT_WORK: 230 ww, err := i.wechatWork.DescribeConfig(&wechatWork.DescribeWechatWorkConf{Domain: req.Username}) 231 if err != nil { 232 return nil, err 233 } 234 if req.State != ww.State { 235 return nil, errors.New("State is error! ") 236 } 237 np := wxwork.NewAuth(ww.CorpID, ww.CorpSecret, ww.AgentID) 238 userID, err := np.CheckCallBack(&wxwork.ScanCodeRequest{ 239 Code: req.AuthCode, 240 State: req.State, 241 AppID: req.UserAgent, 242 Service: req.Service, 243 }) 244 if err != nil { 245 return nil, err 246 } 247 wxUser := np.GetUserInfo(userID) 248 u, err := i.syncWXWORKUser(ctx, &user.User{ 249 Account: wxUser.Name, 250 Domain: ww.Domain, 251 Profile: &user.Profile{ 252 RealName: wxUser.UserID, 253 NickName: wxUser.Name, 254 Avatar: wxUser.Avatar, 255 Email: wxUser.Email, 256 Phone: wxUser.Mobile, 257 }, 258 }) 259 if err != nil { 260 return nil, err 261 } 262 newTK := i.issueUserToken(app, u, token.GrantType_WECHAT_WORK) 263 return newTK, nil 264 265 case token.GrantType_CLIENT: 266 return nil, exception.NewInternalServerError("not impl") 267 case token.GrantType_AUTH_CODE: 268 return nil, exception.NewInternalServerError("not impl") 269 default: 270 return nil, exception.NewInternalServerError("unknown grant type %s", req.GrantType) 271 } 272 } 273 274 func (i *issuer) genBaseDN(username string) (string, string, error) { 275 match := i.emailRE.FindAllStringSubmatch(username, -1) 276 if len(match) == 0 { 277 return "", "", exception.NewBadRequest("ldap user name must like username@company.com") 278 } 279 280 sub := match[0] 281 if len(sub) < 4 { 282 return "", "", exception.NewBadRequest("ldap user name must like username@company.com") 283 } 284 285 dns := []string{} 286 for _, dn := range sub[2:] { 287 dns = append(dns, "dc="+dn) 288 } 289 290 return sub[1], strings.Join(dns, ","), nil 291 } 292 293 func (i *issuer) syncLDAPUser(ctx context.Context, userName string) (*user.User, error) { 294 descUser := user.NewDescriptAccountRequestWithAccount(userName) 295 u, err := i.user.DescribeAccount(ctx, descUser) 296 297 if u != nil && u.Type.IsIn(types.UserType_PRIMARY, types.UserType_SUPPER) { 298 return nil, exception.NewBadRequest("用户名和主账号用户名冲突, 请修改") 299 } 300 301 if err != nil { 302 if exception.IsNotFoundError(err) { 303 req := user.NewCreateUserRequestWithLDAPSync(userName, i.randomPass()) 304 req.UserType = types.UserType_SUB 305 u, err = i.user.CreateAccount(ctx, req) 306 if err != nil { 307 return nil, err 308 } 309 } 310 return u, err 311 } 312 313 return u, nil 314 } 315 316 func (i *issuer) syncWXWORKUser(ctx context.Context, reqUser *user.User) (*user.User, error) { 317 descUser := user.NewDescriptAccountRequestWithAccount(reqUser.Account) 318 u, err := i.user.DescribeAccount(ctx, descUser) 319 320 if u != nil && u.Type.IsIn(types.UserType_PRIMARY, types.UserType_SUPPER) { 321 return nil, exception.NewBadRequest("用户名和主账号用户名冲突, 请修改") 322 } 323 324 if err != nil { 325 if exception.IsNotFoundError(err) { 326 req := user.NewCreateUserRequestWithWXWORKSync(reqUser.Account, i.randomPass()) 327 req.UserType = types.UserType_SUB 328 req.Profile = reqUser.Profile 329 req.Domain = reqUser.Domain 330 u, err = i.user.CreateAccount(ctx, req) 331 if err != nil { 332 return nil, err 333 } 334 } 335 return u, err 336 } 337 338 return u, nil 339 } 340 341 func (i *issuer) randomPass() string { 342 rpass, err := password.NewWithDefault().Generate() 343 if err != nil { 344 i.log.Warnf("generate random password error, %s, use uuid for random password", err) 345 } 346 if rpass != nil { 347 return *rpass 348 } 349 350 return xid.New().String() 351 } 352 353 func (i *issuer) issueUserToken(app *application.Application, u *user.User, gt token.GrantType) *token.Token { 354 tk := i.newBearToken(app, gt) 355 tk.Account = u.Account 356 tk.UserType = u.Type 357 return tk 358 } 359 360 func (i *issuer) newBearToken(app *application.Application, gt token.GrantType) *token.Token { 361 now := time.Now() 362 tk := &token.Token{ 363 Type: token.TokenType_BEARER, 364 AccessToken: token.MakeBearer(24), 365 RefreshToken: token.MakeBearer(32), 366 CreateAt: now.UnixNano() / 1000000, 367 ClientId: app.ClientId, 368 GrantType: gt, 369 ApplicationId: app.Id, 370 ApplicationName: app.Name, 371 } 372 373 if app.AccessTokenExpireSecond != 0 { 374 accessExpire := now.Add(time.Duration(app.AccessTokenExpireSecond) * time.Second) 375 tk.AccessExpiredAt = accessExpire.UnixNano() / 1000000 376 } 377 378 if app.RefreshTokenExpireSecond != 0 { 379 refreshExpir := now.Add(time.Duration(app.RefreshTokenExpireSecond) * time.Second) 380 tk.RefreshExpiredAt = refreshExpir.UnixNano() / 1000000 381 } 382 383 return tk 384 }