From 606e0a3329a3534a00889eee19c25e7d432f7d2d Mon Sep 17 00:00:00 2001 From: bndw Date: Sat, 14 Feb 2026 10:11:16 -0800 Subject: refactor: restructure auth config for better UX Changed from flat structure to hierarchical read/write config: Before: auth: enabled: bool required: bool allowed_npubs_read: [] allowed_npubs_write: [] After: auth: read: enabled: bool allowed_npubs: [] write: enabled: bool allowed_npubs: [] Three states per operation: - enabled=false: no auth, allow all - enabled=true, allowed_npubs=[]: auth required, any valid signature - enabled=true, allowed_npubs=[...]: auth required, whitelist only Much clearer semantics and easier to reason about. --- internal/auth/README.md | 37 ++++++++----- internal/auth/auth_test.go | 9 +++- internal/auth/interceptor.go | 118 +++++++++++++++++++++++++---------------- internal/config/README.md | 70 ++++++++++++++---------- internal/config/config.go | 57 ++++++++++++-------- internal/config/config_test.go | 102 +++++++++++++++++++++-------------- 6 files changed, 242 insertions(+), 151 deletions(-) (limited to 'internal') diff --git a/internal/auth/README.md b/internal/auth/README.md index 366e110..de37010 100644 --- a/internal/auth/README.md +++ b/internal/auth/README.md @@ -142,8 +142,15 @@ import ( // Create auth options authOpts := &auth.InterceptorOptions{ + Read: auth.OperationAuthConfig{ + Enabled: true, // Require auth for reads + AllowedNpubs: nil, // Accept any valid signature + }, + Write: auth.OperationAuthConfig{ + Enabled: true, + AllowedNpubs: []string{"hex-pubkey-1", "hex-pubkey-2"}, // Whitelist + }, TimestampWindow: 60, // Accept events within 60 seconds - Required: true, // Reject unauthenticated requests } // Create gRPC server with interceptors @@ -206,23 +213,25 @@ authOpts := &auth.InterceptorOptions{ ### InterceptorOptions +- **`Read`**: Authentication config for read operations (Subscribe, QueryEvents, CountEvents) + - **`Enabled`**: false = no auth (allow all), true = auth required + - **`AllowedNpubs`**: Optional whitelist (hex format, normalized from npub in config) + - If `Enabled=false`: no auth required + - If `Enabled=true && AllowedNpubs=[]`: auth required, any valid signature accepted + - If `Enabled=true && AllowedNpubs=[...]`: auth required, only whitelisted npubs accepted + +- **`Write`**: Authentication config for write operations (PublishEvent, PublishBatch) + - Same structure as `Read` + - **`TimestampWindow`**: Maximum age of events in seconds (default: 60) -- **`Required`**: Whether to reject unauthenticated requests (default: false) - **`ValidatePayload`**: Whether to verify payload hash when present (default: false) -- **`AllowedNpubsRead`**: Optional whitelist of allowed pubkeys for read operations (nil = allow all) - - Config accepts npub format only (human-readable bech32) - - Automatically normalized to hex format (computer-readable) at config load time - - Controls access to Query, Get, List, Subscribe, and other read methods -- **`AllowedNpubsWrite`**: Optional whitelist of allowed pubkeys for write operations (nil = allow all) - - Config accepts npub format only (human-readable bech32) - - Automatically normalized to hex format (computer-readable) at config load time - - Controls access to Publish, Delete, Create, Update, and other write methods +- **`SkipMethods`**: List of methods that bypass auth (e.g., health checks) **Access Control Patterns:** -- **Public relay**: Set `AllowedNpubsWrite` (only some can publish), leave `AllowedNpubsRead` empty (everyone can read) -- **Private relay**: Set both lists (restricted read and write access) -- **Open relay**: Leave both empty (everyone can read and write) -- **Read-only relay**: Set `AllowedNpubsRead`, block all writes +- **Public relay**: `Read.Enabled=false`, `Write.Enabled=true` with whitelist +- **Private relay**: Both `Enabled=true` with whitelists +- **Open relay**: Both `Enabled=false` +- **Authenticated reads, open writes**: `Read.Enabled=true`, `Write.Enabled=false` ### NostrCredentials Options diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index d5f3257..bcbb4a3 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -243,8 +243,15 @@ func TestValidateAuthFromContext(t *testing.T) { ctx := metadata.NewIncomingContext(context.Background(), md) opts := &InterceptorOptions{ + Read: OperationAuthConfig{ + Enabled: true, + AllowedNpubs: nil, + }, + Write: OperationAuthConfig{ + Enabled: true, + AllowedNpubs: nil, + }, TimestampWindow: 60, - Required: true, } pubkey, err := validateAuthFromContext(ctx, "/test.Service/Method", opts) diff --git a/internal/auth/interceptor.go b/internal/auth/interceptor.go index 149cc01..d394102 100644 --- a/internal/auth/interceptor.go +++ b/internal/auth/interceptor.go @@ -21,49 +21,55 @@ const ( // InterceptorOptions configures the authentication interceptor behavior. type InterceptorOptions struct { + // Read configures authentication for read operations (Subscribe, QueryEvents, etc.) + Read OperationAuthConfig + + // Write configures authentication for write operations (PublishEvent, PublishBatch) + Write OperationAuthConfig + // TimestampWindow is the maximum age of auth events in seconds. // Events older than this are rejected to prevent replay attacks. // Default: 60 seconds TimestampWindow int64 - // Required determines whether authentication is mandatory. - // If true, requests without valid auth are rejected. - // If false, unauthenticated requests are allowed (pubkey will be empty). - // Default: false - Required bool - // ValidatePayload checks the payload hash tag if present. // Default: false ValidatePayload bool - // AllowedNpubsRead is an optional whitelist of allowed pubkeys for read operations (hex format). - // Config accepts npub format only, normalized to hex at load time. - // If nil or empty, all valid signatures are accepted for reads. - // Default: nil (allow all) - AllowedNpubsRead []string - - // AllowedNpubsWrite is an optional whitelist of allowed pubkeys for write operations (hex format). - // Config accepts npub format only, normalized to hex at load time. - // If nil or empty, all valid signatures are accepted for writes. - // Default: nil (allow all) - AllowedNpubsWrite []string - // SkipMethods is a list of gRPC methods that bypass authentication. // Useful for public endpoints like health checks or relay info. - // Example: []string{"/nostr.v1.NostrRelay/QueryEvents"} + // Example: []string{"/grpc.health.v1.Health/Check"} // Default: nil (authenticate all methods) SkipMethods []string } +// OperationAuthConfig configures auth for a specific operation type. +type OperationAuthConfig struct { + // Enabled determines if auth is required. + // false = no auth, allow all + // true = auth required + Enabled bool + + // AllowedNpubs is an optional whitelist (hex format, normalized from npub at config load). + // If Enabled=true && AllowedNpubs=[]: any valid signature accepted + // If Enabled=true && AllowedNpubs=[...]: only whitelisted npubs accepted + AllowedNpubs []string +} + // DefaultInterceptorOptions returns the default configuration. func DefaultInterceptorOptions() *InterceptorOptions { return &InterceptorOptions{ - TimestampWindow: 60, - Required: false, - ValidatePayload: false, - AllowedNpubsRead: nil, - AllowedNpubsWrite: nil, - SkipMethods: nil, + Read: OperationAuthConfig{ + Enabled: false, + AllowedNpubs: nil, + }, + Write: OperationAuthConfig{ + Enabled: false, + AllowedNpubs: nil, + }, + TimestampWindow: 60, + ValidatePayload: false, + SkipMethods: nil, } } @@ -79,14 +85,23 @@ func NostrUnaryInterceptor(opts *InterceptorOptions) grpc.UnaryServerInterceptor return handler(ctx, req) } + // Check if auth is required for this operation type + var authRequired bool + if isWriteMethod(info.FullMethod) { + authRequired = opts.Write.Enabled + } else { + authRequired = opts.Read.Enabled + } + + // If auth not required, skip validation + if !authRequired { + return handler(ctx, req) + } + // Extract and validate auth pubkey, err := validateAuthFromContext(ctx, info.FullMethod, opts) if err != nil { - if opts.Required { - return nil, status.Error(codes.Unauthenticated, err.Error()) - } - // Auth not required, continue without pubkey - return handler(ctx, req) + return nil, status.Error(codes.Unauthenticated, err.Error()) } // Add pubkey to context for handlers @@ -108,15 +123,24 @@ func NostrStreamInterceptor(opts *InterceptorOptions) grpc.StreamServerIntercept return handler(srv, ss) } + // Check if auth is required for this operation type + var authRequired bool + if isWriteMethod(info.FullMethod) { + authRequired = opts.Write.Enabled + } else { + authRequired = opts.Read.Enabled + } + + // If auth not required, skip validation + if !authRequired { + return handler(srv, ss) + } + // Extract and validate auth ctx := ss.Context() pubkey, err := validateAuthFromContext(ctx, info.FullMethod, opts) if err != nil { - if opts.Required { - return status.Error(codes.Unauthenticated, err.Error()) - } - // Auth not required, continue without pubkey - return handler(srv, ss) + return status.Error(codes.Unauthenticated, err.Error()) } // Wrap stream with authenticated context @@ -177,23 +201,25 @@ func validateAuthFromContext(ctx context.Context, method string, opts *Intercept // Extract pubkey pubkey := ExtractPubkey(event) - // Check whitelist based on operation type (all values are already normalized to hex) + // Get the operation config based on method type + var opConfig OperationAuthConfig if isWriteMethod(method) { - // Write operation - check write allowlist - if len(opts.AllowedNpubsWrite) > 0 { - if !contains(opts.AllowedNpubsWrite, pubkey) { - return "", fmt.Errorf("pubkey not authorized for write operations") - } - } + opConfig = opts.Write } else { - // Read operation - check read allowlist - if len(opts.AllowedNpubsRead) > 0 { - if !contains(opts.AllowedNpubsRead, pubkey) { - return "", fmt.Errorf("pubkey not authorized for read operations") + opConfig = opts.Read + } + + // Check whitelist if configured + if len(opConfig.AllowedNpubs) > 0 { + if !contains(opConfig.AllowedNpubs, pubkey) { + if isWriteMethod(method) { + return "", fmt.Errorf("pubkey not authorized for write operations") } + return "", fmt.Errorf("pubkey not authorized for read operations") } } + // No whitelist or pubkey in whitelist - allow return pubkey, nil } diff --git a/internal/config/README.md b/internal/config/README.md index 7deb38f..3dcf215 100644 --- a/internal/config/README.md +++ b/internal/config/README.md @@ -80,41 +80,51 @@ database: # Authentication configuration auth: - # Enable authentication - enabled: false - - # Require authentication for all requests - # If false, authentication is optional (pubkey available if provided) - required: false + # Read authentication (Subscribe, QueryEvents, CountEvents) + read: + enabled: false # false = no auth, allow all + allowed_npubs: [] # npub format only (e.g., npub1...) + # If enabled=false: no auth, allow all reads + # If enabled=true && allowed_npubs=[]: auth required, any valid signature accepted + # If enabled=true && allowed_npubs=[...]: auth required, only whitelisted npubs + + # Write authentication (PublishEvent, PublishBatch) + write: + enabled: false + allowed_npubs: [] # Timestamp window in seconds for replay protection timestamp_window: 60 - # Allowed npubs for read operations (optional, whitelist) - # If empty, all valid signatures are accepted for reads - # Use npub format only (e.g., npub1...) - allowed_npubs_read: [] - - # Allowed npubs for write operations (optional, whitelist) - # If empty, all valid signatures are accepted for writes - # Use npub format only (e.g., npub1...) - allowed_npubs_write: [] - - # Example use cases: - # - Public relay: allowed_npubs_write (only some can publish), empty read (everyone can read) - # - Private relay: both lists populated (restricted read and write) - # - Open relay: both lists empty (everyone can read and write) - # - # Example: - # allowed_npubs_read: - # - npub1a2b3c4d5e6f... - # allowed_npubs_write: - # - npub1a2b3c4d5e6f... - # Skip authentication for these methods skip_methods: - "/grpc.health.v1.Health/Check" +# Common patterns: +# Public relay (anyone can read, only whitelisted can write): +# read: +# enabled: false +# write: +# enabled: true +# allowed_npubs: +# - npub1... +# +# Private relay (whitelisted read and write): +# read: +# enabled: true +# allowed_npubs: +# - npub1... +# write: +# enabled: true +# allowed_npubs: +# - npub1... +# +# Open relay (everyone can read and write): +# read: +# enabled: false +# write: +# enabled: false + # Rate limiting configuration rate_limit: # Enable rate limiting @@ -235,8 +245,10 @@ Complex types: ```bash # Lists (comma-separated, npub format) -export MUXSTR_AUTH_ALLOWED_NPUBS_READ="npub1...,npub1..." -export MUXSTR_AUTH_ALLOWED_NPUBS_WRITE="npub1..." +export MUXSTR_AUTH_READ_ENABLED=true +export MUXSTR_AUTH_READ_ALLOWED_NPUBS="npub1...,npub1..." +export MUXSTR_AUTH_WRITE_ENABLED=true +export MUXSTR_AUTH_WRITE_ALLOWED_NPUBS="npub1..." # Durations export MUXSTR_SERVER_READ_TIMEOUT="30s" diff --git a/internal/config/config.go b/internal/config/config.go index 3e52272..294510d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -39,12 +39,19 @@ type DatabaseConfig struct { // AuthConfig holds authentication configuration. type AuthConfig struct { - Enabled bool `yaml:"enabled"` - Required bool `yaml:"required"` - TimestampWindow int64 `yaml:"timestamp_window"` - AllowedNpubsRead []string `yaml:"allowed_npubs_read"` // npub format only (bech32) - normalized to hex internally - AllowedNpubsWrite []string `yaml:"allowed_npubs_write"` // npub format only (bech32) - normalized to hex internally - SkipMethods []string `yaml:"skip_methods"` + Read AuthOperationConfig `yaml:"read"` + Write AuthOperationConfig `yaml:"write"` + TimestampWindow int64 `yaml:"timestamp_window"` + SkipMethods []string `yaml:"skip_methods"` +} + +// AuthOperationConfig configures auth for a specific operation type (read or write). +type AuthOperationConfig struct { + Enabled bool `yaml:"enabled"` // false = no auth required, true = auth required + AllowedNpubs []string `yaml:"allowed_npubs"` // npub format only - normalized to hex internally + // If enabled=false: no auth, allow all + // If enabled=true && allowed_npubs=[]: auth required, any valid signature accepted + // If enabled=true && allowed_npubs=[...]: auth required, only whitelisted npubs } // RateLimitConfig holds rate limiting configuration. @@ -111,8 +118,14 @@ func Default() *Config { Path: "relay.db", }, Auth: AuthConfig{ - Enabled: false, - Required: false, + Read: AuthOperationConfig{ + Enabled: false, + AllowedNpubs: nil, + }, + Write: AuthOperationConfig{ + Enabled: false, + AllowedNpubs: nil, + }, TimestampWindow: 60, }, RateLimit: RateLimitConfig{ @@ -184,15 +197,15 @@ func normalizeNpubs(cfg *Config) error { var err error // Normalize read allowlist - cfg.Auth.AllowedNpubsRead, err = normalizeNpubList(cfg.Auth.AllowedNpubsRead) + cfg.Auth.Read.AllowedNpubs, err = normalizeNpubList(cfg.Auth.Read.AllowedNpubs) if err != nil { - return fmt.Errorf("allowed_npubs_read: %w", err) + return fmt.Errorf("auth.read.allowed_npubs: %w", err) } // Normalize write allowlist - cfg.Auth.AllowedNpubsWrite, err = normalizeNpubList(cfg.Auth.AllowedNpubsWrite) + cfg.Auth.Write.AllowedNpubs, err = normalizeNpubList(cfg.Auth.Write.AllowedNpubs) if err != nil { - return fmt.Errorf("allowed_npubs_write: %w", err) + return fmt.Errorf("auth.write.allowed_npubs: %w", err) } return nil @@ -299,11 +312,17 @@ func applyEnvOverrides(cfg *Config) { } // Auth - if val := os.Getenv("MUXSTR_AUTH_ENABLED"); val != "" { - cfg.Auth.Enabled = parseBool(val) + if val := os.Getenv("MUXSTR_AUTH_READ_ENABLED"); val != "" { + cfg.Auth.Read.Enabled = parseBool(val) + } + if val := os.Getenv("MUXSTR_AUTH_READ_ALLOWED_NPUBS"); val != "" { + cfg.Auth.Read.AllowedNpubs = strings.Split(val, ",") } - if val := os.Getenv("MUXSTR_AUTH_REQUIRED"); val != "" { - cfg.Auth.Required = parseBool(val) + if val := os.Getenv("MUXSTR_AUTH_WRITE_ENABLED"); val != "" { + cfg.Auth.Write.Enabled = parseBool(val) + } + if val := os.Getenv("MUXSTR_AUTH_WRITE_ALLOWED_NPUBS"); val != "" { + cfg.Auth.Write.AllowedNpubs = strings.Split(val, ",") } if val := os.Getenv("MUXSTR_AUTH_TIMESTAMP_WINDOW"); val != "" { var n int64 @@ -311,12 +330,6 @@ func applyEnvOverrides(cfg *Config) { cfg.Auth.TimestampWindow = n } } - if val := os.Getenv("MUXSTR_AUTH_ALLOWED_NPUBS_READ"); val != "" { - cfg.Auth.AllowedNpubsRead = strings.Split(val, ",") - } - if val := os.Getenv("MUXSTR_AUTH_ALLOWED_NPUBS_WRITE"); val != "" { - cfg.Auth.AllowedNpubsWrite = strings.Split(val, ",") - } // Rate limit if val := os.Getenv("MUXSTR_RATE_LIMIT_ENABLED"); val != "" { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index c0d4555..65a742a 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -40,8 +40,10 @@ database: path: "test.db" auth: - enabled: true - required: true + read: + enabled: true + write: + enabled: true timestamp_window: 120 rate_limit: @@ -75,12 +77,12 @@ metrics: t.Errorf("expected db path test.db, got %s", cfg.Database.Path) } - if !cfg.Auth.Enabled { - t.Error("expected auth enabled") + if !cfg.Auth.Read.Enabled { + t.Error("expected auth read enabled") } - if !cfg.Auth.Required { - t.Error("expected auth required") + if !cfg.Auth.Write.Enabled { + t.Error("expected auth write enabled") } if cfg.Auth.TimestampWindow != 120 { @@ -99,11 +101,13 @@ metrics: func TestEnvOverrides(t *testing.T) { // Set environment variables os.Setenv("MUXSTR_SERVER_GRPC_ADDR", ":7777") - os.Setenv("MUXSTR_AUTH_ENABLED", "true") + os.Setenv("MUXSTR_AUTH_READ_ENABLED", "true") + os.Setenv("MUXSTR_AUTH_WRITE_ENABLED", "true") os.Setenv("MUXSTR_RATE_LIMIT_DEFAULT_RPS", "200") defer func() { os.Unsetenv("MUXSTR_SERVER_GRPC_ADDR") - os.Unsetenv("MUXSTR_AUTH_ENABLED") + os.Unsetenv("MUXSTR_AUTH_READ_ENABLED") + os.Unsetenv("MUXSTR_AUTH_WRITE_ENABLED") os.Unsetenv("MUXSTR_RATE_LIMIT_DEFAULT_RPS") }() @@ -118,8 +122,12 @@ func TestEnvOverrides(t *testing.T) { t.Errorf("expected env override :7777, got %s", cfg.Server.GrpcAddr) } - if !cfg.Auth.Enabled { - t.Error("expected auth enabled from env") + if !cfg.Auth.Read.Enabled { + t.Error("expected auth read enabled from env") + } + + if !cfg.Auth.Write.Enabled { + t.Error("expected auth write enabled from env") } if cfg.RateLimit.DefaultRPS != 200 { @@ -206,7 +214,8 @@ func TestSaveAndLoad(t *testing.T) { // Create config cfg := Default() cfg.Server.GrpcAddr = ":9999" - cfg.Auth.Enabled = true + cfg.Auth.Read.Enabled = true + cfg.Auth.Write.Enabled = true cfg.RateLimit.DefaultRPS = 100 // Save to temp file @@ -232,8 +241,12 @@ func TestSaveAndLoad(t *testing.T) { t.Errorf("expected grpc_addr :9999, got %s", loaded.Server.GrpcAddr) } - if !loaded.Auth.Enabled { - t.Error("expected auth enabled") + if !loaded.Auth.Read.Enabled { + t.Error("expected auth read enabled") + } + + if !loaded.Auth.Write.Enabled { + t.Error("expected auth write enabled") } if loaded.RateLimit.DefaultRPS != 100 { @@ -259,12 +272,15 @@ database: path: "test.db" auth: - enabled: true - allowed_npubs_read: - - npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6 - - npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft - allowed_npubs_write: - - npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6 + read: + enabled: true + allowed_npubs: + - npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6 + - npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft + write: + enabled: true + allowed_npubs: + - npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6 ` if _, err := tmpfile.Write([]byte(configData)); err != nil { @@ -278,17 +294,17 @@ auth: } // Verify read npubs were normalized to hex - if len(cfg.Auth.AllowedNpubsRead) != 2 { - t.Errorf("expected 2 allowed npubs for read, got %d", len(cfg.Auth.AllowedNpubsRead)) + if len(cfg.Auth.Read.AllowedNpubs) != 2 { + t.Errorf("expected 2 allowed npubs for read, got %d", len(cfg.Auth.Read.AllowedNpubs)) } // Verify write npubs were normalized to hex - if len(cfg.Auth.AllowedNpubsWrite) != 1 { - t.Errorf("expected 1 allowed npub for write, got %d", len(cfg.Auth.AllowedNpubsWrite)) + if len(cfg.Auth.Write.AllowedNpubs) != 1 { + t.Errorf("expected 1 allowed npub for write, got %d", len(cfg.Auth.Write.AllowedNpubs)) } // Check that they're hex format (64 chars, not npub1...) - for i, pubkey := range cfg.Auth.AllowedNpubsRead { + for i, pubkey := range cfg.Auth.Read.AllowedNpubs { if len(pubkey) != 64 { t.Errorf("read npub %d: expected 64 hex chars, got %d", i, len(pubkey)) } @@ -297,7 +313,7 @@ auth: } } - for i, pubkey := range cfg.Auth.AllowedNpubsWrite { + for i, pubkey := range cfg.Auth.Write.AllowedNpubs { if len(pubkey) != 64 { t.Errorf("write npub %d: expected 64 hex chars, got %d", i, len(pubkey)) } @@ -310,14 +326,14 @@ auth: expectedHex1 := "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d" expectedHex2 := "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52" - if cfg.Auth.AllowedNpubsRead[0] != expectedHex1 { - t.Errorf("read npub 0: expected %s, got %s", expectedHex1, cfg.Auth.AllowedNpubsRead[0]) + if cfg.Auth.Read.AllowedNpubs[0] != expectedHex1 { + t.Errorf("read npub 0: expected %s, got %s", expectedHex1, cfg.Auth.Read.AllowedNpubs[0]) } - if cfg.Auth.AllowedNpubsRead[1] != expectedHex2 { - t.Errorf("read npub 1: expected %s, got %s", expectedHex2, cfg.Auth.AllowedNpubsRead[1]) + if cfg.Auth.Read.AllowedNpubs[1] != expectedHex2 { + t.Errorf("read npub 1: expected %s, got %s", expectedHex2, cfg.Auth.Read.AllowedNpubs[1]) } - if cfg.Auth.AllowedNpubsWrite[0] != expectedHex1 { - t.Errorf("write npub 0: expected %s, got %s", expectedHex1, cfg.Auth.AllowedNpubsWrite[0]) + if cfg.Auth.Write.AllowedNpubs[0] != expectedHex1 { + t.Errorf("write npub 0: expected %s, got %s", expectedHex1, cfg.Auth.Write.AllowedNpubs[0]) } } @@ -337,8 +353,10 @@ server: database: path: "test.db" auth: - allowed_npubs_read: - - 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d + read: + enabled: true + allowed_npubs: + - 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d `, expectError: true, errorMsg: "must start with 'npub1'", @@ -352,8 +370,10 @@ server: database: path: "test.db" auth: - allowed_npubs_write: - - 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d + write: + enabled: true + allowed_npubs: + - 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d `, expectError: true, errorMsg: "must start with 'npub1'", @@ -367,10 +387,14 @@ server: database: path: "test.db" auth: - allowed_npubs_read: - - npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6 - allowed_npubs_write: - - npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft + read: + enabled: true + allowed_npubs: + - npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6 + write: + enabled: true + allowed_npubs: + - npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft `, expectError: false, }, -- cgit v1.2.3