gitea源码

ssh_key_deploy.go 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package asymkey
  4. import (
  5. "context"
  6. "fmt"
  7. "time"
  8. "code.gitea.io/gitea/models/db"
  9. "code.gitea.io/gitea/models/perm"
  10. "code.gitea.io/gitea/modules/timeutil"
  11. "xorm.io/builder"
  12. )
  13. // ________ .__ ____ __.
  14. // \______ \ ____ ______ | | ____ ___.__.| |/ _|____ ___.__.
  15. // | | \_/ __ \\____ \| | / _ < | || <_/ __ < | |
  16. // | ` \ ___/| |_> > |_( <_> )___ || | \ ___/\___ |
  17. // /_______ /\___ > __/|____/\____// ____||____|__ \___ > ____|
  18. // \/ \/|__| \/ \/ \/\/
  19. //
  20. // This file contains functions specific to DeployKeys
  21. // DeployKey represents deploy key information and its relation with repository.
  22. type DeployKey struct {
  23. ID int64 `xorm:"pk autoincr"`
  24. KeyID int64 `xorm:"UNIQUE(s) INDEX"`
  25. RepoID int64 `xorm:"UNIQUE(s) INDEX"`
  26. Name string
  27. Fingerprint string
  28. Content string `xorm:"-"`
  29. Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 1"`
  30. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  31. UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
  32. HasRecentActivity bool `xorm:"-"`
  33. HasUsed bool `xorm:"-"`
  34. }
  35. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
  36. func (key *DeployKey) AfterLoad() {
  37. key.HasUsed = key.UpdatedUnix > key.CreatedUnix
  38. key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
  39. }
  40. // GetContent gets associated public key content.
  41. func (key *DeployKey) GetContent(ctx context.Context) error {
  42. pkey, err := GetPublicKeyByID(ctx, key.KeyID)
  43. if err != nil {
  44. return err
  45. }
  46. key.Content = pkey.Content
  47. return nil
  48. }
  49. // IsReadOnly checks if the key can only be used for read operations, used by template
  50. func (key *DeployKey) IsReadOnly() bool {
  51. return key.Mode == perm.AccessModeRead
  52. }
  53. func init() {
  54. db.RegisterModel(new(DeployKey))
  55. }
  56. func checkDeployKey(ctx context.Context, keyID, repoID int64, name string) error {
  57. // Note: We want error detail, not just true or false here.
  58. has, err := db.GetEngine(ctx).
  59. Where("key_id = ? AND repo_id = ?", keyID, repoID).
  60. Get(new(DeployKey))
  61. if err != nil {
  62. return err
  63. } else if has {
  64. return ErrDeployKeyAlreadyExist{keyID, repoID}
  65. }
  66. has, err = db.GetEngine(ctx).
  67. Where("repo_id = ? AND name = ?", repoID, name).
  68. Get(new(DeployKey))
  69. if err != nil {
  70. return err
  71. } else if has {
  72. return ErrDeployKeyNameAlreadyUsed{repoID, name}
  73. }
  74. return nil
  75. }
  76. // addDeployKey adds new key-repo relation.
  77. func addDeployKey(ctx context.Context, keyID, repoID int64, name, fingerprint string, mode perm.AccessMode) (*DeployKey, error) {
  78. if err := checkDeployKey(ctx, keyID, repoID, name); err != nil {
  79. return nil, err
  80. }
  81. key := &DeployKey{
  82. KeyID: keyID,
  83. RepoID: repoID,
  84. Name: name,
  85. Fingerprint: fingerprint,
  86. Mode: mode,
  87. }
  88. return key, db.Insert(ctx, key)
  89. }
  90. // HasDeployKey returns true if public key is a deploy key of given repository.
  91. func HasDeployKey(ctx context.Context, keyID, repoID int64) bool {
  92. has, _ := db.GetEngine(ctx).
  93. Where("key_id = ? AND repo_id = ?", keyID, repoID).
  94. Get(new(DeployKey))
  95. return has
  96. }
  97. // AddDeployKey add new deploy key to database and authorized_keys file.
  98. func AddDeployKey(ctx context.Context, repoID int64, name, content string, readOnly bool) (*DeployKey, error) {
  99. fingerprint, err := CalcFingerprint(content)
  100. if err != nil {
  101. return nil, err
  102. }
  103. accessMode := perm.AccessModeRead
  104. if !readOnly {
  105. accessMode = perm.AccessModeWrite
  106. }
  107. return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
  108. pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
  109. if err != nil {
  110. return nil, err
  111. } else if exist {
  112. if pkey.Type != KeyTypeDeploy {
  113. return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
  114. }
  115. } else {
  116. // First time use this deploy key.
  117. pkey = &PublicKey{
  118. Fingerprint: fingerprint,
  119. Mode: accessMode,
  120. Type: KeyTypeDeploy,
  121. Content: content,
  122. Name: name,
  123. }
  124. if err = addKey(ctx, pkey); err != nil {
  125. return nil, fmt.Errorf("addKey: %w", err)
  126. }
  127. }
  128. key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
  129. if err != nil {
  130. return nil, err
  131. }
  132. return key, nil
  133. })
  134. }
  135. // GetDeployKeyByID returns deploy key by given ID.
  136. func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) {
  137. key, exist, err := db.GetByID[DeployKey](ctx, id)
  138. if err != nil {
  139. return nil, err
  140. } else if !exist {
  141. return nil, ErrDeployKeyNotExist{id, 0, 0}
  142. }
  143. return key, nil
  144. }
  145. // GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
  146. func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, error) {
  147. key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"key_id": keyID, "repo_id": repoID})
  148. if err != nil {
  149. return nil, err
  150. } else if !exist {
  151. return nil, ErrDeployKeyNotExist{0, keyID, repoID}
  152. }
  153. return key, nil
  154. }
  155. // IsDeployKeyExistByKeyID return true if there is at least one deploykey with the key id
  156. func IsDeployKeyExistByKeyID(ctx context.Context, keyID int64) (bool, error) {
  157. return db.GetEngine(ctx).
  158. Where("key_id = ?", keyID).
  159. Get(new(DeployKey))
  160. }
  161. // UpdateDeployKeyCols updates deploy key information in the specified columns.
  162. func UpdateDeployKeyCols(ctx context.Context, key *DeployKey, cols ...string) error {
  163. _, err := db.GetEngine(ctx).ID(key.ID).Cols(cols...).Update(key)
  164. return err
  165. }
  166. // ListDeployKeysOptions are options for ListDeployKeys
  167. type ListDeployKeysOptions struct {
  168. db.ListOptions
  169. RepoID int64
  170. KeyID int64
  171. Fingerprint string
  172. }
  173. func (opt ListDeployKeysOptions) ToConds() builder.Cond {
  174. cond := builder.NewCond()
  175. if opt.RepoID != 0 {
  176. cond = cond.And(builder.Eq{"repo_id": opt.RepoID})
  177. }
  178. if opt.KeyID != 0 {
  179. cond = cond.And(builder.Eq{"key_id": opt.KeyID})
  180. }
  181. if opt.Fingerprint != "" {
  182. cond = cond.And(builder.Eq{"fingerprint": opt.Fingerprint})
  183. }
  184. return cond
  185. }