service.go raw

   1  package server
   2  
   3  import (
   4  	"context"
   5  	"encoding/hex"
   6  	"errors"
   7  	"time"
   8  
   9  	"google.golang.org/grpc/codes"
  10  	"google.golang.org/grpc/status"
  11  	"next.orly.dev/pkg/lol/log"
  12  
  13  	"next.orly.dev/pkg/acl"
  14  	"next.orly.dev/pkg/database"
  15  	orlyaclv1 "next.orly.dev/pkg/proto/orlyacl/v1"
  16  	orlydbv1 "next.orly.dev/pkg/proto/orlydb/v1"
  17  )
  18  
  19  // ACLService implements the orlyaclv1.ACLServiceServer interface.
  20  type ACLService struct {
  21  	orlyaclv1.UnimplementedACLServiceServer
  22  	cfg *Config
  23  	db  database.Database
  24  }
  25  
  26  // NewACLService creates a new ACL service.
  27  func NewACLService(cfg *Config, db database.Database) *ACLService {
  28  	return &ACLService{
  29  		cfg: cfg,
  30  		db:  db,
  31  	}
  32  }
  33  
  34  // === Core ACL Methods ===
  35  
  36  func (s *ACLService) GetAccessLevel(ctx context.Context, req *orlyaclv1.AccessLevelRequest) (*orlyaclv1.AccessLevelResponse, error) {
  37  	level := acl.Registry.GetAccessLevel(req.Pubkey, req.Address)
  38  	return &orlyaclv1.AccessLevelResponse{Level: level}, nil
  39  }
  40  
  41  func (s *ACLService) CheckPolicy(ctx context.Context, req *orlyaclv1.PolicyCheckRequest) (*orlyaclv1.PolicyCheckResponse, error) {
  42  	ev := orlydbv1.ProtoToEvent(req.Event)
  43  	allowed, err := acl.Registry.CheckPolicy(ev)
  44  	resp := &orlyaclv1.PolicyCheckResponse{Allowed: allowed}
  45  	if err != nil {
  46  		resp.Error = err.Error()
  47  	}
  48  	return resp, nil
  49  }
  50  
  51  func (s *ACLService) GetACLInfo(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ACLInfoResponse, error) {
  52  	name, description, documentation := acl.Registry.GetACLInfo()
  53  	return &orlyaclv1.ACLInfoResponse{
  54  		Name:          name,
  55  		Description:   description,
  56  		Documentation: documentation,
  57  	}, nil
  58  }
  59  
  60  func (s *ACLService) GetMode(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ModeResponse, error) {
  61  	return &orlyaclv1.ModeResponse{Mode: acl.Registry.Type()}, nil
  62  }
  63  
  64  func (s *ACLService) Ready(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ReadyResponse, error) {
  65  	// Check if database is ready
  66  	select {
  67  	case <-s.db.Ready():
  68  		return &orlyaclv1.ReadyResponse{Ready: true}, nil
  69  	default:
  70  		return &orlyaclv1.ReadyResponse{Ready: false}, nil
  71  	}
  72  }
  73  
  74  // === Follows ACL Methods ===
  75  
  76  func (s *ACLService) GetThrottleDelay(ctx context.Context, req *orlyaclv1.ThrottleDelayRequest) (*orlyaclv1.ThrottleDelayResponse, error) {
  77  	// Get the active ACL and check if it's Follows
  78  	for _, i := range acl.Registry.ACLs() {
  79  		if i.Type() == "follows" {
  80  			if follows, ok := i.(*acl.Follows); ok {
  81  				delay := follows.GetThrottleDelay(req.Pubkey, req.Ip)
  82  				return &orlyaclv1.ThrottleDelayResponse{DelayMs: delay.Milliseconds()}, nil
  83  			}
  84  		}
  85  	}
  86  	return &orlyaclv1.ThrottleDelayResponse{DelayMs: 0}, nil
  87  }
  88  
  89  func (s *ACLService) AddFollow(ctx context.Context, req *orlyaclv1.AddFollowRequest) (*orlyaclv1.Empty, error) {
  90  	acl.Registry.AddFollow(req.Pubkey)
  91  	return &orlyaclv1.Empty{}, nil
  92  }
  93  
  94  func (s *ACLService) GetFollowedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.FollowedPubkeysResponse, error) {
  95  	for _, i := range acl.Registry.ACLs() {
  96  		if i.Type() == "follows" {
  97  			if follows, ok := i.(*acl.Follows); ok {
  98  				pubkeys := follows.GetFollowedPubkeys()
  99  				return &orlyaclv1.FollowedPubkeysResponse{Pubkeys: pubkeys}, nil
 100  			}
 101  		}
 102  	}
 103  	return &orlyaclv1.FollowedPubkeysResponse{}, nil
 104  }
 105  
 106  func (s *ACLService) GetAdminRelays(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.AdminRelaysResponse, error) {
 107  	for _, i := range acl.Registry.ACLs() {
 108  		if i.Type() == "follows" {
 109  			if follows, ok := i.(*acl.Follows); ok {
 110  				urls := follows.AdminRelays()
 111  				return &orlyaclv1.AdminRelaysResponse{Urls: urls}, nil
 112  			}
 113  		}
 114  	}
 115  	return &orlyaclv1.AdminRelaysResponse{}, nil
 116  }
 117  
 118  // === Managed ACL Methods ===
 119  
 120  func (s *ACLService) BanPubkey(ctx context.Context, req *orlyaclv1.BanPubkeyRequest) (*orlyaclv1.Empty, error) {
 121  	managed := s.getManagedACL()
 122  	if managed == nil {
 123  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 124  	}
 125  	managedACL := managed.GetManagedACL()
 126  	if managedACL == nil {
 127  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 128  	}
 129  	if err := managedACL.SaveBannedPubkey(req.Pubkey, req.Reason); err != nil {
 130  		return nil, status.Errorf(codes.Internal, "failed to ban pubkey: %v", err)
 131  	}
 132  	return &orlyaclv1.Empty{}, nil
 133  }
 134  
 135  func (s *ACLService) UnbanPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 136  	managed := s.getManagedACL()
 137  	if managed == nil {
 138  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 139  	}
 140  	managedACL := managed.GetManagedACL()
 141  	if managedACL == nil {
 142  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 143  	}
 144  	if err := managedACL.RemoveBannedPubkey(req.Pubkey); err != nil {
 145  		return nil, status.Errorf(codes.Internal, "failed to unban pubkey: %v", err)
 146  	}
 147  	return &orlyaclv1.Empty{}, nil
 148  }
 149  
 150  func (s *ACLService) ListBannedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBannedPubkeysResponse, error) {
 151  	managed := s.getManagedACL()
 152  	if managed == nil {
 153  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 154  	}
 155  	managedACL := managed.GetManagedACL()
 156  	if managedACL == nil {
 157  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 158  	}
 159  	banned, err := managedACL.ListBannedPubkeys()
 160  	if err != nil {
 161  		return nil, status.Errorf(codes.Internal, "failed to list banned pubkeys: %v", err)
 162  	}
 163  	resp := &orlyaclv1.ListBannedPubkeysResponse{}
 164  	for _, b := range banned {
 165  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.BannedPubkey{
 166  			Pubkey: b.Pubkey,
 167  			Reason: b.Reason,
 168  			Added:  b.Added.Unix(),
 169  		})
 170  	}
 171  	return resp, nil
 172  }
 173  
 174  func (s *ACLService) AllowPubkey(ctx context.Context, req *orlyaclv1.AllowPubkeyRequest) (*orlyaclv1.Empty, error) {
 175  	managed := s.getManagedACL()
 176  	if managed == nil {
 177  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 178  	}
 179  	managedACL := managed.GetManagedACL()
 180  	if managedACL == nil {
 181  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 182  	}
 183  	if err := managedACL.SaveAllowedPubkey(req.Pubkey, req.Reason); err != nil {
 184  		return nil, status.Errorf(codes.Internal, "failed to allow pubkey: %v", err)
 185  	}
 186  	return &orlyaclv1.Empty{}, nil
 187  }
 188  
 189  func (s *ACLService) DisallowPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 190  	managed := s.getManagedACL()
 191  	if managed == nil {
 192  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 193  	}
 194  	managedACL := managed.GetManagedACL()
 195  	if managedACL == nil {
 196  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 197  	}
 198  	if err := managedACL.RemoveAllowedPubkey(req.Pubkey); err != nil {
 199  		return nil, status.Errorf(codes.Internal, "failed to disallow pubkey: %v", err)
 200  	}
 201  	return &orlyaclv1.Empty{}, nil
 202  }
 203  
 204  func (s *ACLService) ListAllowedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedPubkeysResponse, error) {
 205  	managed := s.getManagedACL()
 206  	if managed == nil {
 207  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 208  	}
 209  	managedACL := managed.GetManagedACL()
 210  	if managedACL == nil {
 211  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 212  	}
 213  	allowed, err := managedACL.ListAllowedPubkeys()
 214  	if err != nil {
 215  		return nil, status.Errorf(codes.Internal, "failed to list allowed pubkeys: %v", err)
 216  	}
 217  	resp := &orlyaclv1.ListAllowedPubkeysResponse{}
 218  	for _, a := range allowed {
 219  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.AllowedPubkey{
 220  			Pubkey: a.Pubkey,
 221  			Reason: a.Reason,
 222  			Added:  a.Added.Unix(),
 223  		})
 224  	}
 225  	return resp, nil
 226  }
 227  
 228  func (s *ACLService) BanEvent(ctx context.Context, req *orlyaclv1.BanEventRequest) (*orlyaclv1.Empty, error) {
 229  	managed := s.getManagedACL()
 230  	if managed == nil {
 231  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 232  	}
 233  	managedACL := managed.GetManagedACL()
 234  	if managedACL == nil {
 235  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 236  	}
 237  	if err := managedACL.SaveBannedEvent(req.EventId, req.Reason); err != nil {
 238  		return nil, status.Errorf(codes.Internal, "failed to ban event: %v", err)
 239  	}
 240  	return &orlyaclv1.Empty{}, nil
 241  }
 242  
 243  func (s *ACLService) UnbanEvent(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) {
 244  	managed := s.getManagedACL()
 245  	if managed == nil {
 246  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 247  	}
 248  	managedACL := managed.GetManagedACL()
 249  	if managedACL == nil {
 250  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 251  	}
 252  	if err := managedACL.RemoveBannedEvent(req.EventId); err != nil {
 253  		return nil, status.Errorf(codes.Internal, "failed to unban event: %v", err)
 254  	}
 255  	return &orlyaclv1.Empty{}, nil
 256  }
 257  
 258  func (s *ACLService) ListBannedEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBannedEventsResponse, error) {
 259  	managed := s.getManagedACL()
 260  	if managed == nil {
 261  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 262  	}
 263  	managedACL := managed.GetManagedACL()
 264  	if managedACL == nil {
 265  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 266  	}
 267  	banned, err := managedACL.ListBannedEvents()
 268  	if err != nil {
 269  		return nil, status.Errorf(codes.Internal, "failed to list banned events: %v", err)
 270  	}
 271  	resp := &orlyaclv1.ListBannedEventsResponse{}
 272  	for _, b := range banned {
 273  		resp.Events = append(resp.Events, &orlyaclv1.BannedEvent{
 274  			EventId: b.ID,
 275  			Reason:  b.Reason,
 276  			Added:   b.Added.Unix(),
 277  		})
 278  	}
 279  	return resp, nil
 280  }
 281  
 282  func (s *ACLService) AllowEvent(ctx context.Context, req *orlyaclv1.BanEventRequest) (*orlyaclv1.Empty, error) {
 283  	managed := s.getManagedACL()
 284  	if managed == nil {
 285  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 286  	}
 287  	managedACL := managed.GetManagedACL()
 288  	if managedACL == nil {
 289  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 290  	}
 291  	if err := managedACL.SaveAllowedEvent(req.EventId, req.Reason); err != nil {
 292  		return nil, status.Errorf(codes.Internal, "failed to allow event: %v", err)
 293  	}
 294  	return &orlyaclv1.Empty{}, nil
 295  }
 296  
 297  func (s *ACLService) DisallowEvent(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) {
 298  	managed := s.getManagedACL()
 299  	if managed == nil {
 300  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 301  	}
 302  	managedACL := managed.GetManagedACL()
 303  	if managedACL == nil {
 304  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 305  	}
 306  	if err := managedACL.RemoveAllowedEvent(req.EventId); err != nil {
 307  		return nil, status.Errorf(codes.Internal, "failed to disallow event: %v", err)
 308  	}
 309  	return &orlyaclv1.Empty{}, nil
 310  }
 311  
 312  func (s *ACLService) ListAllowedEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedEventsResponse, error) {
 313  	managed := s.getManagedACL()
 314  	if managed == nil {
 315  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 316  	}
 317  	managedACL := managed.GetManagedACL()
 318  	if managedACL == nil {
 319  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 320  	}
 321  	allowed, err := managedACL.ListAllowedEvents()
 322  	if err != nil {
 323  		return nil, status.Errorf(codes.Internal, "failed to list allowed events: %v", err)
 324  	}
 325  	resp := &orlyaclv1.ListAllowedEventsResponse{}
 326  	for _, a := range allowed {
 327  		resp.Events = append(resp.Events, &orlyaclv1.AllowedEvent{
 328  			EventId: a.ID,
 329  			Reason:  a.Reason,
 330  			Added:   a.Added.Unix(),
 331  		})
 332  	}
 333  	return resp, nil
 334  }
 335  
 336  func (s *ACLService) BlockIP(ctx context.Context, req *orlyaclv1.BlockIPRequest) (*orlyaclv1.Empty, error) {
 337  	managed := s.getManagedACL()
 338  	if managed == nil {
 339  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 340  	}
 341  	managedACL := managed.GetManagedACL()
 342  	if managedACL == nil {
 343  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 344  	}
 345  	if err := managedACL.SaveBlockedIP(req.Ip, req.Reason); err != nil {
 346  		return nil, status.Errorf(codes.Internal, "failed to block IP: %v", err)
 347  	}
 348  	return &orlyaclv1.Empty{}, nil
 349  }
 350  
 351  func (s *ACLService) UnblockIP(ctx context.Context, req *orlyaclv1.IPRequest) (*orlyaclv1.Empty, error) {
 352  	managed := s.getManagedACL()
 353  	if managed == nil {
 354  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 355  	}
 356  	managedACL := managed.GetManagedACL()
 357  	if managedACL == nil {
 358  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 359  	}
 360  	if err := managedACL.RemoveBlockedIP(req.Ip); err != nil {
 361  		return nil, status.Errorf(codes.Internal, "failed to unblock IP: %v", err)
 362  	}
 363  	return &orlyaclv1.Empty{}, nil
 364  }
 365  
 366  func (s *ACLService) ListBlockedIPs(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBlockedIPsResponse, error) {
 367  	managed := s.getManagedACL()
 368  	if managed == nil {
 369  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 370  	}
 371  	managedACL := managed.GetManagedACL()
 372  	if managedACL == nil {
 373  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 374  	}
 375  	blocked, err := managedACL.ListBlockedIPs()
 376  	if err != nil {
 377  		return nil, status.Errorf(codes.Internal, "failed to list blocked IPs: %v", err)
 378  	}
 379  	resp := &orlyaclv1.ListBlockedIPsResponse{}
 380  	for _, b := range blocked {
 381  		resp.Ips = append(resp.Ips, &orlyaclv1.BlockedIP{
 382  			Ip:     b.IP,
 383  			Reason: b.Reason,
 384  			Added:  b.Added.Unix(),
 385  		})
 386  	}
 387  	return resp, nil
 388  }
 389  
 390  func (s *ACLService) AllowKind(ctx context.Context, req *orlyaclv1.AllowKindRequest) (*orlyaclv1.Empty, error) {
 391  	managed := s.getManagedACL()
 392  	if managed == nil {
 393  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 394  	}
 395  	managedACL := managed.GetManagedACL()
 396  	if managedACL == nil {
 397  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 398  	}
 399  	if err := managedACL.SaveAllowedKind(int(req.Kind)); err != nil {
 400  		return nil, status.Errorf(codes.Internal, "failed to allow kind: %v", err)
 401  	}
 402  	return &orlyaclv1.Empty{}, nil
 403  }
 404  
 405  func (s *ACLService) DisallowKind(ctx context.Context, req *orlyaclv1.KindRequest) (*orlyaclv1.Empty, error) {
 406  	managed := s.getManagedACL()
 407  	if managed == nil {
 408  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 409  	}
 410  	managedACL := managed.GetManagedACL()
 411  	if managedACL == nil {
 412  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 413  	}
 414  	if err := managedACL.RemoveAllowedKind(int(req.Kind)); err != nil {
 415  		return nil, status.Errorf(codes.Internal, "failed to disallow kind: %v", err)
 416  	}
 417  	return &orlyaclv1.Empty{}, nil
 418  }
 419  
 420  func (s *ACLService) ListAllowedKinds(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedKindsResponse, error) {
 421  	managed := s.getManagedACL()
 422  	if managed == nil {
 423  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 424  	}
 425  	managedACL := managed.GetManagedACL()
 426  	if managedACL == nil {
 427  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 428  	}
 429  	kinds, err := managedACL.ListAllowedKinds()
 430  	if err != nil {
 431  		return nil, status.Errorf(codes.Internal, "failed to list allowed kinds: %v", err)
 432  	}
 433  	resp := &orlyaclv1.ListAllowedKindsResponse{}
 434  	for _, k := range kinds {
 435  		resp.Kinds = append(resp.Kinds, int32(k))
 436  	}
 437  	return resp, nil
 438  }
 439  
 440  func (s *ACLService) UpdatePeerAdmins(ctx context.Context, req *orlyaclv1.UpdatePeerAdminsRequest) (*orlyaclv1.Empty, error) {
 441  	managed := s.getManagedACL()
 442  	if managed == nil {
 443  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 444  	}
 445  	managed.UpdatePeerAdmins(req.PeerPubkeys)
 446  	return &orlyaclv1.Empty{}, nil
 447  }
 448  
 449  // === Curating ACL Methods ===
 450  
 451  func (s *ACLService) TrustPubkey(ctx context.Context, req *orlyaclv1.TrustPubkeyRequest) (*orlyaclv1.Empty, error) {
 452  	curating := s.getCuratingACL()
 453  	if curating == nil {
 454  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 455  	}
 456  	if err := curating.TrustPubkey(req.Pubkey, req.Note); err != nil {
 457  		return nil, status.Errorf(codes.Internal, "failed to trust pubkey: %v", err)
 458  	}
 459  	return &orlyaclv1.Empty{}, nil
 460  }
 461  
 462  func (s *ACLService) UntrustPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 463  	curating := s.getCuratingACL()
 464  	if curating == nil {
 465  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 466  	}
 467  	if err := curating.UntrustPubkey(req.Pubkey); err != nil {
 468  		return nil, status.Errorf(codes.Internal, "failed to untrust pubkey: %v", err)
 469  	}
 470  	return &orlyaclv1.Empty{}, nil
 471  }
 472  
 473  func (s *ACLService) ListTrustedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListTrustedPubkeysResponse, error) {
 474  	curating := s.getCuratingACL()
 475  	if curating == nil {
 476  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 477  	}
 478  	curatingACL := curating.GetCuratingACL()
 479  	if curatingACL == nil {
 480  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 481  	}
 482  	trusted, err := curatingACL.ListTrustedPubkeys()
 483  	if err != nil {
 484  		return nil, status.Errorf(codes.Internal, "failed to list trusted pubkeys: %v", err)
 485  	}
 486  	resp := &orlyaclv1.ListTrustedPubkeysResponse{}
 487  	for _, t := range trusted {
 488  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.TrustedPubkey{
 489  			Pubkey: t.Pubkey,
 490  			Note:   t.Note,
 491  			Added:  t.Added.Unix(),
 492  		})
 493  	}
 494  	return resp, nil
 495  }
 496  
 497  func (s *ACLService) BlacklistPubkey(ctx context.Context, req *orlyaclv1.BlacklistPubkeyRequest) (*orlyaclv1.Empty, error) {
 498  	curating := s.getCuratingACL()
 499  	if curating == nil {
 500  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 501  	}
 502  	if err := curating.BlacklistPubkey(req.Pubkey, req.Reason); err != nil {
 503  		return nil, status.Errorf(codes.Internal, "failed to blacklist pubkey: %v", err)
 504  	}
 505  	return &orlyaclv1.Empty{}, nil
 506  }
 507  
 508  func (s *ACLService) UnblacklistPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 509  	curating := s.getCuratingACL()
 510  	if curating == nil {
 511  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 512  	}
 513  	if err := curating.UnblacklistPubkey(req.Pubkey); err != nil {
 514  		return nil, status.Errorf(codes.Internal, "failed to unblacklist pubkey: %v", err)
 515  	}
 516  	return &orlyaclv1.Empty{}, nil
 517  }
 518  
 519  func (s *ACLService) ListBlacklistedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBlacklistedPubkeysResponse, error) {
 520  	curating := s.getCuratingACL()
 521  	if curating == nil {
 522  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 523  	}
 524  	curatingACL := curating.GetCuratingACL()
 525  	if curatingACL == nil {
 526  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 527  	}
 528  	blacklisted, err := curatingACL.ListBlacklistedPubkeys()
 529  	if err != nil {
 530  		return nil, status.Errorf(codes.Internal, "failed to list blacklisted pubkeys: %v", err)
 531  	}
 532  	resp := &orlyaclv1.ListBlacklistedPubkeysResponse{}
 533  	for _, b := range blacklisted {
 534  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.BlacklistedPubkey{
 535  			Pubkey: b.Pubkey,
 536  			Reason: b.Reason,
 537  			Added:  b.Added.Unix(),
 538  		})
 539  	}
 540  	return resp, nil
 541  }
 542  
 543  func (s *ACLService) MarkSpam(ctx context.Context, req *orlyaclv1.MarkSpamRequest) (*orlyaclv1.Empty, error) {
 544  	curating := s.getCuratingACL()
 545  	if curating == nil {
 546  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 547  	}
 548  	curatingACL := curating.GetCuratingACL()
 549  	if curatingACL == nil {
 550  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 551  	}
 552  	if err := curatingACL.MarkEventAsSpam(req.EventId, req.Pubkey, req.Reason); err != nil {
 553  		return nil, status.Errorf(codes.Internal, "failed to mark spam: %v", err)
 554  	}
 555  	return &orlyaclv1.Empty{}, nil
 556  }
 557  
 558  func (s *ACLService) UnmarkSpam(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) {
 559  	curating := s.getCuratingACL()
 560  	if curating == nil {
 561  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 562  	}
 563  	curatingACL := curating.GetCuratingACL()
 564  	if curatingACL == nil {
 565  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 566  	}
 567  	if err := curatingACL.UnmarkEventAsSpam(req.EventId); err != nil {
 568  		return nil, status.Errorf(codes.Internal, "failed to unmark spam: %v", err)
 569  	}
 570  	return &orlyaclv1.Empty{}, nil
 571  }
 572  
 573  func (s *ACLService) ListSpamEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListSpamEventsResponse, error) {
 574  	curating := s.getCuratingACL()
 575  	if curating == nil {
 576  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 577  	}
 578  	curatingACL := curating.GetCuratingACL()
 579  	if curatingACL == nil {
 580  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 581  	}
 582  	spam, err := curatingACL.ListSpamEvents()
 583  	if err != nil {
 584  		return nil, status.Errorf(codes.Internal, "failed to list spam events: %v", err)
 585  	}
 586  	resp := &orlyaclv1.ListSpamEventsResponse{}
 587  	for _, se := range spam {
 588  		resp.Events = append(resp.Events, &orlyaclv1.SpamEvent{
 589  			EventId: se.EventID,
 590  			Pubkey:  se.Pubkey,
 591  			Reason:  se.Reason,
 592  			Added:   se.Added.Unix(),
 593  		})
 594  	}
 595  	return resp, nil
 596  }
 597  
 598  func (s *ACLService) RateLimitCheck(ctx context.Context, req *orlyaclv1.RateLimitCheckRequest) (*orlyaclv1.RateLimitCheckResponse, error) {
 599  	curating := s.getCuratingACL()
 600  	if curating == nil {
 601  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 602  	}
 603  	allowed, message, err := curating.RateLimitCheck(req.Pubkey, req.Ip)
 604  	if err != nil {
 605  		return nil, status.Errorf(codes.Internal, "failed to check rate limit: %v", err)
 606  	}
 607  	return &orlyaclv1.RateLimitCheckResponse{
 608  		Allowed: allowed,
 609  		Message: message,
 610  	}, nil
 611  }
 612  
 613  func (s *ACLService) ProcessConfigEvent(ctx context.Context, req *orlyaclv1.ConfigEventRequest) (*orlyaclv1.Empty, error) {
 614  	curating := s.getCuratingACL()
 615  	if curating == nil {
 616  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 617  	}
 618  	ev := orlydbv1.ProtoToEvent(req.Event)
 619  	if err := curating.ProcessConfigEvent(ev); err != nil {
 620  		return nil, status.Errorf(codes.Internal, "failed to process config event: %v", err)
 621  	}
 622  	return &orlyaclv1.Empty{}, nil
 623  }
 624  
 625  func (s *ACLService) GetCuratingConfig(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.CuratingConfig, error) {
 626  	curating := s.getCuratingACL()
 627  	if curating == nil {
 628  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 629  	}
 630  	config, err := curating.GetConfig()
 631  	if err != nil {
 632  		return nil, status.Errorf(codes.Internal, "failed to get config: %v", err)
 633  	}
 634  	resp := &orlyaclv1.CuratingConfig{
 635  		ConfigEventId:  config.ConfigEventID,
 636  		ConfigPubkey:   config.ConfigPubkey,
 637  		ConfiguredAt:   config.ConfiguredAt,
 638  		DailyLimit:     int32(config.DailyLimit),
 639  		IpDailyLimit:   int32(config.IPDailyLimit),
 640  		FirstBanHours:  int32(config.FirstBanHours),
 641  		SecondBanHours: int32(config.SecondBanHours),
 642  		KindCategories: config.KindCategories,
 643  		AllowedRanges:  config.AllowedRanges,
 644  	}
 645  	for _, k := range config.AllowedKinds {
 646  		resp.AllowedKinds = append(resp.AllowedKinds, int32(k))
 647  	}
 648  	return resp, nil
 649  }
 650  
 651  func (s *ACLService) IsCuratingConfigured(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.BoolResponse, error) {
 652  	curating := s.getCuratingACL()
 653  	if curating == nil {
 654  		return &orlyaclv1.BoolResponse{Value: false}, nil
 655  	}
 656  	configured, err := curating.IsConfigured()
 657  	if err != nil {
 658  		return nil, status.Errorf(codes.Internal, "failed to check if configured: %v", err)
 659  	}
 660  	return &orlyaclv1.BoolResponse{Value: configured}, nil
 661  }
 662  
 663  func (s *ACLService) ListUnclassifiedUsers(ctx context.Context, req *orlyaclv1.PaginationRequest) (*orlyaclv1.ListUnclassifiedUsersResponse, error) {
 664  	curating := s.getCuratingACL()
 665  	if curating == nil {
 666  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 667  	}
 668  	curatingACL := curating.GetCuratingACL()
 669  	if curatingACL == nil {
 670  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 671  	}
 672  	// The underlying ListUnclassifiedUsers only takes limit, not offset
 673  	// We'll request limit+offset and skip the first offset items
 674  	limit := int(req.Limit)
 675  	offset := int(req.Offset)
 676  	if limit == 0 {
 677  		limit = 100 // Default limit
 678  	}
 679  	users, err := curatingACL.ListUnclassifiedUsers(limit + offset)
 680  	if err != nil {
 681  		return nil, status.Errorf(codes.Internal, "failed to list unclassified users: %v", err)
 682  	}
 683  	// Apply offset
 684  	if offset > 0 && len(users) > offset {
 685  		users = users[offset:]
 686  	} else if offset > 0 {
 687  		users = nil
 688  	}
 689  	// Apply limit
 690  	if limit > 0 && len(users) > limit {
 691  		users = users[:limit]
 692  	}
 693  	resp := &orlyaclv1.ListUnclassifiedUsersResponse{Total: int32(len(users))}
 694  	for _, u := range users {
 695  		resp.Users = append(resp.Users, &orlyaclv1.UnclassifiedUser{
 696  			Pubkey:     u.Pubkey,
 697  			EventCount: int32(u.EventCount),
 698  			FirstSeen:  u.LastEvent.Format("2006-01-02T15:04:05Z"),
 699  		})
 700  	}
 701  	return resp, nil
 702  }
 703  
 704  func (s *ACLService) GetEventsForPubkey(ctx context.Context, req *orlyaclv1.GetEventsForPubkeyRequest) (*orlyaclv1.EventsForPubkeyResponse, error) {
 705  	curating := s.getCuratingACL()
 706  	if curating == nil {
 707  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 708  	}
 709  	curatingACL := curating.GetCuratingACL()
 710  	if curatingACL == nil {
 711  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 712  	}
 713  	events, total, err := curatingACL.GetEventsForPubkey(req.Pubkey, int(req.Limit), int(req.Offset))
 714  	if err != nil {
 715  		return nil, status.Errorf(codes.Internal, "failed to get events for pubkey: %v", err)
 716  	}
 717  	resp := &orlyaclv1.EventsForPubkeyResponse{Total: int32(total)}
 718  	for _, ev := range events {
 719  		resp.Events = append(resp.Events, &orlyaclv1.EventSummary{
 720  			Id:        ev.ID,
 721  			Kind:      uint32(ev.Kind),
 722  			Content:   []byte(ev.Content),
 723  			CreatedAt: ev.CreatedAt,
 724  		})
 725  	}
 726  	return resp, nil
 727  }
 728  
 729  func (s *ACLService) DeleteEventsForPubkey(ctx context.Context, req *orlyaclv1.DeleteEventsForPubkeyRequest) (*orlyaclv1.DeleteCountResponse, error) {
 730  	curating := s.getCuratingACL()
 731  	if curating == nil {
 732  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 733  	}
 734  	curatingACL := curating.GetCuratingACL()
 735  	if curatingACL == nil {
 736  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 737  	}
 738  	count, err := curatingACL.DeleteEventsForPubkey(req.Pubkey)
 739  	if err != nil {
 740  		return nil, status.Errorf(codes.Internal, "failed to delete events for pubkey: %v", err)
 741  	}
 742  	return &orlyaclv1.DeleteCountResponse{Count: int32(count)}, nil
 743  }
 744  
 745  func (s *ACLService) ScanAllPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ScanResultResponse, error) {
 746  	curating := s.getCuratingACL()
 747  	if curating == nil {
 748  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 749  	}
 750  	curatingACL := curating.GetCuratingACL()
 751  	if curatingACL == nil {
 752  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 753  	}
 754  	result, err := curatingACL.ScanAllPubkeys()
 755  	if err != nil {
 756  		return nil, status.Errorf(codes.Internal, "failed to scan all pubkeys: %v", err)
 757  	}
 758  	return &orlyaclv1.ScanResultResponse{
 759  		TotalPubkeys: int32(result.TotalPubkeys),
 760  		TotalEvents:  int32(result.TotalEvents),
 761  	}, nil
 762  }
 763  
 764  // === Helper Methods ===
 765  
 766  func (s *ACLService) getManagedACL() *acl.Managed {
 767  	for _, i := range acl.Registry.ACLs() {
 768  		if i.Type() == "managed" {
 769  			if managed, ok := i.(*acl.Managed); ok {
 770  				return managed
 771  			}
 772  		}
 773  	}
 774  	return nil
 775  }
 776  
 777  func (s *ACLService) getCuratingACL() *acl.Curating {
 778  	for _, i := range acl.Registry.ACLs() {
 779  		if i.Type() == "curating" {
 780  			if curating, ok := i.(*acl.Curating); ok {
 781  				return curating
 782  			}
 783  		}
 784  	}
 785  	return nil
 786  }
 787  
 788  // === Paid ACL Methods ===
 789  
 790  func (s *ACLService) getPaidACL() *acl.Paid {
 791  	for _, i := range acl.Registry.ACLs() {
 792  		if i.Type() == "paid" {
 793  			if paid, ok := i.(*acl.Paid); ok {
 794  				return paid
 795  			}
 796  		}
 797  	}
 798  	return nil
 799  }
 800  
 801  func (s *ACLService) SubscribePubkey(ctx context.Context, req *orlyaclv1.SubscribeRequest) (*orlyaclv1.Empty, error) {
 802  	paid := s.getPaidACL()
 803  	if paid == nil {
 804  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 805  	}
 806  	expiresAt := time.Unix(req.ExpiresAt, 0)
 807  	if err := paid.Subscribe(req.Pubkey, expiresAt, req.InvoiceHash, req.Alias); err != nil {
 808  		return nil, status.Errorf(codes.Internal, "failed to subscribe: %v", err)
 809  	}
 810  	return &orlyaclv1.Empty{}, nil
 811  }
 812  
 813  func (s *ACLService) UnsubscribePubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 814  	paid := s.getPaidACL()
 815  	if paid == nil {
 816  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 817  	}
 818  	if err := paid.Unsubscribe(req.Pubkey); err != nil {
 819  		return nil, status.Errorf(codes.Internal, "failed to unsubscribe: %v", err)
 820  	}
 821  	return &orlyaclv1.Empty{}, nil
 822  }
 823  
 824  func (s *ACLService) IsSubscribed(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.BoolResponse, error) {
 825  	paid := s.getPaidACL()
 826  	if paid == nil {
 827  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 828  	}
 829  	return &orlyaclv1.BoolResponse{Value: paid.IsSubscribed(req.Pubkey)}, nil
 830  }
 831  
 832  func (s *ACLService) GetSubscription(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.SubscriptionResponse, error) {
 833  	paid := s.getPaidACL()
 834  	if paid == nil {
 835  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 836  	}
 837  	sub, err := paid.GetSubscription(req.Pubkey)
 838  	if err != nil {
 839  		return nil, status.Errorf(codes.NotFound, "subscription not found: %v", err)
 840  	}
 841  	alias, _ := paid.GetAliasByPubkey(req.Pubkey)
 842  	return &orlyaclv1.SubscriptionResponse{
 843  		Pubkey:    sub.PubkeyHex,
 844  		Alias:     alias,
 845  		ExpiresAt: sub.ExpiresAt.Unix(),
 846  		CreatedAt: sub.CreatedAt.Unix(),
 847  		HasAlias:  alias != "",
 848  	}, nil
 849  }
 850  
 851  func (s *ACLService) ClaimAlias(ctx context.Context, req *orlyaclv1.ClaimAliasRequest) (*orlyaclv1.Empty, error) {
 852  	paid := s.getPaidACL()
 853  	if paid == nil {
 854  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 855  	}
 856  	if err := paid.ClaimAlias(req.Alias, req.Pubkey); err != nil {
 857  		if errors.Is(err, database.ErrAliasTaken) {
 858  			return nil, status.Errorf(codes.AlreadyExists, "alias already taken")
 859  		}
 860  		return nil, status.Errorf(codes.Internal, "failed to claim alias: %v", err)
 861  	}
 862  	return &orlyaclv1.Empty{}, nil
 863  }
 864  
 865  func (s *ACLService) GetAliasByPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.AliasResponse, error) {
 866  	paid := s.getPaidACL()
 867  	if paid == nil {
 868  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 869  	}
 870  	alias, err := paid.GetAliasByPubkey(req.Pubkey)
 871  	if err != nil {
 872  		return nil, status.Errorf(codes.Internal, "failed to get alias: %v", err)
 873  	}
 874  	return &orlyaclv1.AliasResponse{Alias: alias}, nil
 875  }
 876  
 877  func (s *ACLService) GetPubkeyByAlias(ctx context.Context, req *orlyaclv1.AliasRequest) (*orlyaclv1.PubkeyResponse, error) {
 878  	paid := s.getPaidACL()
 879  	if paid == nil {
 880  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 881  	}
 882  	pubkey, err := paid.GetPubkeyByAlias(req.Alias)
 883  	if err != nil {
 884  		return nil, status.Errorf(codes.NotFound, "alias not found: %v", err)
 885  	}
 886  	return &orlyaclv1.PubkeyResponse{Pubkey: pubkey}, nil
 887  }
 888  
 889  func (s *ACLService) IsAliasTaken(ctx context.Context, req *orlyaclv1.AliasRequest) (*orlyaclv1.BoolResponse, error) {
 890  	paid := s.getPaidACL()
 891  	if paid == nil {
 892  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 893  	}
 894  	taken, err := paid.IsAliasTaken(req.Alias)
 895  	if err != nil {
 896  		return nil, status.Errorf(codes.Internal, "failed to check alias: %v", err)
 897  	}
 898  	return &orlyaclv1.BoolResponse{Value: taken}, nil
 899  }
 900  
 901  // Unused but may be needed for debugging
 902  var _ = log.T
 903  var _ = hex.EncodeToString
 904