Files
aperture/aperturedb/secrets.go
Boris Nagaev a4431801ef multi: replace LSAT with L402
auth: LsatAuthenticator -> L402Authenticator
sed -i 's/LsatAuthenticator/L402Authenticator/g' aperture.go auth/authenticator.go auth/authenticator_test.go

rename package lsat to l402
git mv lsat/ l402
sed 's@aperture/lsat@aperture/l402@g' -i `git grep -l aperture/lsat`
sed -i 's@package lsat@package l402@' `git grep -l 'package lsat'`
sed -i 's@lsat\.@l402.@g' -i `git grep -l 'lsat\.'`
sed 's@l402.Id@lsat.Id@' -i mint/mint_test.go

replace lsat with l402 in the code
sed 's@lsat@l402@' -i mint/mint_test.go
sed 's@Lsat@L402@' -i l402/client_interceptor.go
sed 's@lsatstore@l402store@' -i l402/store_test.go

replace LSAT to L402 in comments
sed '/\/\//s@LSAT@L402@g' -i `git grep -l '//.*LSAT'`

replace LSAT -> L402 in the code, skip when a string starts with it
sed 's@\([^"/]\)LSAT@\1L402@g' -i `git grep -l LSAT`
2024-04-16 19:33:03 -03:00

172 lines
4.2 KiB
Go

package aperturedb
import (
"context"
"crypto/rand"
"crypto/sha256"
"database/sql"
"fmt"
"github.com/lightninglabs/aperture/aperturedb/sqlc"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/aperture/mint"
"github.com/lightningnetwork/lnd/clock"
)
type (
// NewSecret is a struct that contains the parameters required to insert
// a new secret into the database.
NewSecret = sqlc.InsertSecretParams
)
// SecretsDB is an interface that defines the set of operations that can be
// executed against the secrets database.
type SecretsDB interface {
// InsertSecret inserts a new secret into the database.
InsertSecret(ctx context.Context, arg NewSecret) (int32, error)
// GetSecretByHash returns the secret that corresponds to the given
// hash.
GetSecretByHash(ctx context.Context, hash []byte) ([]byte, error)
// DeleteSecretByHash removes the secret that corresponds to the given
// hash.
DeleteSecretByHash(ctx context.Context, hash []byte) (int64, error)
}
// SecretsTxOptions defines the set of db txn options the SecretsStore
// understands.
type SecretsDBTxOptions struct {
// readOnly governs if a read only transaction is needed or not.
readOnly bool
}
// ReadOnly returns true if the transaction should be read only.
//
// NOTE: This implements the TxOptions
func (a *SecretsDBTxOptions) ReadOnly() bool {
return a.readOnly
}
// NewSecretsDBReadTx creates a new read transaction option set.
func NewSecretsDBReadTx() SecretsDBTxOptions {
return SecretsDBTxOptions{
readOnly: true,
}
}
// BatchedSecretsDB is a version of the SecretsDB that's capable of batched
// database operations.
type BatchedSecretsDB interface {
SecretsDB
BatchedTx[SecretsDB]
}
// SecretsStore represents a storage backend.
type SecretsStore struct {
db BatchedSecretsDB
clock clock.Clock
}
// NewSecretsStore creates a new SecretsStore instance given a open
// BatchedSecretsDB storage backend.
func NewSecretsStore(db BatchedSecretsDB) *SecretsStore {
return &SecretsStore{
db: db,
clock: clock.NewDefaultClock(),
}
}
// NewSecret creates a new cryptographically random secret which is
// keyed by the given hash.
func (s *SecretsStore) NewSecret(ctx context.Context,
hash [sha256.Size]byte) ([l402.SecretSize]byte, error) {
var secret [l402.SecretSize]byte
if _, err := rand.Read(secret[:]); err != nil {
return [l402.SecretSize]byte{}, err
}
var writeTxOpts SecretsDBTxOptions
err := s.db.ExecTx(ctx, &writeTxOpts, func(tx SecretsDB) error {
_, err := tx.InsertSecret(ctx, NewSecret{
Hash: hash[:],
Secret: secret[:],
CreatedAt: s.clock.Now().UTC(),
})
if err != nil {
return err
}
return nil
})
if err != nil {
return [l402.SecretSize]byte{}, fmt.Errorf("unable to insert "+
"new secret for hash(%x): %w", hash, err)
}
return secret, nil
}
// GetSecret returns the cryptographically random secret that
// corresponds to the given hash. If there is no secret, then
// ErrSecretNotFound is returned.
func (s *SecretsStore) GetSecret(ctx context.Context,
hash [sha256.Size]byte) ([l402.SecretSize]byte, error) {
var secret [l402.SecretSize]byte
readOpts := NewSecretsDBReadTx()
err := s.db.ExecTx(ctx, &readOpts, func(db SecretsDB) error {
secretRow, err := db.GetSecretByHash(ctx, hash[:])
switch {
case err == sql.ErrNoRows:
return mint.ErrSecretNotFound
case err != nil:
return err
}
copy(secret[:], secretRow)
return nil
})
if err != nil {
return [l402.SecretSize]byte{}, fmt.Errorf("unable to get "+
"secret for hash(%x): %w", hash, err)
}
return secret, nil
}
// RevokeSecret removes the cryptographically random secret that
// corresponds to the given hash. This acts as a NOP if the secret does
// not exist.
func (s *SecretsStore) RevokeSecret(ctx context.Context,
hash [sha256.Size]byte) error {
var writeTxOpts SecretsDBTxOptions
err := s.db.ExecTx(ctx, &writeTxOpts, func(tx SecretsDB) error {
nRows, err := tx.DeleteSecretByHash(ctx, hash[:])
if err != nil {
return err
}
if nRows != 1 {
log.Info("deleting secret(%x) did not affect %w rows",
hash, nRows)
}
return nil
})
if err != nil {
return fmt.Errorf("unable to revoke secret for hash(%x): %w",
hash, err)
}
return nil
}