Changes: - forbid to overwrite secrets of other users - add some logs of success - add validation on code (length 50) - use same limiter for POST requests
65 lines
1.8 KiB
Go
65 lines
1.8 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
var ErrNotFound = errors.New("secret not found")
|
|
var ErrForbidden = errors.New("code owned by another user")
|
|
|
|
type SecretsRepository struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func NewSecretsRepository(db *sql.DB) *SecretsRepository {
|
|
return &SecretsRepository{db: db}
|
|
}
|
|
|
|
func (r *SecretsRepository) Get(ctx context.Context, code string) (string, error) {
|
|
var value string
|
|
err := r.db.QueryRowContext(ctx, "SELECT value FROM secrets WHERE code = ?", code).Scan(&value)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return "", ErrNotFound
|
|
}
|
|
if err != nil {
|
|
return "", fmt.Errorf("could not get secret: %w", err)
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// Set stores value under code, owned by user. If code already exists and is
|
|
// owned by a different user, it returns ErrForbidden and leaves the stored
|
|
// value untouched.
|
|
func (r *SecretsRepository) Set(ctx context.Context, code, value, user string) error {
|
|
tx, err := r.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("could not begin transaction: %w", err)
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
|
|
var owner string
|
|
err = tx.QueryRowContext(ctx, "SELECT user FROM secrets WHERE code = ?", code).Scan(&owner)
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
return fmt.Errorf("could not check secret owner: %w", err)
|
|
}
|
|
if !errors.Is(err, sql.ErrNoRows) && owner != user {
|
|
return ErrForbidden
|
|
}
|
|
|
|
if _, err := tx.ExecContext(ctx, `
|
|
INSERT INTO secrets (code, value, user, created_at, updated_at)
|
|
VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
ON CONFLICT(code) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP
|
|
`, code, value, user); err != nil {
|
|
return fmt.Errorf("could not set secret: %w", err)
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return fmt.Errorf("could not commit transaction: %w", err)
|
|
}
|
|
return nil
|
|
}
|