gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package asymkey
  5. import (
  6. "context"
  7. "fmt"
  8. "strings"
  9. "time"
  10. "code.gitea.io/gitea/models/auth"
  11. "code.gitea.io/gitea/models/db"
  12. "code.gitea.io/gitea/models/perm"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/timeutil"
  16. "code.gitea.io/gitea/modules/util"
  17. "golang.org/x/crypto/ssh"
  18. "xorm.io/builder"
  19. )
  20. // KeyType specifies the key type
  21. type KeyType int
  22. const (
  23. // KeyTypeUser specifies the user key
  24. KeyTypeUser = iota + 1
  25. // KeyTypeDeploy specifies the deploy key
  26. KeyTypeDeploy
  27. // KeyTypePrincipal specifies the authorized principal key
  28. KeyTypePrincipal
  29. )
  30. // PublicKey represents a user or deploy SSH public key.
  31. type PublicKey struct {
  32. ID int64 `xorm:"pk autoincr"`
  33. OwnerID int64 `xorm:"INDEX NOT NULL"`
  34. Name string `xorm:"NOT NULL"`
  35. Fingerprint string `xorm:"INDEX NOT NULL"`
  36. Content string `xorm:"MEDIUMTEXT NOT NULL"`
  37. Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 2"`
  38. Type KeyType `xorm:"NOT NULL DEFAULT 1"`
  39. LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
  40. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  41. UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
  42. HasRecentActivity bool `xorm:"-"`
  43. HasUsed bool `xorm:"-"`
  44. Verified bool `xorm:"NOT NULL DEFAULT false"`
  45. }
  46. func init() {
  47. db.RegisterModel(new(PublicKey))
  48. }
  49. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
  50. func (key *PublicKey) AfterLoad() {
  51. key.HasUsed = key.UpdatedUnix > key.CreatedUnix
  52. key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
  53. }
  54. // OmitEmail returns content of public key without email address.
  55. func (key *PublicKey) OmitEmail() string {
  56. return strings.Join(strings.Split(key.Content, " ")[:2], " ")
  57. }
  58. // AuthorizedString returns formatted public key string for authorized_keys file.
  59. //
  60. // TODO: Consider dropping this function
  61. func (key *PublicKey) AuthorizedString() string {
  62. return AuthorizedStringForKey(key)
  63. }
  64. func addKey(ctx context.Context, key *PublicKey) (err error) {
  65. if len(key.Fingerprint) == 0 {
  66. key.Fingerprint, err = CalcFingerprint(key.Content)
  67. if err != nil {
  68. return err
  69. }
  70. }
  71. // Save SSH key.
  72. if err = db.Insert(ctx, key); err != nil {
  73. return err
  74. }
  75. return appendAuthorizedKeysToFile(key)
  76. }
  77. // AddPublicKey adds new public key to database and authorized_keys file.
  78. func AddPublicKey(ctx context.Context, ownerID int64, name, content string, authSourceID int64) (*PublicKey, error) {
  79. log.Trace(content)
  80. fingerprint, err := CalcFingerprint(content)
  81. if err != nil {
  82. return nil, err
  83. }
  84. return db.WithTx2(ctx, func(ctx context.Context) (*PublicKey, error) {
  85. if err := checkKeyFingerprint(ctx, fingerprint); err != nil {
  86. return nil, err
  87. }
  88. // Key name of same user cannot be duplicated.
  89. has, err := db.GetEngine(ctx).
  90. Where("owner_id = ? AND name = ?", ownerID, name).
  91. Get(new(PublicKey))
  92. if err != nil {
  93. return nil, err
  94. } else if has {
  95. return nil, ErrKeyNameAlreadyUsed{ownerID, name}
  96. }
  97. key := &PublicKey{
  98. OwnerID: ownerID,
  99. Name: name,
  100. Fingerprint: fingerprint,
  101. Content: content,
  102. Mode: perm.AccessModeWrite,
  103. Type: KeyTypeUser,
  104. LoginSourceID: authSourceID,
  105. }
  106. if err = addKey(ctx, key); err != nil {
  107. return nil, fmt.Errorf("addKey: %w", err)
  108. }
  109. return key, nil
  110. })
  111. }
  112. // GetPublicKeyByID returns public key by given ID.
  113. func GetPublicKeyByID(ctx context.Context, keyID int64) (*PublicKey, error) {
  114. key := new(PublicKey)
  115. has, err := db.GetEngine(ctx).
  116. ID(keyID).
  117. Get(key)
  118. if err != nil {
  119. return nil, err
  120. } else if !has {
  121. return nil, ErrKeyNotExist{keyID}
  122. }
  123. return key, nil
  124. }
  125. // SearchPublicKeyByContent searches content as prefix (leak e-mail part)
  126. // and returns public key found.
  127. func SearchPublicKeyByContent(ctx context.Context, content string) (*PublicKey, error) {
  128. key := new(PublicKey)
  129. has, err := db.GetEngine(ctx).
  130. Where("content like ?", content+"%").
  131. Get(key)
  132. if err != nil {
  133. return nil, err
  134. } else if !has {
  135. return nil, ErrKeyNotExist{}
  136. }
  137. return key, nil
  138. }
  139. // SearchPublicKeyByContentExact searches content
  140. // and returns public key found.
  141. func SearchPublicKeyByContentExact(ctx context.Context, content string) (*PublicKey, error) {
  142. key := new(PublicKey)
  143. has, err := db.GetEngine(ctx).
  144. Where("content = ?", content).
  145. Get(key)
  146. if err != nil {
  147. return nil, err
  148. } else if !has {
  149. return nil, ErrKeyNotExist{}
  150. }
  151. return key, nil
  152. }
  153. type FindPublicKeyOptions struct {
  154. db.ListOptions
  155. OwnerID int64
  156. Fingerprint string
  157. KeyTypes []KeyType
  158. NotKeytype KeyType
  159. LoginSourceID int64
  160. }
  161. func (opts FindPublicKeyOptions) ToConds() builder.Cond {
  162. cond := builder.NewCond()
  163. if opts.OwnerID > 0 {
  164. cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
  165. }
  166. if opts.Fingerprint != "" {
  167. cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint})
  168. }
  169. if len(opts.KeyTypes) > 0 {
  170. cond = cond.And(builder.In("`type`", opts.KeyTypes))
  171. }
  172. if opts.NotKeytype > 0 {
  173. cond = cond.And(builder.Neq{"`type`": opts.NotKeytype})
  174. }
  175. if opts.LoginSourceID > 0 {
  176. cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID})
  177. }
  178. return cond
  179. }
  180. // UpdatePublicKeyUpdated updates public key use time.
  181. func UpdatePublicKeyUpdated(ctx context.Context, id int64) error {
  182. // Check if key exists before update as affected rows count is unreliable
  183. // and will return 0 affected rows if two updates are made at the same time
  184. if cnt, err := db.GetEngine(ctx).ID(id).Count(&PublicKey{}); err != nil {
  185. return err
  186. } else if cnt != 1 {
  187. return ErrKeyNotExist{id}
  188. }
  189. _, err := db.GetEngine(ctx).ID(id).Cols("updated_unix").Update(&PublicKey{
  190. UpdatedUnix: timeutil.TimeStampNow(),
  191. })
  192. if err != nil {
  193. return err
  194. }
  195. return nil
  196. }
  197. // PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key
  198. func PublicKeysAreExternallyManaged(ctx context.Context, keys []*PublicKey) ([]bool, error) {
  199. sourceCache := make(map[int64]*auth.Source, len(keys))
  200. externals := make([]bool, len(keys))
  201. for i, key := range keys {
  202. if key.LoginSourceID == 0 {
  203. externals[i] = false
  204. continue
  205. }
  206. source, ok := sourceCache[key.LoginSourceID]
  207. if !ok {
  208. var err error
  209. source, err = auth.GetSourceByID(ctx, key.LoginSourceID)
  210. if err != nil {
  211. if auth.IsErrSourceNotExist(err) {
  212. externals[i] = false
  213. sourceCache[key.LoginSourceID] = &auth.Source{
  214. ID: key.LoginSourceID,
  215. }
  216. continue
  217. }
  218. return nil, err
  219. }
  220. }
  221. if sshKeyProvider, ok := source.Cfg.(auth.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
  222. // Disable setting SSH keys for this user
  223. externals[i] = true
  224. }
  225. }
  226. return externals, nil
  227. }
  228. // PublicKeyIsExternallyManaged returns whether the provided KeyID represents an externally managed Key
  229. func PublicKeyIsExternallyManaged(ctx context.Context, id int64) (bool, error) {
  230. key, err := GetPublicKeyByID(ctx, id)
  231. if err != nil {
  232. return false, err
  233. }
  234. if key.LoginSourceID == 0 {
  235. return false, nil
  236. }
  237. source, err := auth.GetSourceByID(ctx, key.LoginSourceID)
  238. if err != nil {
  239. if auth.IsErrSourceNotExist(err) {
  240. return false, nil
  241. }
  242. return false, err
  243. }
  244. if sshKeyProvider, ok := source.Cfg.(auth.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
  245. // Disable setting SSH keys for this user
  246. return true, nil
  247. }
  248. return false, nil
  249. }
  250. // deleteKeysMarkedForDeletion returns true if ssh keys needs update
  251. func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, error) {
  252. return db.WithTx2(ctx, func(ctx context.Context) (bool, error) {
  253. // Delete keys marked for deletion
  254. var sshKeysNeedUpdate bool
  255. for _, KeyToDelete := range keys {
  256. key, err := SearchPublicKeyByContent(ctx, KeyToDelete)
  257. if err != nil {
  258. log.Error("SearchPublicKeyByContent: %v", err)
  259. continue
  260. }
  261. if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
  262. log.Error("DeleteByID[PublicKey]: %v", err)
  263. continue
  264. }
  265. sshKeysNeedUpdate = true
  266. }
  267. return sshKeysNeedUpdate, nil
  268. })
  269. }
  270. // AddPublicKeysBySource add a users public keys. Returns true if there are changes.
  271. func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool {
  272. var sshKeysNeedUpdate bool
  273. for _, sshKey := range sshPublicKeys {
  274. var err error
  275. found := false
  276. keys := []byte(sshKey)
  277. loop:
  278. for len(keys) > 0 && err == nil {
  279. var out ssh.PublicKey
  280. // We ignore options as they are not relevant to Gitea
  281. out, _, _, keys, err = ssh.ParseAuthorizedKey(keys)
  282. if err != nil {
  283. break loop
  284. }
  285. found = true
  286. marshalled := string(ssh.MarshalAuthorizedKey(out))
  287. marshalled = marshalled[:len(marshalled)-1]
  288. sshKeyName := fmt.Sprintf("%s-%s", s.Name, ssh.FingerprintSHA256(out))
  289. if _, err := AddPublicKey(ctx, usr.ID, sshKeyName, marshalled, s.ID); err != nil {
  290. if IsErrKeyAlreadyExist(err) {
  291. log.Trace("AddPublicKeysBySource[%s]: Public SSH Key %s already exists for user", sshKeyName, usr.Name)
  292. } else {
  293. log.Error("AddPublicKeysBySource[%s]: Error adding Public SSH Key for user %s: %v", sshKeyName, usr.Name, err)
  294. }
  295. } else {
  296. log.Trace("AddPublicKeysBySource[%s]: Added Public SSH Key for user %s", sshKeyName, usr.Name)
  297. sshKeysNeedUpdate = true
  298. }
  299. }
  300. if !found && err != nil {
  301. log.Warn("AddPublicKeysBySource[%s]: Skipping invalid Public SSH Key for user %s: %v", s.Name, usr.Name, sshKey)
  302. }
  303. }
  304. return sshKeysNeedUpdate
  305. }
  306. // SynchronizePublicKeys updates a user's public keys. Returns true if there are changes.
  307. func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool {
  308. var sshKeysNeedUpdate bool
  309. log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name)
  310. // Get Public Keys from DB with the current auth source
  311. var giteaKeys []string
  312. keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{
  313. OwnerID: usr.ID,
  314. LoginSourceID: s.ID,
  315. })
  316. if err != nil {
  317. log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err)
  318. }
  319. for _, v := range keys {
  320. giteaKeys = append(giteaKeys, v.OmitEmail())
  321. }
  322. // Process the provided keys to remove duplicates and name part
  323. var providedKeys []string
  324. for _, v := range sshPublicKeys {
  325. sshKeySplit := strings.Split(v, " ")
  326. if len(sshKeySplit) > 1 {
  327. key := strings.Join(sshKeySplit[:2], " ")
  328. if !util.SliceContainsString(providedKeys, key) {
  329. providedKeys = append(providedKeys, key)
  330. }
  331. }
  332. }
  333. // Check if Public Key sync is needed
  334. if util.SliceSortedEqual(giteaKeys, providedKeys) {
  335. log.Trace("synchronizePublicKeys[%s]: Public Keys are already in sync for %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
  336. return false
  337. }
  338. log.Trace("synchronizePublicKeys[%s]: Public Key needs update for user %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
  339. // Add new Public SSH Keys that doesn't already exist in DB
  340. var newKeys []string
  341. for _, key := range providedKeys {
  342. if !util.SliceContainsString(giteaKeys, key) {
  343. newKeys = append(newKeys, key)
  344. }
  345. }
  346. if AddPublicKeysBySource(ctx, usr, s, newKeys) {
  347. sshKeysNeedUpdate = true
  348. }
  349. // Mark keys from DB that no longer exist in the source for deletion
  350. var giteaKeysToDelete []string
  351. for _, giteaKey := range giteaKeys {
  352. if !util.SliceContainsString(providedKeys, giteaKey) {
  353. log.Trace("synchronizePublicKeys[%s]: Marking Public SSH Key for deletion for user %s: %v", s.Name, usr.Name, giteaKey)
  354. giteaKeysToDelete = append(giteaKeysToDelete, giteaKey)
  355. }
  356. }
  357. // Delete keys from DB that no longer exist in the source
  358. needUpd, err := deleteKeysMarkedForDeletion(ctx, giteaKeysToDelete)
  359. if err != nil {
  360. log.Error("synchronizePublicKeys[%s]: Error deleting Public Keys marked for deletion for user %s: %v", s.Name, usr.Name, err)
  361. }
  362. if needUpd {
  363. sshKeysNeedUpdate = true
  364. }
  365. return sshKeysNeedUpdate
  366. }