Documentation
¶
Overview ¶
Package security enforces process-wide rules for authenticated identity, guarded HTTP, validated external names, and protected local files. Subsystems retain their domain types and compose these mechanisms instead of duplicating checks. Classified errors preserve the wire contract; unclassified errors remain opaque, and Redact bounds any external failure a caller elects to preserve.
Outbound HTTP primitives shared by external-service callers. UserAgent identifies the binary to the services it calls; the composition root sets it once at startup. A GuardedClient follows only same-origin redirects, so the Authorization header Go preserves across such a hop never reaches another host.
Private-file primitives shared by every subsystem that persists state or secrets. Directories must belong to this user at mode 0700; reads and process locks refuse links, loose modes, and foreign ownership. Replacement writes and syncs a mode-0600 temporary file before an atomic rename, then syncs the containing directory. Every path is verified when used, never trusted by its history.
Origins is the one browser-origin policy the OAuth boundary and the session routes apply. An origin is an exact HTTPS or loopback HTTP scheme and host. A request without Origin is a native client and passes; a present Origin must be allowlisted. Routes that serve only browsers additionally require Origin, which is the caller's decision, not this policy's.
Principal is the identity the OAuth boundary resolves and every subsystem scopes state by. It travels in the request context rather than in request or state structs. Shared name predicates also live here because callers validate identities and protected path segments without owning a common feature vocabulary.
Every cs-plane WebSocket negotiates one versioned subprotocol and takes its credential as a second, prefixed one, since a browser cannot set WebSocket headers. Upgrader admits any Origin because each route checks Origins first.
Index ¶
- Constants
- Variables
- func AnswerAsPrincipal[T any](status int, produce func(Principal, *http.Request) (T, error)) http.HandlerFunc
- func BoundedClient(client *http.Client, fallback time.Duration) *http.Client
- func CreatedAsPrincipal[T any](location func(T) string, produce func(Principal, *http.Request) (T, error)) http.HandlerFunc
- func DecodeBase64URL(encoded string) ([]byte, bool)
- func DecodeJSON(request *http.Request, target any) error
- func DecodeStrict(r io.Reader, target any) error
- func Do(client *http.Client, request *http.Request, limit int64) ([]byte, int, error)
- func EnsurePrivateDir(dir string) error
- func GetJSON(ctx context.Context, client *http.Client, endpoint, authorization string, ...) error
- func GuardedClient(client *http.Client, fallback time.Duration) *http.Client
- func New(code, message string, status int) error
- func NewRequest(ctx context.Context, method, endpoint, authorization string, body io.Reader) (*http.Request, error)
- func NoContentAsPrincipal(act func(Principal, *http.Request) error) http.HandlerFunc
- func OpenLockFile(path string) (*os.File, error)
- func OwnedPrivate(info os.FileInfo) bool
- func PostForm(parent context.Context, client *http.Client, endpoint string, form url.Values, ...) ([]byte, int, error)
- func PrincipalDirName(principal Principal) string
- func PrivateDir(path string) error
- func ReadPrivateFile(path string, limit int64) ([]byte, error)
- func Redact(operation string, err error, secrets ...string) error
- func RemoveFile(path string) error
- func ReplaceFile(path string, data []byte) error
- func SafeName(value string, max int) bool
- func SameOriginRedirect(from, to *url.URL) bool
- func SubprotocolCredential(protocols []string, prefix string) (string, bool)
- func SyncDir(path string) error
- func TruncateUTF8(value string, limit int) string
- func ValidCredential(value string) bool
- func ValidIdentityValue(value string) bool
- func WithFileLock(path string, fn func() error) error
- func WithPrincipal(ctx context.Context, principal Principal) context.Context
- func WriteError(writer http.ResponseWriter, err error)
- func WriteJSON(writer http.ResponseWriter, status int, value any)
- func WriteJSONBytes(writer http.ResponseWriter, status int, body []byte)
- type APIError
- type Envelope
- type Origins
- type Principal
- type SecretBox
Constants ¶
const (
MaxCredentialBytes = 16 << 10
)
const WebSocketProtocol = "cybershuttle.v1"
Variables ¶
var ErrOriginNotAllowed = New("origin_not_allowed", "origin is not allowed", http.StatusForbidden)
var Upgrader = websocket.Upgrader{Subprotocols: []string{WebSocketProtocol}, CheckOrigin: func(*http.Request) bool { return true }}
var UserAgent = "cs-plane"
Functions ¶
func AnswerAsPrincipal ¶
func AnswerAsPrincipal[T any](status int, produce func(Principal, *http.Request) (T, error)) http.HandlerFunc
AnswerAsPrincipal, CreatedAsPrincipal and NoContentAsPrincipal are the three handler shapes every subsystem uses: a JSON answer, a 201 with Location, and a bodiless 204.
func CreatedAsPrincipal ¶
func DecodeBase64URL ¶
func EnsurePrivateDir ¶
func NewRequest ¶
func NoContentAsPrincipal ¶
func OpenLockFile ¶
OpenLockFile opens or creates a private lock file without following links; the caller flocks its descriptor.
func OwnedPrivate ¶
OwnedPrivate reports whether info describes an entry this user owns with no group or world permission bits.
func PrincipalDirName ¶
func PrivateDir ¶
func RemoveFile ¶
func ReplaceFile ¶
func SameOriginRedirect ¶
func SubprotocolCredential ¶ added in v0.4.2
SubprotocolCredential returns the prefixed credential from an offer of exactly it and WebSocketProtocol, any order.
func TruncateUTF8 ¶
func ValidCredential ¶
func ValidIdentityValue ¶
func WithFileLock ¶
func WriteError ¶
func WriteError(writer http.ResponseWriter, err error)
func WriteJSONBytes ¶
func WriteJSONBytes(writer http.ResponseWriter, status int, body []byte)