main.go raw

   1  package main
   2  
   3  import (
   4  	"context"
   5  	"flag"
   6  	"fmt"
   7  	"os"
   8  	"strings"
   9  	"time"
  10  
  11  	"next.orly.dev/pkg/lol/chk"
  12  	"next.orly.dev/pkg/lol/log"
  13  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  14  	"next.orly.dev/pkg/nostr/encoders/event"
  15  	"next.orly.dev/pkg/nostr/encoders/filter"
  16  	"next.orly.dev/pkg/nostr/encoders/hex"
  17  	"next.orly.dev/pkg/nostr/encoders/kind"
  18  	"next.orly.dev/pkg/nostr/encoders/tag"
  19  	"next.orly.dev/pkg/nostr/ws"
  20  )
  21  
  22  func main() {
  23  	var err error
  24  	url := flag.String("url", "ws://127.0.0.1:34568", "relay websocket URL")
  25  	allowedPubkeyHex := flag.String("allowed-pubkey", "", "hex-encoded allowed pubkey")
  26  	allowedSecHex := flag.String("allowed-sec", "", "hex-encoded allowed secret key")
  27  	unauthorizedPubkeyHex := flag.String("unauthorized-pubkey", "", "hex-encoded unauthorized pubkey")
  28  	unauthorizedSecHex := flag.String("unauthorized-sec", "", "hex-encoded unauthorized secret key")
  29  	timeout := flag.Duration("timeout", 10*time.Second, "operation timeout")
  30  	flag.Parse()
  31  
  32  	if *allowedPubkeyHex == "" || *allowedSecHex == "" {
  33  		log.E.F("required flags: -allowed-pubkey and -allowed-sec")
  34  		os.Exit(1)
  35  	}
  36  	if *unauthorizedPubkeyHex == "" || *unauthorizedSecHex == "" {
  37  		log.E.F("required flags: -unauthorized-pubkey and -unauthorized-sec")
  38  		os.Exit(1)
  39  	}
  40  
  41  	// Decode keys
  42  	allowedSecBytes, err := hex.Dec(*allowedSecHex)
  43  	if err != nil {
  44  		log.E.F("failed to decode allowed secret key: %v", err)
  45  		os.Exit(1)
  46  	}
  47  	var allowedSigner *p8k.Signer
  48  	if allowedSigner, err = p8k.New(); chk.E(err) {
  49  		log.E.F("failed to create allowed signer: %v", err)
  50  		os.Exit(1)
  51  	}
  52  	if err = allowedSigner.InitSec(allowedSecBytes); chk.E(err) {
  53  		log.E.F("failed to initialize allowed signer: %v", err)
  54  		os.Exit(1)
  55  	}
  56  
  57  	unauthorizedSecBytes, err := hex.Dec(*unauthorizedSecHex)
  58  	if err != nil {
  59  		log.E.F("failed to decode unauthorized secret key: %v", err)
  60  		os.Exit(1)
  61  	}
  62  	var unauthorizedSigner *p8k.Signer
  63  	if unauthorizedSigner, err = p8k.New(); chk.E(err) {
  64  		log.E.F("failed to create unauthorized signer: %v", err)
  65  		os.Exit(1)
  66  	}
  67  	if err = unauthorizedSigner.InitSec(unauthorizedSecBytes); chk.E(err) {
  68  		log.E.F("failed to initialize unauthorized signer: %v", err)
  69  		os.Exit(1)
  70  	}
  71  
  72  	ctx, cancel := context.WithTimeout(context.Background(), *timeout)
  73  	defer cancel()
  74  
  75  	// Test 1: Authenticated as allowed pubkey - should work
  76  	fmt.Println("Test 1: Publishing event 30520 with allowed pubkey (authenticated)...")
  77  	if err := testWriteEvent(ctx, *url, 30520, allowedSigner, allowedSigner); err != nil {
  78  		fmt.Printf("❌ FAILED: %v\n", err)
  79  		os.Exit(1)
  80  	}
  81  	fmt.Println("✅ PASSED: Event published successfully")
  82  
  83  	// Test 2: Authenticated as allowed pubkey, then read event 10306 - should work
  84  	// First publish an event, then read it
  85  	fmt.Println("\nTest 2: Publishing and reading event 10306 with allowed pubkey (authenticated)...")
  86  	if err := testWriteEvent(ctx, *url, 10306, allowedSigner, allowedSigner); err != nil {
  87  		fmt.Printf("❌ FAILED to publish: %v\n", err)
  88  		os.Exit(1)
  89  	}
  90  	if err := testReadEvent(ctx, *url, 10306, allowedSigner); err != nil {
  91  		fmt.Printf("❌ FAILED to read: %v\n", err)
  92  		os.Exit(1)
  93  	}
  94  	fmt.Println("✅ PASSED: Event readable by allowed user")
  95  
  96  	// Test 3: Unauthenticated request - should be blocked
  97  	fmt.Println("\nTest 3: Publishing event 30520 without authentication...")
  98  	if err := testWriteEventUnauthenticated(ctx, *url, 30520, allowedSigner); err != nil {
  99  		fmt.Printf("✅ PASSED: Event correctly blocked (expected): %v\n", err)
 100  	} else {
 101  		fmt.Println("❌ FAILED: Event was allowed when it should have been blocked")
 102  		os.Exit(1)
 103  	}
 104  
 105  	// Test 4: Authenticated as unauthorized pubkey - should be blocked
 106  	fmt.Println("\nTest 4: Publishing event 30520 with unauthorized pubkey...")
 107  	if err := testWriteEvent(ctx, *url, 30520, unauthorizedSigner, unauthorizedSigner); err != nil {
 108  		fmt.Printf("✅ PASSED: Event correctly blocked (expected): %v\n", err)
 109  	} else {
 110  		fmt.Println("❌ FAILED: Event was allowed when it should have been blocked")
 111  		os.Exit(1)
 112  	}
 113  
 114  	// Test 5: Read event 10306 without authentication - should be blocked
 115  	// Event was published in test 2, so it exists in the database
 116  	fmt.Println("\nTest 5: Reading event 10306 without authentication (should be blocked)...")
 117  	// Wait a bit to ensure event is stored
 118  	time.Sleep(500 * time.Millisecond)
 119  	// If no error is returned, that means no events were received (which is correct)
 120  	// If an error is returned, it means an event was received (which is wrong)
 121  	if err := testReadEventUnauthenticated(ctx, *url, 10306); err != nil {
 122  		// If we got an error about receiving an event, that's a failure
 123  		if strings.Contains(err.Error(), "unexpected event received") {
 124  			fmt.Printf("❌ FAILED: %v\n", err)
 125  			os.Exit(1)
 126  		}
 127  		// Other errors (like connection errors) are also failures
 128  		fmt.Printf("❌ FAILED: Unexpected error: %v\n", err)
 129  		os.Exit(1)
 130  	}
 131  	fmt.Println("✅ PASSED: No events received (correctly filtered by policy)")
 132  
 133  	// Test 6: Read event 10306 with unauthorized pubkey - should be blocked
 134  	fmt.Println("\nTest 6: Reading event 10306 with unauthorized pubkey (should be blocked)...")
 135  	// If no error is returned, that means no events were received (which is correct)
 136  	// If an error is returned about receiving an event, that's a failure
 137  	if err := testReadEvent(ctx, *url, 10306, unauthorizedSigner); err != nil {
 138  		// Connection/subscription errors are failures
 139  		fmt.Printf("❌ FAILED: Unexpected error: %v\n", err)
 140  		os.Exit(1)
 141  	}
 142  	fmt.Println("✅ PASSED: No events received (correctly filtered by policy)")
 143  
 144  	fmt.Println("\n✅ All tests passed!")
 145  }
 146  
 147  func testWriteEvent(ctx context.Context, url string, kindNum uint16, eventSigner, authSigner *p8k.Signer) error {
 148  	rl, err := ws.RelayConnect(ctx, url)
 149  	if err != nil {
 150  		return fmt.Errorf("connect error: %w", err)
 151  	}
 152  	defer rl.Close()
 153  
 154  	// Send a REQ first to trigger AUTH challenge (when AuthToWrite is enabled)
 155  	// This is needed because challenges are sent on REQ, not on connect
 156  	limit := uint(1)
 157  	ff := filter.NewS(&filter.F{
 158  		Kinds: kind.NewS(kind.New(kindNum)),
 159  		Limit: &limit,
 160  	})
 161  	sub, err := rl.Subscribe(ctx, ff)
 162  	if err != nil {
 163  		return fmt.Errorf("subscription error (may be expected): %w", err)
 164  	}
 165  	// Wait a bit for challenge to arrive
 166  	time.Sleep(500 * time.Millisecond)
 167  	sub.Unsub()
 168  
 169  	// Authenticate
 170  	if err = rl.Auth(ctx, authSigner); err != nil {
 171  		return fmt.Errorf("auth error: %w", err)
 172  	}
 173  
 174  	// Create and sign event
 175  	ev := &event.E{
 176  		CreatedAt: time.Now().Unix(),
 177  		Kind:      kind.K{K: kindNum}.K,
 178  		Tags:      tag.NewS(),
 179  		Content:   []byte(fmt.Sprintf("test event kind %d", kindNum)),
 180  	}
 181  	// Add p tag for privileged check
 182  	pTag := tag.NewFromAny("p", hex.Enc(authSigner.Pub()))
 183  	ev.Tags.Append(pTag)
 184  	
 185  	// Add d tag for addressable events (kinds 30000-39999)
 186  	if kindNum >= 30000 && kindNum < 40000 {
 187  		dTag := tag.NewFromAny("d", "test")
 188  		ev.Tags.Append(dTag)
 189  	}
 190  
 191  	if err = ev.Sign(eventSigner); err != nil {
 192  		return fmt.Errorf("sign error: %w", err)
 193  	}
 194  
 195  	// Publish
 196  	if err = rl.Publish(ctx, ev); err != nil {
 197  		return fmt.Errorf("publish error: %w", err)
 198  	}
 199  
 200  	return nil
 201  }
 202  
 203  func testWriteEventUnauthenticated(ctx context.Context, url string, kindNum uint16, eventSigner *p8k.Signer) error {
 204  	rl, err := ws.RelayConnect(ctx, url)
 205  	if err != nil {
 206  		return fmt.Errorf("connect error: %w", err)
 207  	}
 208  	defer rl.Close()
 209  
 210  	// Do NOT authenticate
 211  
 212  	// Create and sign event
 213  	ev := &event.E{
 214  		CreatedAt: time.Now().Unix(),
 215  		Kind:      kind.K{K: kindNum}.K,
 216  		Tags:      tag.NewS(),
 217  		Content:   []byte(fmt.Sprintf("test event kind %d (unauthenticated)", kindNum)),
 218  	}
 219  	
 220  	// Add d tag for addressable events (kinds 30000-39999)
 221  	if kindNum >= 30000 && kindNum < 40000 {
 222  		dTag := tag.NewFromAny("d", "test")
 223  		ev.Tags.Append(dTag)
 224  	}
 225  
 226  	if err = ev.Sign(eventSigner); err != nil {
 227  		return fmt.Errorf("sign error: %w", err)
 228  	}
 229  
 230  	// Publish (should fail)
 231  	if err = rl.Publish(ctx, ev); err != nil {
 232  		return fmt.Errorf("publish error (expected): %w", err)
 233  	}
 234  
 235  	return nil
 236  }
 237  
 238  func testReadEvent(ctx context.Context, url string, kindNum uint16, authSigner *p8k.Signer) error {
 239  	rl, err := ws.RelayConnect(ctx, url)
 240  	if err != nil {
 241  		return fmt.Errorf("connect error: %w", err)
 242  	}
 243  	defer rl.Close()
 244  
 245  	// Send a REQ first to trigger AUTH challenge (when AuthToWrite is enabled)
 246  	// Then authenticate
 247  	ff := filter.NewS(&filter.F{
 248  		Kinds: kind.NewS(kind.New(kindNum)),
 249  	})
 250  	sub, err := rl.Subscribe(ctx, ff)
 251  	if err != nil {
 252  		return fmt.Errorf("subscription error: %w", err)
 253  	}
 254  	// Wait a bit for challenge to arrive
 255  	time.Sleep(500 * time.Millisecond)
 256  
 257  	// Authenticate
 258  	if err = rl.Auth(ctx, authSigner); err != nil {
 259  		sub.Unsub()
 260  		return fmt.Errorf("auth error: %w", err)
 261  	}
 262  
 263  	// Wait for events or timeout
 264  	// If we receive any events, return nil (success)
 265  	// If we don't receive events, also return nil (no events found, which may be expected)
 266  	select {
 267  	case ev := <-sub.Events:
 268  		if ev != nil {
 269  			sub.Unsub()
 270  			return nil // Event received
 271  		}
 272  	case <-sub.EndOfStoredEvents:
 273  		// EOSE received, no more events
 274  		sub.Unsub()
 275  		return nil
 276  	case <-time.After(5 * time.Second):
 277  		// No events received - this might be OK if no events exist or they're filtered
 278  		sub.Unsub()
 279  		return nil
 280  	case <-ctx.Done():
 281  		sub.Unsub()
 282  		return ctx.Err()
 283  	}
 284  
 285  	return nil
 286  }
 287  
 288  func testReadEventUnauthenticated(ctx context.Context, url string, kindNum uint16) error {
 289  	rl, err := ws.RelayConnect(ctx, url)
 290  	if err != nil {
 291  		return fmt.Errorf("connect error: %w", err)
 292  	}
 293  	defer rl.Close()
 294  
 295  	// Do NOT authenticate
 296  
 297  	// Subscribe to events
 298  	ff := filter.NewS(&filter.F{
 299  		Kinds: kind.NewS(kind.New(kindNum)),
 300  	})
 301  
 302  	sub, err := rl.Subscribe(ctx, ff)
 303  	if err != nil {
 304  		return fmt.Errorf("subscription error (may be expected): %w", err)
 305  	}
 306  	defer sub.Unsub()
 307  
 308  	// Wait for events or timeout
 309  	// If we receive any events, that's a failure (should be blocked)
 310  	select {
 311  	case ev := <-sub.Events:
 312  		if ev != nil {
 313  			return fmt.Errorf("unexpected event received: should have been blocked by policy (event ID: %s)", hex.Enc(ev.ID))
 314  		}
 315  	case <-sub.EndOfStoredEvents:
 316  		// EOSE received, no events (this is expected for unauthenticated privileged events)
 317  		return nil
 318  	case <-time.After(5 * time.Second):
 319  		// No events received - this is expected for unauthenticated requests
 320  		return nil
 321  	case <-ctx.Done():
 322  		return ctx.Err()
 323  	}
 324  
 325  	return nil
 326  }
 327  
 328