You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ssh_key_deploy.go 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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() error {
  42. pkey, err := GetPublicKeyByID(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(keyID, repoID int64) bool {
  92. has, _ := db.GetEngine(db.DefaultContext).
  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(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. ctx, committer, err := db.TxContext(db.DefaultContext)
  108. if err != nil {
  109. return nil, err
  110. }
  111. defer committer.Close()
  112. pkey := &PublicKey{
  113. Fingerprint: fingerprint,
  114. }
  115. has, err := db.GetByBean(ctx, pkey)
  116. if err != nil {
  117. return nil, err
  118. }
  119. if has {
  120. if pkey.Type != KeyTypeDeploy {
  121. return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
  122. }
  123. } else {
  124. // First time use this deploy key.
  125. pkey.Mode = accessMode
  126. pkey.Type = KeyTypeDeploy
  127. pkey.Content = content
  128. pkey.Name = name
  129. if err = addKey(ctx, pkey); err != nil {
  130. return nil, fmt.Errorf("addKey: %w", err)
  131. }
  132. }
  133. key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
  134. if err != nil {
  135. return nil, err
  136. }
  137. return key, committer.Commit()
  138. }
  139. // GetDeployKeyByID returns deploy key by given ID.
  140. func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) {
  141. key := new(DeployKey)
  142. has, err := db.GetEngine(ctx).ID(id).Get(key)
  143. if err != nil {
  144. return nil, err
  145. } else if !has {
  146. return nil, ErrDeployKeyNotExist{id, 0, 0}
  147. }
  148. return key, nil
  149. }
  150. // GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
  151. func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, error) {
  152. key := &DeployKey{
  153. KeyID: keyID,
  154. RepoID: repoID,
  155. }
  156. has, err := db.GetByBean(ctx, key)
  157. if err != nil {
  158. return nil, err
  159. } else if !has {
  160. return nil, ErrDeployKeyNotExist{0, keyID, repoID}
  161. }
  162. return key, nil
  163. }
  164. // IsDeployKeyExistByKeyID return true if there is at least one deploykey with the key id
  165. func IsDeployKeyExistByKeyID(ctx context.Context, keyID int64) (bool, error) {
  166. return db.GetEngine(ctx).
  167. Where("key_id = ?", keyID).
  168. Get(new(DeployKey))
  169. }
  170. // UpdateDeployKeyCols updates deploy key information in the specified columns.
  171. func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
  172. _, err := db.GetEngine(db.DefaultContext).ID(key.ID).Cols(cols...).Update(key)
  173. return err
  174. }
  175. // ListDeployKeysOptions are options for ListDeployKeys
  176. type ListDeployKeysOptions struct {
  177. db.ListOptions
  178. RepoID int64
  179. KeyID int64
  180. Fingerprint string
  181. }
  182. func (opt ListDeployKeysOptions) toCond() builder.Cond {
  183. cond := builder.NewCond()
  184. if opt.RepoID != 0 {
  185. cond = cond.And(builder.Eq{"repo_id": opt.RepoID})
  186. }
  187. if opt.KeyID != 0 {
  188. cond = cond.And(builder.Eq{"key_id": opt.KeyID})
  189. }
  190. if opt.Fingerprint != "" {
  191. cond = cond.And(builder.Eq{"fingerprint": opt.Fingerprint})
  192. }
  193. return cond
  194. }
  195. // ListDeployKeys returns a list of deploy keys matching the provided arguments.
  196. func ListDeployKeys(ctx context.Context, opts *ListDeployKeysOptions) ([]*DeployKey, error) {
  197. sess := db.GetEngine(ctx).Where(opts.toCond())
  198. if opts.Page != 0 {
  199. sess = db.SetSessionPagination(sess, opts)
  200. keys := make([]*DeployKey, 0, opts.PageSize)
  201. return keys, sess.Find(&keys)
  202. }
  203. keys := make([]*DeployKey, 0, 5)
  204. return keys, sess.Find(&keys)
  205. }
  206. // CountDeployKeys returns count deploy keys matching the provided arguments.
  207. func CountDeployKeys(opts *ListDeployKeysOptions) (int64, error) {
  208. return db.GetEngine(db.DefaultContext).Where(opts.toCond()).Count(&DeployKey{})
  209. }