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 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "fmt"
  7. "time"
  8. "code.gitea.io/gitea/models/db"
  9. "code.gitea.io/gitea/modules/timeutil"
  10. "xorm.io/builder"
  11. "xorm.io/xorm"
  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 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
  50. func (key *DeployKey) IsReadOnly() bool {
  51. return key.Mode == AccessModeRead
  52. }
  53. func init() {
  54. db.RegisterModel(new(DeployKey))
  55. }
  56. func checkDeployKey(e db.Engine, keyID, repoID int64, name string) error {
  57. // Note: We want error detail, not just true or false here.
  58. has, err := e.
  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 = e.
  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(e *xorm.Session, keyID, repoID int64, name, fingerprint string, mode AccessMode) (*DeployKey, error) {
  78. if err := checkDeployKey(e, 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. _, err := e.Insert(key)
  89. return key, err
  90. }
  91. // HasDeployKey returns true if public key is a deploy key of given repository.
  92. func HasDeployKey(keyID, repoID int64) bool {
  93. has, _ := db.GetEngine(db.DefaultContext).
  94. Where("key_id = ? AND repo_id = ?", keyID, repoID).
  95. Get(new(DeployKey))
  96. return has
  97. }
  98. // AddDeployKey add new deploy key to database and authorized_keys file.
  99. func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey, error) {
  100. fingerprint, err := calcFingerprint(content)
  101. if err != nil {
  102. return nil, err
  103. }
  104. accessMode := AccessModeRead
  105. if !readOnly {
  106. accessMode = AccessModeWrite
  107. }
  108. sess := db.NewSession(db.DefaultContext)
  109. defer sess.Close()
  110. if err = sess.Begin(); err != nil {
  111. return nil, err
  112. }
  113. pkey := &PublicKey{
  114. Fingerprint: fingerprint,
  115. }
  116. has, err := sess.Get(pkey)
  117. if err != nil {
  118. return nil, err
  119. }
  120. if has {
  121. if pkey.Type != KeyTypeDeploy {
  122. return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
  123. }
  124. } else {
  125. // First time use this deploy key.
  126. pkey.Mode = accessMode
  127. pkey.Type = KeyTypeDeploy
  128. pkey.Content = content
  129. pkey.Name = name
  130. if err = addKey(sess, pkey); err != nil {
  131. return nil, fmt.Errorf("addKey: %v", err)
  132. }
  133. }
  134. key, err := addDeployKey(sess, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
  135. if err != nil {
  136. return nil, err
  137. }
  138. return key, sess.Commit()
  139. }
  140. // GetDeployKeyByID returns deploy key by given ID.
  141. func GetDeployKeyByID(id int64) (*DeployKey, error) {
  142. return getDeployKeyByID(db.GetEngine(db.DefaultContext), id)
  143. }
  144. func getDeployKeyByID(e db.Engine, id int64) (*DeployKey, error) {
  145. key := new(DeployKey)
  146. has, err := e.ID(id).Get(key)
  147. if err != nil {
  148. return nil, err
  149. } else if !has {
  150. return nil, ErrDeployKeyNotExist{id, 0, 0}
  151. }
  152. return key, nil
  153. }
  154. // GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
  155. func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
  156. return getDeployKeyByRepo(db.GetEngine(db.DefaultContext), keyID, repoID)
  157. }
  158. func getDeployKeyByRepo(e db.Engine, keyID, repoID int64) (*DeployKey, error) {
  159. key := &DeployKey{
  160. KeyID: keyID,
  161. RepoID: repoID,
  162. }
  163. has, err := e.Get(key)
  164. if err != nil {
  165. return nil, err
  166. } else if !has {
  167. return nil, ErrDeployKeyNotExist{0, keyID, repoID}
  168. }
  169. return key, nil
  170. }
  171. // UpdateDeployKeyCols updates deploy key information in the specified columns.
  172. func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
  173. _, err := db.GetEngine(db.DefaultContext).ID(key.ID).Cols(cols...).Update(key)
  174. return err
  175. }
  176. // UpdateDeployKey updates deploy key information.
  177. func UpdateDeployKey(key *DeployKey) error {
  178. _, err := db.GetEngine(db.DefaultContext).ID(key.ID).AllCols().Update(key)
  179. return err
  180. }
  181. // DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
  182. func DeleteDeployKey(doer *User, id int64) error {
  183. sess := db.NewSession(db.DefaultContext)
  184. defer sess.Close()
  185. if err := sess.Begin(); err != nil {
  186. return err
  187. }
  188. if err := deleteDeployKey(sess, doer, id); err != nil {
  189. return err
  190. }
  191. return sess.Commit()
  192. }
  193. func deleteDeployKey(sess db.Engine, doer *User, id int64) error {
  194. key, err := getDeployKeyByID(sess, id)
  195. if err != nil {
  196. if IsErrDeployKeyNotExist(err) {
  197. return nil
  198. }
  199. return fmt.Errorf("GetDeployKeyByID: %v", err)
  200. }
  201. // Check if user has access to delete this key.
  202. if !doer.IsAdmin {
  203. repo, err := getRepositoryByID(sess, key.RepoID)
  204. if err != nil {
  205. return fmt.Errorf("GetRepositoryByID: %v", err)
  206. }
  207. has, err := isUserRepoAdmin(sess, repo, doer)
  208. if err != nil {
  209. return fmt.Errorf("GetUserRepoPermission: %v", err)
  210. } else if !has {
  211. return ErrKeyAccessDenied{doer.ID, key.ID, "deploy"}
  212. }
  213. }
  214. if _, err = sess.ID(key.ID).Delete(new(DeployKey)); err != nil {
  215. return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
  216. }
  217. // Check if this is the last reference to same key content.
  218. has, err := sess.
  219. Where("key_id = ?", key.KeyID).
  220. Get(new(DeployKey))
  221. if err != nil {
  222. return err
  223. } else if !has {
  224. if err = deletePublicKeys(sess, key.KeyID); err != nil {
  225. return err
  226. }
  227. // after deleted the public keys, should rewrite the public keys file
  228. if err = rewriteAllPublicKeys(sess); err != nil {
  229. return err
  230. }
  231. }
  232. return nil
  233. }
  234. // ListDeployKeysOptions are options for ListDeployKeys
  235. type ListDeployKeysOptions struct {
  236. db.ListOptions
  237. RepoID int64
  238. KeyID int64
  239. Fingerprint string
  240. }
  241. func (opt ListDeployKeysOptions) toCond() builder.Cond {
  242. cond := builder.NewCond()
  243. if opt.RepoID != 0 {
  244. cond = cond.And(builder.Eq{"repo_id": opt.RepoID})
  245. }
  246. if opt.KeyID != 0 {
  247. cond = cond.And(builder.Eq{"key_id": opt.KeyID})
  248. }
  249. if opt.Fingerprint != "" {
  250. cond = cond.And(builder.Eq{"fingerprint": opt.Fingerprint})
  251. }
  252. return cond
  253. }
  254. // ListDeployKeys returns a list of deploy keys matching the provided arguments.
  255. func ListDeployKeys(opts *ListDeployKeysOptions) ([]*DeployKey, error) {
  256. return listDeployKeys(db.GetEngine(db.DefaultContext), opts)
  257. }
  258. func listDeployKeys(e db.Engine, opts *ListDeployKeysOptions) ([]*DeployKey, error) {
  259. sess := e.Where(opts.toCond())
  260. if opts.Page != 0 {
  261. sess = db.SetSessionPagination(sess, opts)
  262. keys := make([]*DeployKey, 0, opts.PageSize)
  263. return keys, sess.Find(&keys)
  264. }
  265. keys := make([]*DeployKey, 0, 5)
  266. return keys, sess.Find(&keys)
  267. }
  268. // CountDeployKeys returns count deploy keys matching the provided arguments.
  269. func CountDeployKeys(opts *ListDeployKeysOptions) (int64, error) {
  270. return db.GetEngine(db.DefaultContext).Where(opts.toCond()).Count(&DeployKey{})
  271. }