Something went wrong. Try again.
A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
Something went wrong. Try again.
2.6 kB · 95 lines
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596package auth
import ( "fmt" "strings")
// AccessEntry represents access permissions for a resourcetype AccessEntry struct { Type string `json:"type"` // "repository" Name string `json:"name,omitempty"` // e.g., "alice/myapp" Actions []string `json:"actions,omitempty"` // e.g., ["pull", "push"]}
// ParseScope parses Docker registry scope strings into AccessEntry structures// Scope format: "repository:alice/myapp:pull,push"// Multiple scopes can be providedfunc ParseScope(scopes []string) ([]AccessEntry, error) { var access []AccessEntry
for _, scope := range scopes { if scope == "" { continue }
parts := strings.Split(scope, ":") if len(parts) < 2 { return nil, fmt.Errorf("invalid scope format: %s", scope) }
resourceType := parts[0]
var name string var actions []string
if len(parts) == 2 { // Format: "repository:alice/myapp" (no actions specified) name = parts[1] } else if len(parts) == 3 { // Format: "repository:alice/myapp:pull,push" name = parts[1] if parts[2] != "" { actions = strings.Split(parts[2], ",") } } else { return nil, fmt.Errorf("invalid scope format: %s", scope) }
access = append(access, AccessEntry{ Type: resourceType, Name: name, Actions: actions, }) }
return access, nil}
// ValidateAccess checks if the requested access is allowed for the user// For ATCR, users can only push to repositories under their own handle/DIDfunc ValidateAccess(userDID, userHandle string, access []AccessEntry) error { for _, entry := range access { if entry.Type != "repository" { continue }
// Allow wildcard scope (e.g., "repository:*:pull,push") // This is used by Docker credential helpers to request broad permissions // Actual authorization happens later when accessing specific repositories if entry.Name == "*" { continue }
// Extract the owner from repository name (e.g., "alice/myapp" -> "alice") parts := strings.SplitN(entry.Name, "/", 2) if len(parts) < 1 { return fmt.Errorf("invalid repository name: %s", entry.Name) }
repoOwner := parts[0]
// Check if user is trying to access their own repository // They can use either their handle or DID if repoOwner != userHandle && repoOwner != userDID { // For push/delete operations, strict ownership check for _, action := range entry.Actions { if action == "push" || action == "delete" { return fmt.Errorf("user %s cannot %s to repository %s", userHandle, action, entry.Name) } } } }
return nil}