summaryrefslogtreecommitdiffstats
path: root/models/ssh_key.go
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2021-12-10 16:14:24 +0800
committerGitHub <noreply@github.com>2021-12-10 16:14:24 +0800
commit3ca5dc7e32b372d14ff80d96f14b8f6a805862f1 (patch)
tree50d193ed0dacf2888d57b193a9b0d36065aff205 /models/ssh_key.go
parent0a9fcf63a49799ad3b0f146c54879161bac61e10 (diff)
downloadgitea-3ca5dc7e32b372d14ff80d96f14b8f6a805862f1.tar.gz
gitea-3ca5dc7e32b372d14ff80d96f14b8f6a805862f1.zip
Move keys to models/asymkey (#17917)
* Move keys to models/keys * Rename models/keys -> models/asymkey * change the missed package name * Fix package alias * Fix test * Fix docs * Fix test * Fix test * merge
Diffstat (limited to 'models/ssh_key.go')
-rw-r--r--models/ssh_key.go498
1 files changed, 0 insertions, 498 deletions
diff --git a/models/ssh_key.go b/models/ssh_key.go
deleted file mode 100644
index 0d97096149..0000000000
--- a/models/ssh_key.go
+++ /dev/null
@@ -1,498 +0,0 @@
-// Copyright 2014 The Gogs Authors. All rights reserved.
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "fmt"
- "strings"
- "time"
-
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/login"
- "code.gitea.io/gitea/models/perm"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
-
- "golang.org/x/crypto/ssh"
- "xorm.io/builder"
-)
-
-// KeyType specifies the key type
-type KeyType int
-
-const (
- // KeyTypeUser specifies the user key
- KeyTypeUser = iota + 1
- // KeyTypeDeploy specifies the deploy key
- KeyTypeDeploy
- // KeyTypePrincipal specifies the authorized principal key
- KeyTypePrincipal
-)
-
-// PublicKey represents a user or deploy SSH public key.
-type PublicKey struct {
- ID int64 `xorm:"pk autoincr"`
- OwnerID int64 `xorm:"INDEX NOT NULL"`
- Name string `xorm:"NOT NULL"`
- Fingerprint string `xorm:"INDEX NOT NULL"`
- Content string `xorm:"TEXT NOT NULL"`
- Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 2"`
- Type KeyType `xorm:"NOT NULL DEFAULT 1"`
- LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
-
- CreatedUnix timeutil.TimeStamp `xorm:"created"`
- UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
- HasRecentActivity bool `xorm:"-"`
- HasUsed bool `xorm:"-"`
-}
-
-func init() {
- db.RegisterModel(new(PublicKey))
-}
-
-// AfterLoad is invoked from XORM after setting the values of all fields of this object.
-func (key *PublicKey) AfterLoad() {
- key.HasUsed = key.UpdatedUnix > key.CreatedUnix
- key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
-}
-
-// OmitEmail returns content of public key without email address.
-func (key *PublicKey) OmitEmail() string {
- return strings.Join(strings.Split(key.Content, " ")[:2], " ")
-}
-
-// AuthorizedString returns formatted public key string for authorized_keys file.
-//
-// TODO: Consider dropping this function
-func (key *PublicKey) AuthorizedString() string {
- return AuthorizedStringForKey(key)
-}
-
-func addKey(e db.Engine, key *PublicKey) (err error) {
- if len(key.Fingerprint) == 0 {
- key.Fingerprint, err = calcFingerprint(key.Content)
- if err != nil {
- return err
- }
- }
-
- // Save SSH key.
- if _, err = e.Insert(key); err != nil {
- return err
- }
-
- return appendAuthorizedKeysToFile(key)
-}
-
-// AddPublicKey adds new public key to database and authorized_keys file.
-func AddPublicKey(ownerID int64, name, content string, loginSourceID int64) (*PublicKey, error) {
- log.Trace(content)
-
- fingerprint, err := calcFingerprint(content)
- if err != nil {
- return nil, err
- }
-
- ctx, committer, err := db.TxContext()
- if err != nil {
- return nil, err
- }
- defer committer.Close()
- sess := db.GetEngine(ctx)
-
- if err := checkKeyFingerprint(sess, fingerprint); err != nil {
- return nil, err
- }
-
- // Key name of same user cannot be duplicated.
- has, err := sess.
- Where("owner_id = ? AND name = ?", ownerID, name).
- Get(new(PublicKey))
- if err != nil {
- return nil, err
- } else if has {
- return nil, ErrKeyNameAlreadyUsed{ownerID, name}
- }
-
- key := &PublicKey{
- OwnerID: ownerID,
- Name: name,
- Fingerprint: fingerprint,
- Content: content,
- Mode: perm.AccessModeWrite,
- Type: KeyTypeUser,
- LoginSourceID: loginSourceID,
- }
- if err = addKey(sess, key); err != nil {
- return nil, fmt.Errorf("addKey: %v", err)
- }
-
- return key, committer.Commit()
-}
-
-// GetPublicKeyByID returns public key by given ID.
-func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
- key := new(PublicKey)
- has, err := db.GetEngine(db.DefaultContext).
- ID(keyID).
- Get(key)
- if err != nil {
- return nil, err
- } else if !has {
- return nil, ErrKeyNotExist{keyID}
- }
- return key, nil
-}
-
-func searchPublicKeyByContentWithEngine(e db.Engine, content string) (*PublicKey, error) {
- key := new(PublicKey)
- has, err := e.
- Where("content like ?", content+"%").
- Get(key)
- if err != nil {
- return nil, err
- } else if !has {
- return nil, ErrKeyNotExist{}
- }
- return key, nil
-}
-
-// SearchPublicKeyByContent searches content as prefix (leak e-mail part)
-// and returns public key found.
-func SearchPublicKeyByContent(content string) (*PublicKey, error) {
- return searchPublicKeyByContentWithEngine(db.GetEngine(db.DefaultContext), content)
-}
-
-func searchPublicKeyByContentExactWithEngine(e db.Engine, content string) (*PublicKey, error) {
- key := new(PublicKey)
- has, err := e.
- Where("content = ?", content).
- Get(key)
- if err != nil {
- return nil, err
- } else if !has {
- return nil, ErrKeyNotExist{}
- }
- return key, nil
-}
-
-// SearchPublicKeyByContentExact searches content
-// and returns public key found.
-func SearchPublicKeyByContentExact(content string) (*PublicKey, error) {
- return searchPublicKeyByContentExactWithEngine(db.GetEngine(db.DefaultContext), content)
-}
-
-// SearchPublicKey returns a list of public keys matching the provided arguments.
-func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) {
- keys := make([]*PublicKey, 0, 5)
- cond := builder.NewCond()
- if uid != 0 {
- cond = cond.And(builder.Eq{"owner_id": uid})
- }
- if fingerprint != "" {
- cond = cond.And(builder.Eq{"fingerprint": fingerprint})
- }
- return keys, db.GetEngine(db.DefaultContext).Where(cond).Find(&keys)
-}
-
-// ListPublicKeys returns a list of public keys belongs to given user.
-func ListPublicKeys(uid int64, listOptions db.ListOptions) ([]*PublicKey, error) {
- sess := db.GetEngine(db.DefaultContext).Where("owner_id = ? AND type != ?", uid, KeyTypePrincipal)
- if listOptions.Page != 0 {
- sess = db.SetSessionPagination(sess, &listOptions)
-
- keys := make([]*PublicKey, 0, listOptions.PageSize)
- return keys, sess.Find(&keys)
- }
-
- keys := make([]*PublicKey, 0, 5)
- return keys, sess.Find(&keys)
-}
-
-// CountPublicKeys count public keys a user has
-func CountPublicKeys(userID int64) (int64, error) {
- sess := db.GetEngine(db.DefaultContext).Where("owner_id = ? AND type != ?", userID, KeyTypePrincipal)
- return sess.Count(&PublicKey{})
-}
-
-// ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source.
-func ListPublicKeysBySource(uid, loginSourceID int64) ([]*PublicKey, error) {
- keys := make([]*PublicKey, 0, 5)
- return keys, db.GetEngine(db.DefaultContext).
- Where("owner_id = ? AND login_source_id = ?", uid, loginSourceID).
- Find(&keys)
-}
-
-// UpdatePublicKeyUpdated updates public key use time.
-func UpdatePublicKeyUpdated(id int64) error {
- // Check if key exists before update as affected rows count is unreliable
- // and will return 0 affected rows if two updates are made at the same time
- if cnt, err := db.GetEngine(db.DefaultContext).ID(id).Count(&PublicKey{}); err != nil {
- return err
- } else if cnt != 1 {
- return ErrKeyNotExist{id}
- }
-
- _, err := db.GetEngine(db.DefaultContext).ID(id).Cols("updated_unix").Update(&PublicKey{
- UpdatedUnix: timeutil.TimeStampNow(),
- })
- if err != nil {
- return err
- }
- return nil
-}
-
-// deletePublicKeys does the actual key deletion but does not update authorized_keys file.
-func deletePublicKeys(e db.Engine, keyIDs ...int64) error {
- if len(keyIDs) == 0 {
- return nil
- }
-
- _, err := e.In("id", keyIDs).Delete(new(PublicKey))
- return err
-}
-
-// PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key
-func PublicKeysAreExternallyManaged(keys []*PublicKey) ([]bool, error) {
- sources := make([]*login.Source, 0, 5)
- externals := make([]bool, len(keys))
-keyloop:
- for i, key := range keys {
- if key.LoginSourceID == 0 {
- externals[i] = false
- continue keyloop
- }
-
- var source *login.Source
-
- sourceloop:
- for _, s := range sources {
- if s.ID == key.LoginSourceID {
- source = s
- break sourceloop
- }
- }
-
- if source == nil {
- var err error
- source, err = login.GetSourceByID(key.LoginSourceID)
- if err != nil {
- if login.IsErrSourceNotExist(err) {
- externals[i] = false
- sources[i] = &login.Source{
- ID: key.LoginSourceID,
- }
- continue keyloop
- }
- return nil, err
- }
- }
-
- if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
- // Disable setting SSH keys for this user
- externals[i] = true
- }
- }
-
- return externals, nil
-}
-
-// PublicKeyIsExternallyManaged returns whether the provided KeyID represents an externally managed Key
-func PublicKeyIsExternallyManaged(id int64) (bool, error) {
- key, err := GetPublicKeyByID(id)
- if err != nil {
- return false, err
- }
- if key.LoginSourceID == 0 {
- return false, nil
- }
- source, err := login.GetSourceByID(key.LoginSourceID)
- if err != nil {
- if login.IsErrSourceNotExist(err) {
- return false, nil
- }
- return false, err
- }
- if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
- // Disable setting SSH keys for this user
- return true, nil
- }
- return false, nil
-}
-
-// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
-func DeletePublicKey(doer *user_model.User, id int64) (err error) {
- key, err := GetPublicKeyByID(id)
- if err != nil {
- return err
- }
-
- // Check if user has access to delete this key.
- if !doer.IsAdmin && doer.ID != key.OwnerID {
- return ErrKeyAccessDenied{doer.ID, key.ID, "public"}
- }
-
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err = deletePublicKeys(db.GetEngine(ctx), id); err != nil {
- return err
- }
-
- if err = committer.Commit(); err != nil {
- return err
- }
- committer.Close()
-
- if key.Type == KeyTypePrincipal {
- return RewriteAllPrincipalKeys()
- }
-
- return RewriteAllPublicKeys()
-}
-
-// deleteKeysMarkedForDeletion returns true if ssh keys needs update
-func deleteKeysMarkedForDeletion(keys []string) (bool, error) {
- // Start session
- ctx, committer, err := db.TxContext()
- if err != nil {
- return false, err
- }
- defer committer.Close()
- sess := db.GetEngine(ctx)
-
- // Delete keys marked for deletion
- var sshKeysNeedUpdate bool
- for _, KeyToDelete := range keys {
- key, err := searchPublicKeyByContentWithEngine(sess, KeyToDelete)
- if err != nil {
- log.Error("SearchPublicKeyByContent: %v", err)
- continue
- }
- if err = deletePublicKeys(sess, key.ID); err != nil {
- log.Error("deletePublicKeys: %v", err)
- continue
- }
- sshKeysNeedUpdate = true
- }
-
- if err := committer.Commit(); err != nil {
- return false, err
- }
-
- return sshKeysNeedUpdate, nil
-}
-
-// AddPublicKeysBySource add a users public keys. Returns true if there are changes.
-func AddPublicKeysBySource(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool {
- var sshKeysNeedUpdate bool
- for _, sshKey := range sshPublicKeys {
- var err error
- found := false
- keys := []byte(sshKey)
- loop:
- for len(keys) > 0 && err == nil {
- var out ssh.PublicKey
- // We ignore options as they are not relevant to Gitea
- out, _, _, keys, err = ssh.ParseAuthorizedKey(keys)
- if err != nil {
- break loop
- }
- found = true
- marshalled := string(ssh.MarshalAuthorizedKey(out))
- marshalled = marshalled[:len(marshalled)-1]
- sshKeyName := fmt.Sprintf("%s-%s", s.Name, ssh.FingerprintSHA256(out))
-
- if _, err := AddPublicKey(usr.ID, sshKeyName, marshalled, s.ID); err != nil {
- if IsErrKeyAlreadyExist(err) {
- log.Trace("AddPublicKeysBySource[%s]: Public SSH Key %s already exists for user", sshKeyName, usr.Name)
- } else {
- log.Error("AddPublicKeysBySource[%s]: Error adding Public SSH Key for user %s: %v", sshKeyName, usr.Name, err)
- }
- } else {
- log.Trace("AddPublicKeysBySource[%s]: Added Public SSH Key for user %s", sshKeyName, usr.Name)
- sshKeysNeedUpdate = true
- }
- }
- if !found && err != nil {
- log.Warn("AddPublicKeysBySource[%s]: Skipping invalid Public SSH Key for user %s: %v", s.Name, usr.Name, sshKey)
- }
- }
- return sshKeysNeedUpdate
-}
-
-// SynchronizePublicKeys updates a users public keys. Returns true if there are changes.
-func SynchronizePublicKeys(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool {
- var sshKeysNeedUpdate bool
-
- log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name)
-
- // Get Public Keys from DB with current LDAP source
- var giteaKeys []string
- keys, err := ListPublicKeysBySource(usr.ID, s.ID)
- if err != nil {
- log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err)
- }
-
- for _, v := range keys {
- giteaKeys = append(giteaKeys, v.OmitEmail())
- }
-
- // Process the provided keys to remove duplicates and name part
- var providedKeys []string
- for _, v := range sshPublicKeys {
- sshKeySplit := strings.Split(v, " ")
- if len(sshKeySplit) > 1 {
- key := strings.Join(sshKeySplit[:2], " ")
- if !util.ExistsInSlice(key, providedKeys) {
- providedKeys = append(providedKeys, key)
- }
- }
- }
-
- // Check if Public Key sync is needed
- if util.IsEqualSlice(giteaKeys, providedKeys) {
- log.Trace("synchronizePublicKeys[%s]: Public Keys are already in sync for %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
- return false
- }
- log.Trace("synchronizePublicKeys[%s]: Public Key needs update for user %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
-
- // Add new Public SSH Keys that doesn't already exist in DB
- var newKeys []string
- for _, key := range providedKeys {
- if !util.ExistsInSlice(key, giteaKeys) {
- newKeys = append(newKeys, key)
- }
- }
- if AddPublicKeysBySource(usr, s, newKeys) {
- sshKeysNeedUpdate = true
- }
-
- // Mark keys from DB that no longer exist in the source for deletion
- var giteaKeysToDelete []string
- for _, giteaKey := range giteaKeys {
- if !util.ExistsInSlice(giteaKey, providedKeys) {
- log.Trace("synchronizePublicKeys[%s]: Marking Public SSH Key for deletion for user %s: %v", s.Name, usr.Name, giteaKey)
- giteaKeysToDelete = append(giteaKeysToDelete, giteaKey)
- }
- }
-
- // Delete keys from DB that no longer exist in the source
- needUpd, err := deleteKeysMarkedForDeletion(giteaKeysToDelete)
- if err != nil {
- log.Error("synchronizePublicKeys[%s]: Error deleting Public Keys marked for deletion for user %s: %v", s.Name, usr.Name, err)
- }
- if needUpd {
- sshKeysNeedUpdate = true
- }
-
- return sshKeysNeedUpdate
-}