You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
793 lines
28 KiB
793 lines
28 KiB
package main |
|
|
|
import ( |
|
"context" |
|
"encoding/hex" |
|
|
|
"google.golang.org/grpc/codes" |
|
"google.golang.org/grpc/status" |
|
"lol.mleku.dev/log" |
|
|
|
"next.orly.dev/pkg/acl" |
|
"next.orly.dev/pkg/database" |
|
orlyaclv1 "next.orly.dev/pkg/proto/orlyacl/v1" |
|
orlydbv1 "next.orly.dev/pkg/proto/orlydb/v1" |
|
) |
|
|
|
// ACLService implements the orlyaclv1.ACLServiceServer interface. |
|
type ACLService struct { |
|
orlyaclv1.UnimplementedACLServiceServer |
|
cfg *Config |
|
db database.Database |
|
ready bool |
|
} |
|
|
|
// NewACLService creates a new ACL service. |
|
func NewACLService(cfg *Config, db database.Database) *ACLService { |
|
return &ACLService{ |
|
cfg: cfg, |
|
db: db, |
|
ready: false, // Not ready until Configure completes |
|
} |
|
} |
|
|
|
// SetReady marks the service as ready (or not ready). |
|
func (s *ACLService) SetReady(ready bool) { |
|
s.ready = ready |
|
if ready { |
|
log.I.F("ACL service is now ready") |
|
} |
|
} |
|
|
|
// === Core ACL Methods === |
|
|
|
func (s *ACLService) GetAccessLevel(ctx context.Context, req *orlyaclv1.AccessLevelRequest) (*orlyaclv1.AccessLevelResponse, error) { |
|
level := acl.Registry.GetAccessLevel(req.Pubkey, req.Address) |
|
return &orlyaclv1.AccessLevelResponse{Level: level}, nil |
|
} |
|
|
|
func (s *ACLService) CheckPolicy(ctx context.Context, req *orlyaclv1.PolicyCheckRequest) (*orlyaclv1.PolicyCheckResponse, error) { |
|
ev := orlydbv1.ProtoToEvent(req.Event) |
|
allowed, err := acl.Registry.CheckPolicy(ev) |
|
resp := &orlyaclv1.PolicyCheckResponse{Allowed: allowed} |
|
if err != nil { |
|
resp.Error = err.Error() |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) GetACLInfo(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ACLInfoResponse, error) { |
|
name, description, documentation := acl.Registry.GetACLInfo() |
|
return &orlyaclv1.ACLInfoResponse{ |
|
Name: name, |
|
Description: description, |
|
Documentation: documentation, |
|
}, nil |
|
} |
|
|
|
func (s *ACLService) GetMode(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ModeResponse, error) { |
|
return &orlyaclv1.ModeResponse{Mode: acl.Registry.Type()}, nil |
|
} |
|
|
|
func (s *ACLService) Ready(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ReadyResponse, error) { |
|
// Check if service is fully configured and ready |
|
return &orlyaclv1.ReadyResponse{Ready: s.ready}, nil |
|
} |
|
|
|
// === Follows ACL Methods === |
|
|
|
func (s *ACLService) GetThrottleDelay(ctx context.Context, req *orlyaclv1.ThrottleDelayRequest) (*orlyaclv1.ThrottleDelayResponse, error) { |
|
// Get the active ACL and check if it's Follows |
|
for _, i := range acl.Registry.ACL { |
|
if i.Type() == "follows" { |
|
if follows, ok := i.(*acl.Follows); ok { |
|
delay := follows.GetThrottleDelay(req.Pubkey, req.Ip) |
|
return &orlyaclv1.ThrottleDelayResponse{DelayMs: delay.Milliseconds()}, nil |
|
} |
|
} |
|
} |
|
return &orlyaclv1.ThrottleDelayResponse{DelayMs: 0}, nil |
|
} |
|
|
|
func (s *ACLService) AddFollow(ctx context.Context, req *orlyaclv1.AddFollowRequest) (*orlyaclv1.Empty, error) { |
|
acl.Registry.AddFollow(req.Pubkey) |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) GetFollowedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.FollowedPubkeysResponse, error) { |
|
for _, i := range acl.Registry.ACL { |
|
if i.Type() == "follows" { |
|
if follows, ok := i.(*acl.Follows); ok { |
|
pubkeys := follows.GetFollowedPubkeys() |
|
return &orlyaclv1.FollowedPubkeysResponse{Pubkeys: pubkeys}, nil |
|
} |
|
} |
|
} |
|
return &orlyaclv1.FollowedPubkeysResponse{}, nil |
|
} |
|
|
|
func (s *ACLService) GetAdminRelays(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.AdminRelaysResponse, error) { |
|
for _, i := range acl.Registry.ACL { |
|
if i.Type() == "follows" { |
|
if follows, ok := i.(*acl.Follows); ok { |
|
urls := follows.AdminRelays() |
|
return &orlyaclv1.AdminRelaysResponse{Urls: urls}, nil |
|
} |
|
} |
|
} |
|
return &orlyaclv1.AdminRelaysResponse{}, nil |
|
} |
|
|
|
// === Managed ACL Methods === |
|
|
|
func (s *ACLService) BanPubkey(ctx context.Context, req *orlyaclv1.BanPubkeyRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.SaveBannedPubkey(req.Pubkey, req.Reason); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to ban pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) UnbanPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.RemoveBannedPubkey(req.Pubkey); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to unban pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListBannedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBannedPubkeysResponse, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
banned, err := managedACL.ListBannedPubkeys() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list banned pubkeys: %v", err) |
|
} |
|
resp := &orlyaclv1.ListBannedPubkeysResponse{} |
|
for _, b := range banned { |
|
resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.BannedPubkey{ |
|
Pubkey: b.Pubkey, |
|
Reason: b.Reason, |
|
Added: b.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) AllowPubkey(ctx context.Context, req *orlyaclv1.AllowPubkeyRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.SaveAllowedPubkey(req.Pubkey, req.Reason); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to allow pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) DisallowPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.RemoveAllowedPubkey(req.Pubkey); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to disallow pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListAllowedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedPubkeysResponse, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
allowed, err := managedACL.ListAllowedPubkeys() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list allowed pubkeys: %v", err) |
|
} |
|
resp := &orlyaclv1.ListAllowedPubkeysResponse{} |
|
for _, a := range allowed { |
|
resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.AllowedPubkey{ |
|
Pubkey: a.Pubkey, |
|
Reason: a.Reason, |
|
Added: a.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) BanEvent(ctx context.Context, req *orlyaclv1.BanEventRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.SaveBannedEvent(req.EventId, req.Reason); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to ban event: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) UnbanEvent(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.RemoveBannedEvent(req.EventId); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to unban event: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListBannedEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBannedEventsResponse, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
banned, err := managedACL.ListBannedEvents() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list banned events: %v", err) |
|
} |
|
resp := &orlyaclv1.ListBannedEventsResponse{} |
|
for _, b := range banned { |
|
resp.Events = append(resp.Events, &orlyaclv1.BannedEvent{ |
|
EventId: b.ID, |
|
Reason: b.Reason, |
|
Added: b.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) AllowEvent(ctx context.Context, req *orlyaclv1.BanEventRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.SaveAllowedEvent(req.EventId, req.Reason); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to allow event: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) DisallowEvent(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.RemoveAllowedEvent(req.EventId); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to disallow event: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListAllowedEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedEventsResponse, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
allowed, err := managedACL.ListAllowedEvents() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list allowed events: %v", err) |
|
} |
|
resp := &orlyaclv1.ListAllowedEventsResponse{} |
|
for _, a := range allowed { |
|
resp.Events = append(resp.Events, &orlyaclv1.AllowedEvent{ |
|
EventId: a.ID, |
|
Reason: a.Reason, |
|
Added: a.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) BlockIP(ctx context.Context, req *orlyaclv1.BlockIPRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.SaveBlockedIP(req.Ip, req.Reason); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to block IP: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) UnblockIP(ctx context.Context, req *orlyaclv1.IPRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.RemoveBlockedIP(req.Ip); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to unblock IP: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListBlockedIPs(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBlockedIPsResponse, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
blocked, err := managedACL.ListBlockedIPs() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list blocked IPs: %v", err) |
|
} |
|
resp := &orlyaclv1.ListBlockedIPsResponse{} |
|
for _, b := range blocked { |
|
resp.Ips = append(resp.Ips, &orlyaclv1.BlockedIP{ |
|
Ip: b.IP, |
|
Reason: b.Reason, |
|
Added: b.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) AllowKind(ctx context.Context, req *orlyaclv1.AllowKindRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.SaveAllowedKind(int(req.Kind)); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to allow kind: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) DisallowKind(ctx context.Context, req *orlyaclv1.KindRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
if err := managedACL.RemoveAllowedKind(int(req.Kind)); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to disallow kind: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListAllowedKinds(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedKindsResponse, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managedACL := managed.GetManagedACL() |
|
if managedACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available") |
|
} |
|
kinds, err := managedACL.ListAllowedKinds() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list allowed kinds: %v", err) |
|
} |
|
resp := &orlyaclv1.ListAllowedKindsResponse{} |
|
for _, k := range kinds { |
|
resp.Kinds = append(resp.Kinds, int32(k)) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) UpdatePeerAdmins(ctx context.Context, req *orlyaclv1.UpdatePeerAdminsRequest) (*orlyaclv1.Empty, error) { |
|
managed := s.getManagedACL() |
|
if managed == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available") |
|
} |
|
managed.UpdatePeerAdmins(req.PeerPubkeys) |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
// === Curating ACL Methods === |
|
|
|
func (s *ACLService) TrustPubkey(ctx context.Context, req *orlyaclv1.TrustPubkeyRequest) (*orlyaclv1.Empty, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
if err := curating.TrustPubkey(req.Pubkey, req.Note); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to trust pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) UntrustPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
if err := curating.UntrustPubkey(req.Pubkey); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to untrust pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListTrustedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListTrustedPubkeysResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
trusted, err := curatingACL.ListTrustedPubkeys() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list trusted pubkeys: %v", err) |
|
} |
|
resp := &orlyaclv1.ListTrustedPubkeysResponse{} |
|
for _, t := range trusted { |
|
resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.TrustedPubkey{ |
|
Pubkey: t.Pubkey, |
|
Note: t.Note, |
|
Added: t.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) BlacklistPubkey(ctx context.Context, req *orlyaclv1.BlacklistPubkeyRequest) (*orlyaclv1.Empty, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
if err := curating.BlacklistPubkey(req.Pubkey, req.Reason); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to blacklist pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) UnblacklistPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
if err := curating.UnblacklistPubkey(req.Pubkey); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to unblacklist pubkey: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListBlacklistedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBlacklistedPubkeysResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
blacklisted, err := curatingACL.ListBlacklistedPubkeys() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list blacklisted pubkeys: %v", err) |
|
} |
|
resp := &orlyaclv1.ListBlacklistedPubkeysResponse{} |
|
for _, b := range blacklisted { |
|
resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.BlacklistedPubkey{ |
|
Pubkey: b.Pubkey, |
|
Reason: b.Reason, |
|
Added: b.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) MarkSpam(ctx context.Context, req *orlyaclv1.MarkSpamRequest) (*orlyaclv1.Empty, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
if err := curatingACL.MarkEventAsSpam(req.EventId, req.Pubkey, req.Reason); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to mark spam: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) UnmarkSpam(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
if err := curatingACL.UnmarkEventAsSpam(req.EventId); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to unmark spam: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) ListSpamEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListSpamEventsResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
spam, err := curatingACL.ListSpamEvents() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list spam events: %v", err) |
|
} |
|
resp := &orlyaclv1.ListSpamEventsResponse{} |
|
for _, se := range spam { |
|
resp.Events = append(resp.Events, &orlyaclv1.SpamEvent{ |
|
EventId: se.EventID, |
|
Pubkey: se.Pubkey, |
|
Reason: se.Reason, |
|
Added: se.Added.Unix(), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) RateLimitCheck(ctx context.Context, req *orlyaclv1.RateLimitCheckRequest) (*orlyaclv1.RateLimitCheckResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
allowed, message, err := curating.RateLimitCheck(req.Pubkey, req.Ip) |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to check rate limit: %v", err) |
|
} |
|
return &orlyaclv1.RateLimitCheckResponse{ |
|
Allowed: allowed, |
|
Message: message, |
|
}, nil |
|
} |
|
|
|
func (s *ACLService) ProcessConfigEvent(ctx context.Context, req *orlyaclv1.ConfigEventRequest) (*orlyaclv1.Empty, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
ev := orlydbv1.ProtoToEvent(req.Event) |
|
if err := curating.ProcessConfigEvent(ev); err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to process config event: %v", err) |
|
} |
|
return &orlyaclv1.Empty{}, nil |
|
} |
|
|
|
func (s *ACLService) GetCuratingConfig(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.CuratingConfig, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
config, err := curating.GetConfig() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to get config: %v", err) |
|
} |
|
resp := &orlyaclv1.CuratingConfig{ |
|
ConfigEventId: config.ConfigEventID, |
|
ConfigPubkey: config.ConfigPubkey, |
|
ConfiguredAt: config.ConfiguredAt, |
|
DailyLimit: int32(config.DailyLimit), |
|
IpDailyLimit: int32(config.IPDailyLimit), |
|
FirstBanHours: int32(config.FirstBanHours), |
|
SecondBanHours: int32(config.SecondBanHours), |
|
KindCategories: config.KindCategories, |
|
AllowedRanges: config.AllowedRanges, |
|
} |
|
for _, k := range config.AllowedKinds { |
|
resp.AllowedKinds = append(resp.AllowedKinds, int32(k)) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) IsCuratingConfigured(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.BoolResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return &orlyaclv1.BoolResponse{Value: false}, nil |
|
} |
|
configured, err := curating.IsConfigured() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to check if configured: %v", err) |
|
} |
|
return &orlyaclv1.BoolResponse{Value: configured}, nil |
|
} |
|
|
|
func (s *ACLService) ListUnclassifiedUsers(ctx context.Context, req *orlyaclv1.PaginationRequest) (*orlyaclv1.ListUnclassifiedUsersResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
// The underlying ListUnclassifiedUsers only takes limit, not offset |
|
// We'll request limit+offset and skip the first offset items |
|
limit := int(req.Limit) |
|
offset := int(req.Offset) |
|
if limit == 0 { |
|
limit = 100 // Default limit |
|
} |
|
users, err := curatingACL.ListUnclassifiedUsers(limit + offset) |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to list unclassified users: %v", err) |
|
} |
|
// Apply offset |
|
if offset > 0 && len(users) > offset { |
|
users = users[offset:] |
|
} else if offset > 0 { |
|
users = nil |
|
} |
|
// Apply limit |
|
if limit > 0 && len(users) > limit { |
|
users = users[:limit] |
|
} |
|
resp := &orlyaclv1.ListUnclassifiedUsersResponse{Total: int32(len(users))} |
|
for _, u := range users { |
|
resp.Users = append(resp.Users, &orlyaclv1.UnclassifiedUser{ |
|
Pubkey: u.Pubkey, |
|
EventCount: int32(u.EventCount), |
|
FirstSeen: u.LastEvent.Format("2006-01-02T15:04:05Z"), |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) GetEventsForPubkey(ctx context.Context, req *orlyaclv1.GetEventsForPubkeyRequest) (*orlyaclv1.EventsForPubkeyResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
events, total, err := curatingACL.GetEventsForPubkey(req.Pubkey, int(req.Limit), int(req.Offset)) |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to get events for pubkey: %v", err) |
|
} |
|
resp := &orlyaclv1.EventsForPubkeyResponse{Total: int32(total)} |
|
for _, ev := range events { |
|
resp.Events = append(resp.Events, &orlyaclv1.EventSummary{ |
|
Id: ev.ID, |
|
Kind: uint32(ev.Kind), |
|
Content: []byte(ev.Content), |
|
CreatedAt: ev.CreatedAt, |
|
}) |
|
} |
|
return resp, nil |
|
} |
|
|
|
func (s *ACLService) DeleteEventsForPubkey(ctx context.Context, req *orlyaclv1.DeleteEventsForPubkeyRequest) (*orlyaclv1.DeleteCountResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
count, err := curatingACL.DeleteEventsForPubkey(req.Pubkey) |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to delete events for pubkey: %v", err) |
|
} |
|
return &orlyaclv1.DeleteCountResponse{Count: int32(count)}, nil |
|
} |
|
|
|
func (s *ACLService) ScanAllPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ScanResultResponse, error) { |
|
curating := s.getCuratingACL() |
|
if curating == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available") |
|
} |
|
curatingACL := curating.GetCuratingACL() |
|
if curatingACL == nil { |
|
return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available") |
|
} |
|
result, err := curatingACL.ScanAllPubkeys() |
|
if err != nil { |
|
return nil, status.Errorf(codes.Internal, "failed to scan all pubkeys: %v", err) |
|
} |
|
return &orlyaclv1.ScanResultResponse{ |
|
TotalPubkeys: int32(result.TotalPubkeys), |
|
TotalEvents: int32(result.TotalEvents), |
|
}, nil |
|
} |
|
|
|
// === Helper Methods === |
|
|
|
func (s *ACLService) getManagedACL() *acl.Managed { |
|
for _, i := range acl.Registry.ACL { |
|
if i.Type() == "managed" { |
|
if managed, ok := i.(*acl.Managed); ok { |
|
return managed |
|
} |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func (s *ACLService) getCuratingACL() *acl.Curating { |
|
for _, i := range acl.Registry.ACL { |
|
if i.Type() == "curating" { |
|
if curating, ok := i.(*acl.Curating); ok { |
|
return curating |
|
} |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
// Unused but may be needed for debugging |
|
var _ = log.T |
|
var _ = hex.EncodeToString
|
|
|