code.gitea.io/gitea@v1.21.7/models/auth/oauth2.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package auth 5 6 import ( 7 "context" 8 "encoding/base32" 9 "encoding/base64" 10 "fmt" 11 "net" 12 "net/url" 13 "strings" 14 15 "code.gitea.io/gitea/models/db" 16 "code.gitea.io/gitea/modules/container" 17 "code.gitea.io/gitea/modules/setting" 18 "code.gitea.io/gitea/modules/timeutil" 19 "code.gitea.io/gitea/modules/util" 20 21 uuid "github.com/google/uuid" 22 "github.com/minio/sha256-simd" 23 "golang.org/x/crypto/bcrypt" 24 "xorm.io/builder" 25 "xorm.io/xorm" 26 ) 27 28 // OAuth2Application represents an OAuth2 client (RFC 6749) 29 type OAuth2Application struct { 30 ID int64 `xorm:"pk autoincr"` 31 UID int64 `xorm:"INDEX"` 32 Name string 33 ClientID string `xorm:"unique"` 34 ClientSecret string 35 // OAuth defines both Confidential and Public client types 36 // https://datatracker.ietf.org/doc/html/rfc6749#section-2.1 37 // "Authorization servers MUST record the client type in the client registration details" 38 // https://datatracker.ietf.org/doc/html/rfc8252#section-8.4 39 ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"` 40 RedirectURIs []string `xorm:"redirect_uris JSON TEXT"` 41 CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` 42 UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` 43 } 44 45 func init() { 46 db.RegisterModel(new(OAuth2Application)) 47 db.RegisterModel(new(OAuth2AuthorizationCode)) 48 db.RegisterModel(new(OAuth2Grant)) 49 } 50 51 type BuiltinOAuth2Application struct { 52 ConfigName string 53 DisplayName string 54 RedirectURIs []string 55 } 56 57 func BuiltinApplications() map[string]*BuiltinOAuth2Application { 58 m := make(map[string]*BuiltinOAuth2Application) 59 m["a4792ccc-144e-407e-86c9-5e7d8d9c3269"] = &BuiltinOAuth2Application{ 60 ConfigName: "git-credential-oauth", 61 DisplayName: "git-credential-oauth", 62 RedirectURIs: []string{"http://127.0.0.1", "https://127.0.0.1"}, 63 } 64 m["e90ee53c-94e2-48ac-9358-a874fb9e0662"] = &BuiltinOAuth2Application{ 65 ConfigName: "git-credential-manager", 66 DisplayName: "Git Credential Manager", 67 RedirectURIs: []string{"http://127.0.0.1", "https://127.0.0.1"}, 68 } 69 return m 70 } 71 72 func Init(ctx context.Context) error { 73 builtinApps := BuiltinApplications() 74 var builtinAllClientIDs []string 75 for clientID := range builtinApps { 76 builtinAllClientIDs = append(builtinAllClientIDs, clientID) 77 } 78 79 var registeredApps []*OAuth2Application 80 if err := db.GetEngine(ctx).In("client_id", builtinAllClientIDs).Find(®isteredApps); err != nil { 81 return err 82 } 83 84 clientIDsToAdd := container.Set[string]{} 85 for _, configName := range setting.OAuth2.DefaultApplications { 86 found := false 87 for clientID, builtinApp := range builtinApps { 88 if builtinApp.ConfigName == configName { 89 clientIDsToAdd.Add(clientID) // add all user-configured apps to the "add" list 90 found = true 91 } 92 } 93 if !found { 94 return fmt.Errorf("unknown oauth2 application: %q", configName) 95 } 96 } 97 clientIDsToDelete := container.Set[string]{} 98 for _, app := range registeredApps { 99 if !clientIDsToAdd.Contains(app.ClientID) { 100 clientIDsToDelete.Add(app.ClientID) // if a registered app is not in the "add" list, it should be deleted 101 } 102 } 103 for _, app := range registeredApps { 104 clientIDsToAdd.Remove(app.ClientID) // no need to re-add existing (registered) apps, so remove them from the set 105 } 106 107 for _, app := range registeredApps { 108 if clientIDsToDelete.Contains(app.ClientID) { 109 if err := deleteOAuth2Application(ctx, app.ID, 0); err != nil { 110 return err 111 } 112 } 113 } 114 for clientID := range clientIDsToAdd { 115 builtinApp := builtinApps[clientID] 116 if err := db.Insert(ctx, &OAuth2Application{ 117 Name: builtinApp.DisplayName, 118 ClientID: clientID, 119 RedirectURIs: builtinApp.RedirectURIs, 120 }); err != nil { 121 return err 122 } 123 } 124 125 return nil 126 } 127 128 // TableName sets the table name to `oauth2_application` 129 func (app *OAuth2Application) TableName() string { 130 return "oauth2_application" 131 } 132 133 // ContainsRedirectURI checks if redirectURI is allowed for app 134 func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { 135 contains := func(s string) bool { 136 s = strings.TrimSuffix(strings.ToLower(s), "/") 137 for _, u := range app.RedirectURIs { 138 if strings.TrimSuffix(strings.ToLower(u), "/") == s { 139 return true 140 } 141 } 142 return false 143 } 144 if !app.ConfidentialClient { 145 uri, err := url.Parse(redirectURI) 146 // ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3 147 if err == nil && uri.Scheme == "http" && uri.Port() != "" { 148 ip := net.ParseIP(uri.Hostname()) 149 if ip != nil && ip.IsLoopback() { 150 // strip port 151 uri.Host = uri.Hostname() 152 if contains(uri.String()) { 153 return true 154 } 155 } 156 } 157 } 158 return contains(redirectURI) 159 } 160 161 // Base32 characters, but lowercased. 162 const lowerBase32Chars = "abcdefghijklmnopqrstuvwxyz234567" 163 164 // base32 encoder that uses lowered characters without padding. 165 var base32Lower = base32.NewEncoding(lowerBase32Chars).WithPadding(base32.NoPadding) 166 167 // GenerateClientSecret will generate the client secret and returns the plaintext and saves the hash at the database 168 func (app *OAuth2Application) GenerateClientSecret() (string, error) { 169 rBytes, err := util.CryptoRandomBytes(32) 170 if err != nil { 171 return "", err 172 } 173 // Add a prefix to the base32, this is in order to make it easier 174 // for code scanners to grab sensitive tokens. 175 clientSecret := "gto_" + base32Lower.EncodeToString(rBytes) 176 177 hashedSecret, err := bcrypt.GenerateFromPassword([]byte(clientSecret), bcrypt.DefaultCost) 178 if err != nil { 179 return "", err 180 } 181 app.ClientSecret = string(hashedSecret) 182 if _, err := db.GetEngine(db.DefaultContext).ID(app.ID).Cols("client_secret").Update(app); err != nil { 183 return "", err 184 } 185 return clientSecret, nil 186 } 187 188 // ValidateClientSecret validates the given secret by the hash saved in database 189 func (app *OAuth2Application) ValidateClientSecret(secret []byte) bool { 190 return bcrypt.CompareHashAndPassword([]byte(app.ClientSecret), secret) == nil 191 } 192 193 // GetGrantByUserID returns a OAuth2Grant by its user and application ID 194 func (app *OAuth2Application) GetGrantByUserID(ctx context.Context, userID int64) (grant *OAuth2Grant, err error) { 195 grant = new(OAuth2Grant) 196 if has, err := db.GetEngine(ctx).Where("user_id = ? AND application_id = ?", userID, app.ID).Get(grant); err != nil { 197 return nil, err 198 } else if !has { 199 return nil, nil 200 } 201 return grant, nil 202 } 203 204 // CreateGrant generates a grant for an user 205 func (app *OAuth2Application) CreateGrant(ctx context.Context, userID int64, scope string) (*OAuth2Grant, error) { 206 grant := &OAuth2Grant{ 207 ApplicationID: app.ID, 208 UserID: userID, 209 Scope: scope, 210 } 211 err := db.Insert(ctx, grant) 212 if err != nil { 213 return nil, err 214 } 215 return grant, nil 216 } 217 218 // GetOAuth2ApplicationByClientID returns the oauth2 application with the given client_id. Returns an error if not found. 219 func GetOAuth2ApplicationByClientID(ctx context.Context, clientID string) (app *OAuth2Application, err error) { 220 app = new(OAuth2Application) 221 has, err := db.GetEngine(ctx).Where("client_id = ?", clientID).Get(app) 222 if !has { 223 return nil, ErrOAuthClientIDInvalid{ClientID: clientID} 224 } 225 return app, err 226 } 227 228 // GetOAuth2ApplicationByID returns the oauth2 application with the given id. Returns an error if not found. 229 func GetOAuth2ApplicationByID(ctx context.Context, id int64) (app *OAuth2Application, err error) { 230 app = new(OAuth2Application) 231 has, err := db.GetEngine(ctx).ID(id).Get(app) 232 if err != nil { 233 return nil, err 234 } 235 if !has { 236 return nil, ErrOAuthApplicationNotFound{ID: id} 237 } 238 return app, nil 239 } 240 241 // GetOAuth2ApplicationsByUserID returns all oauth2 applications owned by the user 242 func GetOAuth2ApplicationsByUserID(ctx context.Context, userID int64) (apps []*OAuth2Application, err error) { 243 apps = make([]*OAuth2Application, 0) 244 err = db.GetEngine(ctx).Where("uid = ?", userID).Find(&apps) 245 return apps, err 246 } 247 248 // CreateOAuth2ApplicationOptions holds options to create an oauth2 application 249 type CreateOAuth2ApplicationOptions struct { 250 Name string 251 UserID int64 252 ConfidentialClient bool 253 RedirectURIs []string 254 } 255 256 // CreateOAuth2Application inserts a new oauth2 application 257 func CreateOAuth2Application(ctx context.Context, opts CreateOAuth2ApplicationOptions) (*OAuth2Application, error) { 258 clientID := uuid.New().String() 259 app := &OAuth2Application{ 260 UID: opts.UserID, 261 Name: opts.Name, 262 ClientID: clientID, 263 RedirectURIs: opts.RedirectURIs, 264 ConfidentialClient: opts.ConfidentialClient, 265 } 266 if err := db.Insert(ctx, app); err != nil { 267 return nil, err 268 } 269 return app, nil 270 } 271 272 // UpdateOAuth2ApplicationOptions holds options to update an oauth2 application 273 type UpdateOAuth2ApplicationOptions struct { 274 ID int64 275 Name string 276 UserID int64 277 ConfidentialClient bool 278 RedirectURIs []string 279 } 280 281 // UpdateOAuth2Application updates an oauth2 application 282 func UpdateOAuth2Application(opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) { 283 ctx, committer, err := db.TxContext(db.DefaultContext) 284 if err != nil { 285 return nil, err 286 } 287 defer committer.Close() 288 289 app, err := GetOAuth2ApplicationByID(ctx, opts.ID) 290 if err != nil { 291 return nil, err 292 } 293 if app.UID != opts.UserID { 294 return nil, fmt.Errorf("UID mismatch") 295 } 296 builtinApps := BuiltinApplications() 297 if _, builtin := builtinApps[app.ClientID]; builtin { 298 return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID) 299 } 300 301 app.Name = opts.Name 302 app.RedirectURIs = opts.RedirectURIs 303 app.ConfidentialClient = opts.ConfidentialClient 304 305 if err = updateOAuth2Application(ctx, app); err != nil { 306 return nil, err 307 } 308 app.ClientSecret = "" 309 310 return app, committer.Commit() 311 } 312 313 func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error { 314 if _, err := db.GetEngine(ctx).ID(app.ID).UseBool("confidential_client").Update(app); err != nil { 315 return err 316 } 317 return nil 318 } 319 320 func deleteOAuth2Application(ctx context.Context, id, userid int64) error { 321 sess := db.GetEngine(ctx) 322 // the userid could be 0 if the app is instance-wide 323 if deleted, err := sess.Where(builder.Eq{"id": id, "uid": userid}).Delete(&OAuth2Application{}); err != nil { 324 return err 325 } else if deleted == 0 { 326 return ErrOAuthApplicationNotFound{ID: id} 327 } 328 codes := make([]*OAuth2AuthorizationCode, 0) 329 // delete correlating auth codes 330 if err := sess.Join("INNER", "oauth2_grant", 331 "oauth2_authorization_code.grant_id = oauth2_grant.id AND oauth2_grant.application_id = ?", id).Find(&codes); err != nil { 332 return err 333 } 334 codeIDs := make([]int64, 0, len(codes)) 335 for _, grant := range codes { 336 codeIDs = append(codeIDs, grant.ID) 337 } 338 339 if _, err := sess.In("id", codeIDs).Delete(new(OAuth2AuthorizationCode)); err != nil { 340 return err 341 } 342 343 if _, err := sess.Where("application_id = ?", id).Delete(new(OAuth2Grant)); err != nil { 344 return err 345 } 346 return nil 347 } 348 349 // DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app. 350 func DeleteOAuth2Application(id, userid int64) error { 351 ctx, committer, err := db.TxContext(db.DefaultContext) 352 if err != nil { 353 return err 354 } 355 defer committer.Close() 356 app, err := GetOAuth2ApplicationByID(ctx, id) 357 if err != nil { 358 return err 359 } 360 builtinApps := BuiltinApplications() 361 if _, builtin := builtinApps[app.ClientID]; builtin { 362 return fmt.Errorf("failed to delete OAuth2 application: application is locked: %s", app.ClientID) 363 } 364 if err := deleteOAuth2Application(ctx, id, userid); err != nil { 365 return err 366 } 367 return committer.Commit() 368 } 369 370 // ListOAuth2Applications returns a list of oauth2 applications belongs to given user. 371 func ListOAuth2Applications(uid int64, listOptions db.ListOptions) ([]*OAuth2Application, int64, error) { 372 sess := db.GetEngine(db.DefaultContext). 373 Where("uid=?", uid). 374 Desc("id") 375 376 if listOptions.Page != 0 { 377 sess = db.SetSessionPagination(sess, &listOptions) 378 379 apps := make([]*OAuth2Application, 0, listOptions.PageSize) 380 total, err := sess.FindAndCount(&apps) 381 return apps, total, err 382 } 383 384 apps := make([]*OAuth2Application, 0, 5) 385 total, err := sess.FindAndCount(&apps) 386 return apps, total, err 387 } 388 389 ////////////////////////////////////////////////////// 390 391 // OAuth2AuthorizationCode is a code to obtain an access token in combination with the client secret once. It has a limited lifetime. 392 type OAuth2AuthorizationCode struct { 393 ID int64 `xorm:"pk autoincr"` 394 Grant *OAuth2Grant `xorm:"-"` 395 GrantID int64 396 Code string `xorm:"INDEX unique"` 397 CodeChallenge string 398 CodeChallengeMethod string 399 RedirectURI string 400 ValidUntil timeutil.TimeStamp `xorm:"index"` 401 } 402 403 // TableName sets the table name to `oauth2_authorization_code` 404 func (code *OAuth2AuthorizationCode) TableName() string { 405 return "oauth2_authorization_code" 406 } 407 408 // GenerateRedirectURI generates a redirect URI for a successful authorization request. State will be used if not empty. 409 func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL, error) { 410 redirect, err := url.Parse(code.RedirectURI) 411 if err != nil { 412 return nil, err 413 } 414 q := redirect.Query() 415 if state != "" { 416 q.Set("state", state) 417 } 418 q.Set("code", code.Code) 419 redirect.RawQuery = q.Encode() 420 return redirect, err 421 } 422 423 // Invalidate deletes the auth code from the database to invalidate this code 424 func (code *OAuth2AuthorizationCode) Invalidate(ctx context.Context) error { 425 _, err := db.GetEngine(ctx).ID(code.ID).NoAutoCondition().Delete(code) 426 return err 427 } 428 429 // ValidateCodeChallenge validates the given verifier against the saved code challenge. This is part of the PKCE implementation. 430 func (code *OAuth2AuthorizationCode) ValidateCodeChallenge(verifier string) bool { 431 switch code.CodeChallengeMethod { 432 case "S256": 433 // base64url(SHA256(verifier)) see https://tools.ietf.org/html/rfc7636#section-4.6 434 h := sha256.Sum256([]byte(verifier)) 435 hashedVerifier := base64.RawURLEncoding.EncodeToString(h[:]) 436 return hashedVerifier == code.CodeChallenge 437 case "plain": 438 return verifier == code.CodeChallenge 439 case "": 440 return true 441 default: 442 // unsupported method -> return false 443 return false 444 } 445 } 446 447 // GetOAuth2AuthorizationByCode returns an authorization by its code 448 func GetOAuth2AuthorizationByCode(ctx context.Context, code string) (auth *OAuth2AuthorizationCode, err error) { 449 auth = new(OAuth2AuthorizationCode) 450 if has, err := db.GetEngine(ctx).Where("code = ?", code).Get(auth); err != nil { 451 return nil, err 452 } else if !has { 453 return nil, nil 454 } 455 auth.Grant = new(OAuth2Grant) 456 if has, err := db.GetEngine(ctx).ID(auth.GrantID).Get(auth.Grant); err != nil { 457 return nil, err 458 } else if !has { 459 return nil, nil 460 } 461 return auth, nil 462 } 463 464 ////////////////////////////////////////////////////// 465 466 // OAuth2Grant represents the permission of an user for a specific application to access resources 467 type OAuth2Grant struct { 468 ID int64 `xorm:"pk autoincr"` 469 UserID int64 `xorm:"INDEX unique(user_application)"` 470 Application *OAuth2Application `xorm:"-"` 471 ApplicationID int64 `xorm:"INDEX unique(user_application)"` 472 Counter int64 `xorm:"NOT NULL DEFAULT 1"` 473 Scope string `xorm:"TEXT"` 474 Nonce string `xorm:"TEXT"` 475 CreatedUnix timeutil.TimeStamp `xorm:"created"` 476 UpdatedUnix timeutil.TimeStamp `xorm:"updated"` 477 } 478 479 // TableName sets the table name to `oauth2_grant` 480 func (grant *OAuth2Grant) TableName() string { 481 return "oauth2_grant" 482 } 483 484 // GenerateNewAuthorizationCode generates a new authorization code for a grant and saves it to the database 485 func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redirectURI, codeChallenge, codeChallengeMethod string) (code *OAuth2AuthorizationCode, err error) { 486 rBytes, err := util.CryptoRandomBytes(32) 487 if err != nil { 488 return &OAuth2AuthorizationCode{}, err 489 } 490 // Add a prefix to the base32, this is in order to make it easier 491 // for code scanners to grab sensitive tokens. 492 codeSecret := "gta_" + base32Lower.EncodeToString(rBytes) 493 494 code = &OAuth2AuthorizationCode{ 495 Grant: grant, 496 GrantID: grant.ID, 497 RedirectURI: redirectURI, 498 Code: codeSecret, 499 CodeChallenge: codeChallenge, 500 CodeChallengeMethod: codeChallengeMethod, 501 } 502 if err := db.Insert(ctx, code); err != nil { 503 return nil, err 504 } 505 return code, nil 506 } 507 508 // IncreaseCounter increases the counter and updates the grant 509 func (grant *OAuth2Grant) IncreaseCounter(ctx context.Context) error { 510 _, err := db.GetEngine(ctx).ID(grant.ID).Incr("counter").Update(new(OAuth2Grant)) 511 if err != nil { 512 return err 513 } 514 updatedGrant, err := GetOAuth2GrantByID(ctx, grant.ID) 515 if err != nil { 516 return err 517 } 518 grant.Counter = updatedGrant.Counter 519 return nil 520 } 521 522 // ScopeContains returns true if the grant scope contains the specified scope 523 func (grant *OAuth2Grant) ScopeContains(scope string) bool { 524 for _, currentScope := range strings.Split(grant.Scope, " ") { 525 if scope == currentScope { 526 return true 527 } 528 } 529 return false 530 } 531 532 // SetNonce updates the current nonce value of a grant 533 func (grant *OAuth2Grant) SetNonce(ctx context.Context, nonce string) error { 534 grant.Nonce = nonce 535 _, err := db.GetEngine(ctx).ID(grant.ID).Cols("nonce").Update(grant) 536 if err != nil { 537 return err 538 } 539 return nil 540 } 541 542 // GetOAuth2GrantByID returns the grant with the given ID 543 func GetOAuth2GrantByID(ctx context.Context, id int64) (grant *OAuth2Grant, err error) { 544 grant = new(OAuth2Grant) 545 if has, err := db.GetEngine(ctx).ID(id).Get(grant); err != nil { 546 return nil, err 547 } else if !has { 548 return nil, nil 549 } 550 return grant, err 551 } 552 553 // GetOAuth2GrantsByUserID lists all grants of a certain user 554 func GetOAuth2GrantsByUserID(ctx context.Context, uid int64) ([]*OAuth2Grant, error) { 555 type joinedOAuth2Grant struct { 556 Grant *OAuth2Grant `xorm:"extends"` 557 Application *OAuth2Application `xorm:"extends"` 558 } 559 var results *xorm.Rows 560 var err error 561 if results, err = db.GetEngine(ctx). 562 Table("oauth2_grant"). 563 Where("user_id = ?", uid). 564 Join("INNER", "oauth2_application", "application_id = oauth2_application.id"). 565 Rows(new(joinedOAuth2Grant)); err != nil { 566 return nil, err 567 } 568 defer results.Close() 569 grants := make([]*OAuth2Grant, 0) 570 for results.Next() { 571 joinedGrant := new(joinedOAuth2Grant) 572 if err := results.Scan(joinedGrant); err != nil { 573 return nil, err 574 } 575 joinedGrant.Grant.Application = joinedGrant.Application 576 grants = append(grants, joinedGrant.Grant) 577 } 578 return grants, nil 579 } 580 581 // RevokeOAuth2Grant deletes the grant with grantID and userID 582 func RevokeOAuth2Grant(ctx context.Context, grantID, userID int64) error { 583 _, err := db.GetEngine(ctx).Where(builder.Eq{"id": grantID, "user_id": userID}).Delete(&OAuth2Grant{}) 584 return err 585 } 586 587 // ErrOAuthClientIDInvalid will be thrown if client id cannot be found 588 type ErrOAuthClientIDInvalid struct { 589 ClientID string 590 } 591 592 // IsErrOauthClientIDInvalid checks if an error is a ErrOAuthClientIDInvalid. 593 func IsErrOauthClientIDInvalid(err error) bool { 594 _, ok := err.(ErrOAuthClientIDInvalid) 595 return ok 596 } 597 598 // Error returns the error message 599 func (err ErrOAuthClientIDInvalid) Error() string { 600 return fmt.Sprintf("Client ID invalid [Client ID: %s]", err.ClientID) 601 } 602 603 // Unwrap unwraps this as a ErrNotExist err 604 func (err ErrOAuthClientIDInvalid) Unwrap() error { 605 return util.ErrNotExist 606 } 607 608 // ErrOAuthApplicationNotFound will be thrown if id cannot be found 609 type ErrOAuthApplicationNotFound struct { 610 ID int64 611 } 612 613 // IsErrOAuthApplicationNotFound checks if an error is a ErrReviewNotExist. 614 func IsErrOAuthApplicationNotFound(err error) bool { 615 _, ok := err.(ErrOAuthApplicationNotFound) 616 return ok 617 } 618 619 // Error returns the error message 620 func (err ErrOAuthApplicationNotFound) Error() string { 621 return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID) 622 } 623 624 // Unwrap unwraps this as a ErrNotExist err 625 func (err ErrOAuthApplicationNotFound) Unwrap() error { 626 return util.ErrNotExist 627 } 628 629 // GetOAuth2ProviderSources returns all actived LoginOAuth2 sources 630 func GetOAuth2ProviderSources(onlyActive bool) ([]*Source, error) { 631 sources := make([]*Source, 0, 1) 632 sess := db.GetEngine(db.DefaultContext) 633 if onlyActive { 634 sess = sess.Where("is_active = ?", true) 635 } 636 if err := sess.Where("type = ?", OAuth2).Find(&sources); err != nil { 637 return nil, err 638 } 639 return sources, nil 640 } 641 642 // GetActiveOAuth2SourceByName returns a OAuth2 AuthSource based on the given name 643 func GetActiveOAuth2SourceByName(name string) (*Source, error) { 644 authSource := new(Source) 645 has, err := db.GetEngine(db.DefaultContext).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(authSource) 646 if err != nil { 647 return nil, err 648 } 649 650 if !has { 651 return nil, fmt.Errorf("oauth2 source not found, name: %q", name) 652 } 653 654 return authSource, nil 655 } 656 657 func DeleteOAuth2RelictsByUserID(ctx context.Context, userID int64) error { 658 deleteCond := builder.Select("id").From("oauth2_grant").Where(builder.Eq{"oauth2_grant.user_id": userID}) 659 660 if _, err := db.GetEngine(ctx).In("grant_id", deleteCond). 661 Delete(&OAuth2AuthorizationCode{}); err != nil { 662 return err 663 } 664 665 if err := db.DeleteBeans(ctx, 666 &OAuth2Application{UID: userID}, 667 &OAuth2Grant{UserID: userID}, 668 ); err != nil { 669 return fmt.Errorf("DeleteBeans: %w", err) 670 } 671 672 return nil 673 }