A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
atcr.io
docker
container
atproto
go
1package auth
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7
8 "atcr.io/pkg/atproto"
9)
10
11// HoldAuthorizer checks if a DID has read/write access to a hold
12// Implementations can query local PDS (hold service) or remote XRPC (appview)
13type HoldAuthorizer interface {
14 // CheckReadAccess checks if userDID can read from holdDID
15 // Returns: (allowed bool, error)
16 CheckReadAccess(ctx context.Context, holdDID, userDID string) (bool, error)
17
18 // CheckWriteAccess checks if userDID can write to holdDID
19 // Returns: (allowed bool, error)
20 CheckWriteAccess(ctx context.Context, holdDID, userDID string) (bool, error)
21
22 // GetCaptainRecord retrieves the captain record for a hold
23 // Used to check public flag and allowAllCrew settings
24 GetCaptainRecord(ctx context.Context, holdDID string) (*atproto.CaptainRecord, error)
25
26 // IsCrewMember checks if userDID is a crew member of holdDID
27 IsCrewMember(ctx context.Context, holdDID, userDID string) (bool, error)
28}
29
30// CheckReadAccessWithCaptain implements the standard read authorization logic
31// This is shared across all HoldAuthorizer implementations
32// Read access rules:
33// - Public hold: allow anyone (even anonymous)
34// - Private hold: require authentication (any authenticated user)
35func CheckReadAccessWithCaptain(captain *atproto.CaptainRecord, userDID string) bool {
36 if captain.Public {
37 // Public hold - allow anyone (even anonymous)
38 return true
39 }
40
41 // Private hold - require authentication
42 // Any authenticated user with a DID can read
43 if userDID == "" {
44 // Anonymous user trying to access private hold
45 return false
46 }
47
48 // For MVP: assume DID presence means they have sailor.profile
49 // Future: could query PDS to verify sailor.profile exists
50 return true
51}
52
53// CheckWriteAccessWithCaptain implements the standard write authorization logic
54// This is shared across all HoldAuthorizer implementations
55// Write access rules:
56// - Must be authenticated
57// - Must be hold owner OR crew member
58func CheckWriteAccessWithCaptain(captain *atproto.CaptainRecord, userDID string, isCrew bool) bool {
59 slog.Debug("Checking write access", "userDID", userDID, "owner", captain.Owner, "isCrew", isCrew)
60
61 if userDID == "" {
62 // Anonymous writes not allowed
63 slog.Debug("Write access denied: anonymous user")
64 return false
65 }
66
67 // Check if DID is the hold owner
68 if userDID == captain.Owner {
69 // Owner always has write access
70 slog.Debug("Write access allowed: user is hold owner")
71 return true
72 }
73
74 // Check if DID is a crew member
75 if isCrew {
76 slog.Debug("Write access allowed: user is crew member")
77 } else {
78 slog.Debug("Write access denied: user is not owner or crew")
79 }
80 return isCrew
81}
82
83// ErrHoldNotFound is returned when a hold's captain record cannot be found
84var ErrHoldNotFound = fmt.Errorf("hold not found")
85
86// ErrUnauthorized is returned when access is denied
87var ErrUnauthorized = fmt.Errorf("unauthorized")