package scope import ( "errors" "fmt" "strconv" "strings" "source.toby3d.me/toby3d/auth/internal/common" ) // Scope represent single token scope supported by IndieAuth. // // NOTE(toby3d): Encapsulate enums in structs for extra compile-time safety: // https://threedots.tech/post/safer-enums-in-go/#struct-based-enums type Scope struct { scope string } var ErrScopeUnknown error = errors.New("unknown scope") //nolint:gochecknoglobals // structs cannot be constants var ( Und = Scope{} // "und" // https://indieweb.org/scope#Micropub_Scopes Create = Scope{"create"} // "create" Delete = Scope{"delete"} // "delete" Draft = Scope{"draft"} // "draft" Media = Scope{"media"} // "media" Undelete = Scope{"undelete"} // "undelete" Update = Scope{"update"} // "update" // https://indieweb.org/scope#Microsub_Scopes Block = Scope{"block"} // "block" Channels = Scope{"channels"} // "channels" Follow = Scope{"follow"} // "follow" Mute = Scope{"mute"} // "mute" Read = Scope{"read"} // "read" // This scope requests access to the user's default profile information // which include the following properties: name, photo, url. // // NOTE(toby3d): https://indieauth.net/source/#profile-information Profile = Scope{"profile"} // "profile" // This scope requests access to the user's email address in the // following property: email. // // Note that because the profile scope is required when requesting // profile information, the email scope cannot be requested on its own // and must be requested along with the profile scope if desired. // // NOTE(toby3d): https://indieauth.net/source/#profile-information Email = Scope{"email"} // "email" ) //nolint:gochecknoglobals // maps cannot be constants var uidsScopes = map[string]Scope{ Block.scope: Block, Channels.scope: Channels, Create.scope: Create, Delete.scope: Delete, Draft.scope: Draft, Email.scope: Email, Follow.scope: Follow, Media.scope: Media, Mute.scope: Mute, Profile.scope: Profile, Read.scope: Read, Undelete.scope: Undelete, Update.scope: Update, } // Parse parses scope slug into Scope domain. func Parse(uid string) (Scope, error) { if scope, ok := uidsScopes[strings.ToLower(uid)]; ok { return scope, nil } return Und, fmt.Errorf("%w: %s", ErrScopeUnknown, uid) } func (s *Scope) UnmarshalJSON(v []byte) error { src, err := strconv.Unquote(string(v)) if err != nil { return fmt.Errorf("Scope: UnmarshalJSON: cannot unquote string: %w", err) } out, err := Parse(src) if err != nil { return fmt.Errorf("Scope: UnmarshalJSON: cannot parse scope: %w", err) } *s = out return nil } func (s Scope) MarshalJSON() ([]byte, error) { return []byte(strconv.Quote(s.scope)), nil } // String returns string representation of scope. func (s Scope) String() string { if s == Und { return common.Und } return s.scope } func (s Scope) GoString() string { return "scope.Scope(" + s.String() + ")" }