main pico / pkg / db / postgres / keys.go
Antonio Mika  ·  2026-10-10
  1package postgres
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"github.com/jmoiron/sqlx"
  8	"github.com/picosh/pico/pkg/db"
  9)
 10
 11func (me *PsqlDB) insertPublicKeyWithTx(userID, key, name string, tx *sqlx.Tx) error {
 12	pk, _ := me.findPublicKeyByKey(key)
 13	if pk != nil {
 14		return db.ErrPublicKeyTaken
 15	}
 16	query := `INSERT INTO public_keys (user_id, public_key, name) VALUES ($1, $2, $3)`
 17	_, err := tx.Exec(query, userID, key, name)
 18	return err
 19}
 20
 21func (me *PsqlDB) InsertPublicKey(userID, key, name string) error {
 22	pk, _ := me.findPublicKeyByKey(key)
 23	if pk != nil {
 24		return db.ErrPublicKeyTaken
 25	}
 26	query := `INSERT INTO public_keys (user_id, public_key, name) VALUES ($1, $2, $3)`
 27	_, err := me.Db.Exec(query, userID, key, name)
 28	return err
 29}
 30
 31func (me *PsqlDB) UpdatePublicKey(pubkeyID, name string) (*db.PublicKey, error) {
 32	pk, err := me.findPublicKey(pubkeyID)
 33	if err != nil {
 34		return nil, err
 35	}
 36
 37	query := `UPDATE public_keys SET name=$1 WHERE id=$2;`
 38	_, err = me.Db.Exec(query, name, pk.ID)
 39	if err != nil {
 40		return nil, err
 41	}
 42
 43	pk, err = me.findPublicKey(pubkeyID)
 44	if err != nil {
 45		return nil, err
 46	}
 47	return pk, nil
 48}
 49
 50func (me *PsqlDB) findPublicKeyByKey(key string) (*db.PublicKey, error) {
 51	var keys []*db.PublicKey
 52	rs, err := me.Db.Queryx(`SELECT id, user_id, name, public_key, created_at FROM public_keys WHERE public_key = $1`, key)
 53	if err != nil {
 54		return nil, err
 55	}
 56	defer func() { _ = rs.Close() }()
 57
 58	for rs.Next() {
 59		pk := &db.PublicKey{}
 60		err := rs.Scan(&pk.ID, &pk.UserID, &pk.Name, &pk.Key, &pk.CreatedAt)
 61		if err != nil {
 62			return nil, err
 63		}
 64
 65		keys = append(keys, pk)
 66	}
 67
 68	if rs.Err() != nil {
 69		return nil, rs.Err()
 70	}
 71
 72	if len(keys) == 0 {
 73		return nil, fmt.Errorf("pubkey not found in our database: [%s]", key)
 74	}
 75
 76	// When we run PublicKeyByKey and there are multiple public keys returned from the database
 77	// that should mean that we don't have the correct username for this public key.
 78	// When that happens we need to reject the authentication and ask the user to provide the correct
 79	// username when using ssh.  So instead of `ssh <domain>` it should be `ssh user@<domain>`
 80	if len(keys) > 1 {
 81		return nil, &db.ErrMultiplePublicKeys{}
 82	}
 83
 84	return keys[0], nil
 85}
 86
 87func (me *PsqlDB) findPublicKey(pubkeyID string) (*db.PublicKey, error) {
 88	pk := &db.PublicKey{}
 89	err := me.Db.Get(pk, `SELECT * FROM public_keys WHERE id = $1`, pubkeyID)
 90	if err != nil {
 91		return nil, err
 92	}
 93	return pk, nil
 94}
 95
 96func (me *PsqlDB) FindKeysByUser(user *db.User) ([]*db.PublicKey, error) {
 97	var keys []*db.PublicKey
 98	err := me.Db.Select(&keys, `SELECT * FROM public_keys WHERE user_id = $1 ORDER BY created_at ASC`, user.ID)
 99	if err != nil {
100		return nil, err
101	}
102	return keys, nil
103}
104
105func (me *PsqlDB) RemoveKeys(keyIDs []string) error {
106	param := "{" + strings.Join(keyIDs, ",") + "}"
107	_, err := me.Db.Exec(`DELETE FROM public_keys WHERE id = ANY($1::uuid[])`, param)
108	return err
109}