forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
1// an sqlite3 backed secret manager 2package secrets 3 4import ( 5 "database/sql" 6 "fmt" 7 "time" 8 9 _ "github.com/mattn/go-sqlite3" 10) 11 12type SqliteManager struct { 13 db *sql.DB 14 tableName string 15} 16 17type SqliteManagerOpt func(*SqliteManager) 18 19func WithTableName(name string) SqliteManagerOpt { 20 return func(s *SqliteManager) { 21 s.tableName = name 22 } 23} 24 25func NewSQLiteManager(dbPath string, opts ...SqliteManagerOpt) (*SqliteManager, error) { 26 db, err := sql.Open("sqlite3", dbPath) 27 if err != nil { 28 return nil, fmt.Errorf("failed to open sqlite database: %w", err) 29 } 30 31 manager := &SqliteManager{ 32 db: db, 33 tableName: "secrets", 34 } 35 36 for _, o := range opts { 37 o(manager) 38 } 39 40 if err := manager.init(); err != nil { 41 return nil, err 42 } 43 44 return manager, nil 45} 46 47// creates a table and sets up the schema, migrations if any can go here 48func (s *SqliteManager) init() error { 49 createTable := 50 `create table if not exists ` + s.tableName + `( 51 id integer primary key autoincrement, 52 repo text not null, 53 key text not null, 54 value text not null, 55 created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 56 created_by text not null, 57 58 unique(repo, key) 59 );` 60 _, err := s.db.Exec(createTable) 61 return err 62} 63 64func (s *SqliteManager) AddSecret(secret UnlockedSecret) error { 65 query := fmt.Sprintf(` 66 insert or ignore into %s (repo, key, value, created_by) 67 values (?, ?, ?, ?); 68 `, s.tableName) 69 70 res, err := s.db.Exec(query, secret.Repo, secret.Key, secret.Value, secret.CreatedBy) 71 if err != nil { 72 return err 73 } 74 75 num, err := res.RowsAffected() 76 if err != nil { 77 return err 78 } 79 80 if num == 0 { 81 return ErrKeyAlreadyPresent 82 } 83 84 return nil 85} 86 87func (s *SqliteManager) RemoveSecret(secret Secret[any]) error { 88 query := fmt.Sprintf(` 89 delete from %s where repo = ? and key = ?; 90 `, s.tableName) 91 92 res, err := s.db.Exec(query, secret.Repo, secret.Key) 93 if err != nil { 94 return err 95 } 96 97 num, err := res.RowsAffected() 98 if err != nil { 99 return err 100 } 101 102 if num == 0 { 103 return ErrKeyNotFound 104 } 105 106 return nil 107} 108 109func (s *SqliteManager) GetSecretsLocked(didSlashRepo DidSlashRepo) ([]LockedSecret, error) { 110 query := fmt.Sprintf(` 111 select repo, key, created_at, created_by from %s where repo = ?; 112 `, s.tableName) 113 114 rows, err := s.db.Query(query, didSlashRepo) 115 if err != nil { 116 return nil, err 117 } 118 119 var ls []LockedSecret 120 for rows.Next() { 121 var l LockedSecret 122 var createdAt string 123 if err = rows.Scan(&l.Repo, &l.Key, &createdAt, &l.CreatedBy); err != nil { 124 return nil, err 125 } 126 127 if t, err := time.Parse(time.RFC3339, createdAt); err == nil { 128 l.CreatedAt = t 129 } 130 131 ls = append(ls, l) 132 } 133 134 if err = rows.Err(); err != nil { 135 return nil, err 136 } 137 138 return ls, nil 139} 140 141func (s *SqliteManager) GetSecretsUnlocked(didSlashRepo DidSlashRepo) ([]UnlockedSecret, error) { 142 query := fmt.Sprintf(` 143 select repo, key, value, created_at, created_by from %s where repo = ?; 144 `, s.tableName) 145 146 rows, err := s.db.Query(query, didSlashRepo) 147 if err != nil { 148 return nil, err 149 } 150 151 var ls []UnlockedSecret 152 for rows.Next() { 153 var l UnlockedSecret 154 var createdAt string 155 if err = rows.Scan(&l.Repo, &l.Key, &l.Value, &createdAt, &l.CreatedBy); err != nil { 156 return nil, err 157 } 158 159 if t, err := time.Parse(time.RFC3339, createdAt); err == nil { 160 l.CreatedAt = t 161 } 162 163 ls = append(ls, l) 164 } 165 166 if err = rows.Err(); err != nil { 167 return nil, err 168 } 169 170 return ls, nil 171}