gitea源码

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