Documentation
¶
Overview ¶
Package safeguard is the One Identity Safeguard SDK for Go.
It provides a Go-idiomatic client for the Safeguard for Privileged Passwords Web API: authentication, Invoke-style API calls, Application-to-Application (A2A) credential retrieval, and event listeners.
Authentication ¶
Connect establishes an authenticated Client from a Credential. Build a credential with UsernamePassword, Certificate, PKCEHeadless, Token, or Anonymous. PKCEHeadless is the recommended flow for automation because it does not depend on the Resource Owner Grant, which appliances commonly disable. Interactive browser and device-code logins live in the browser and devicecode subpackages so headless consumers keep a lean dependency graph. A client with no credential starts in an anonymous session, sufficient for the Notification service.
Making requests ¶
Client.Invoke issues a call against a Service and returns a Response; Client.Get, Client.Post, Client.Put, and Client.Delete are shorthands. InvokeTyped decodes a JSON response straight into a Go value. Client.Stream, Client.Download, and Client.Upload move large payloads without buffering. Per-request behavior is set with ReqOption values such as WithQueryParam, WithHeader, WithAccept, and WithRequestTimeout.
Transport and authorization ¶
A call's TLS identity and its authorization are chosen independently of the service: a Service only selects the base URL path and never implies which credential authorizes the request. Authorization is owned by the transport, so the reserved Authorization header cannot be set on a request; it is rejected with ErrReservedHeader.
Secrets ¶
Secret holds sensitive bytes -- passwords, tokens, API keys, retrieved credentials -- and makes reading them a deliberate act: it redacts itself in fmt, JSON, and structured logs, so a credential is not disclosed by accident. Obtain the bytes only through Secret.Expose or Secret.ExposeString.
TLS trust ¶
TLS verification is on by default, and appliance hosts must use https (a non-https host is rejected). Trust a privately issued appliance certificate with WithCABundle, which replaces the system trust store for server verification. WithInsecureTLS disables verification and exists only for bootstrapping development appliances; it must never be used in production.
A2A and events ¶
NewA2AContext builds an A2AContext that retrieves credentials over the A2A service using a client certificate and per-account API keys, with no user session. Client.NewEventListener and Client.NewPersistentEventListener (and their A2AContext counterparts) deliver Safeguard events over a SignalR-over-WebSocket connection.
Example ¶
Example shows the canonical flow: build a credential, Connect, make a call, and Close. The client owns an in-memory token that Close releases.
package main
import (
"context"
"fmt"
"log"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
ctx := context.Background()
password := safeguard.NewSecretString("correct horse battery staple")
defer password.Zero()
client, err := safeguard.Connect(ctx, "safeguard.example.com",
safeguard.UsernamePassword("local", "Admin", password))
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
resp, err := client.Get(ctx, safeguard.Core, "Me")
if err != nil {
log.Fatal(err)
}
fmt.Printf("GET Me -> %d\n", resp.StatusCode)
}
Output:
Index ¶
- Constants
- Variables
- func InvokeTyped[T any](ctx context.Context, c *Client, m HTTPMethod, s Service, relURL string, ...) (T, error)
- type A2AContext
- func (a *A2AContext) APIVersion() string
- func (a *A2AContext) BrokerAccessRequest(ctx context.Context, brokerAPIKey Secret, req BrokeredAccessRequest) (*AccessRequest, error)
- func (a *A2AContext) Close() error
- func (a *A2AContext) GetRetrievableAccounts(ctx context.Context, filter string) ([]A2ARetrievableAccount, error)
- func (a *A2AContext) Host() string
- func (a *A2AContext) NewEventListener(apiKey Secret) *EventListener
- func (a *A2AContext) NewPersistentEventListener(apiKey Secret) *PersistentEventListener
- func (a *A2AContext) RetrieveAPIKey(ctx context.Context, apiKey Secret) ([]APIKey, error)
- func (a *A2AContext) RetrievePassword(ctx context.Context, apiKey Secret) (Secret, error)
- func (a *A2AContext) RetrievePrivateKey(ctx context.Context, apiKey Secret, format KeyFormat) (Secret, error)
- func (a *A2AContext) SetPassword(ctx context.Context, apiKey Secret, newPassword Secret) error
- func (a *A2AContext) SetPrivateKey(ctx context.Context, apiKey Secret, privateKeyPEM Secret, passphrase Secret, ...) error
- type A2AOption
- type A2ARetrievableAccount
- type APIError
- type APIKey
- type AccessRequest
- type AccessRequestType
- type AuthenticationError
- type AuthorizationError
- type BrokeredAccessRequest
- type CertOption
- type Client
- func (c *Client) APIVersion() string
- func (c *Client) Close() error
- func (c *Client) Delete(ctx context.Context, s Service, relURL string, opts ...ReqOption) (Response, error)
- func (c *Client) Download(ctx context.Context, s Service, relURL string, w io.Writer, opts ...ReqOption) (Response, error)
- func (c *Client) Get(ctx context.Context, s Service, relURL string, opts ...ReqOption) (Response, error)
- func (c *Client) Host() string
- func (c *Client) Invoke(ctx context.Context, m HTTPMethod, s Service, relURL string, body any, ...) (Response, error)
- func (c *Client) Logout(ctx context.Context) error
- func (c *Client) NewEventListener() *EventListener
- func (c *Client) NewPersistentEventListener() *PersistentEventListener
- func (c *Client) Post(ctx context.Context, s Service, relURL string, body any, opts ...ReqOption) (Response, error)
- func (c *Client) Put(ctx context.Context, s Service, relURL string, body any, opts ...ReqOption) (Response, error)
- func (c *Client) RefreshToken(ctx context.Context) error
- func (c *Client) Stream(ctx context.Context, m HTTPMethod, s Service, relURL string, body any, ...) (io.ReadCloser, Response, error)
- func (c *Client) TokenLifetimeRemaining(ctx context.Context) (time.Duration, error)
- func (c *Client) Upload(ctx context.Context, s Service, relURL string, r io.Reader, opts ...ReqOption) (Response, error)
- type Credential
- func Anonymous() Credential
- func AuthorizedSession(login LoginFunc) Credential
- func Certificate(certPEM []byte, password Secret, opts ...CertOption) Credential
- func PKCEHeadless(provider, username string, password Secret, opts ...PKCEOption) Credential
- func Token(userToken Secret) Credential
- func UsernamePassword(provider, username string, password Secret) Credential
- type EventHandlerFunc
- type EventListener
- type HTTPMethod
- type KeyFormat
- type LoginFunc
- type LoginTransport
- type NotFoundError
- type Option
- type PKCEOption
- type PersistentEventListener
- type ReqOption
- func WithAPIVersionOverride(version string) ReqOption
- func WithAccept(mediaType string) ReqOption
- func WithHeader(key, value string) ReqOption
- func WithHost(host string) ReqOption
- func WithQueryParam(key, value string) ReqOption
- func WithQueryParams(params map[string]string) ReqOption
- func WithRequestTimeout(d time.Duration) ReqOption
- type Response
- type SecondaryFactorFunc
- type Secret
- func (s *Secret) Close() error
- func (s Secret) Equal(other Secret) bool
- func (s Secret) Expose() []byte
- func (s Secret) ExposeString() string
- func (s Secret) Format(f fmt.State, _ rune)
- func (s Secret) GoString() string
- func (s Secret) IsZero() bool
- func (s Secret) Len() int
- func (s Secret) LogValue() slog.Value
- func (s Secret) MarshalJSON() ([]byte, error)
- func (s Secret) MarshalText() ([]byte, error)
- func (s Secret) String() string
- func (s *Secret) Zero()
- type ServerCertValidator
- type Service
- type Timeouts
- type TransportError
Examples ¶
Constants ¶
const DefaultAPIVersion = "v4"
DefaultAPIVersion is the Safeguard API version used when a caller does not override it. Safeguard's current default major version is v4.
Variables ¶
var ( // ErrNotAuthenticated indicates an operation requires a session that has not // been established (or has been ended by Logout/Close). ErrNotAuthenticated = errors.New("safeguard: not authenticated") // ErrNotRefreshable indicates the current credential cannot be refreshed (for // example a bare user token supplied via Token). ErrNotRefreshable = errors.New("safeguard: token is not refreshable") // ErrReservedHeader indicates a request option attempted to set a reserved // header (Authorization) that the transport controls exclusively. ErrReservedHeader = errors.New("safeguard: Authorization is a reserved header and cannot be set on a request") // ErrClosed indicates the client has been closed and can no longer be used. ErrClosed = errors.New("safeguard: client is closed") // ErrAlreadyStarted indicates an event listener's Start was called more than // once. Create a new listener instead of restarting a stopped one. ErrAlreadyStarted = errors.New("safeguard: event listener already started") )
Sentinel errors returned by the SDK. Compare with errors.Is.
var ErrSecondaryFactorFailed = auth.ErrSecondaryFactorFailed
ErrSecondaryFactorFailed indicates the appliance rejected the supplied secondary (multi-factor) authentication code. Compare with errors.Is.
var ErrSecondaryFactorRequired = auth.ErrSecondaryFactorRequired
ErrSecondaryFactorRequired indicates a PKCE headless login reached a secondary (multi-factor) authentication step but no secondary factor provider was supplied. Provide WithSecondaryFactor. Compare with errors.Is.
Functions ¶
func InvokeTyped ¶
func InvokeTyped[T any](ctx context.Context, c *Client, m HTTPMethod, s Service, relURL string, body any, opts ...ReqOption) (T, error)
InvokeTyped performs an Invoke and JSON-decodes a successful response body into a value of type T. An empty body yields the zero value of T with no error.
Example ¶
ExampleInvokeTyped decodes a successful JSON response directly into a Go value.
package main
import (
"context"
"fmt"
"log"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
var client *safeguard.Client // obtained from safeguard.Connect
ctx := context.Background()
type me struct {
UserName string `json:"UserName"`
DisplayName string `json:"DisplayName"`
}
info, err := safeguard.InvokeTyped[me](ctx, client, safeguard.MethodGet, safeguard.Core, "Me", nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("signed in as %s (%s)\n", info.UserName, info.DisplayName)
}
Output:
Types ¶
type A2AContext ¶
type A2AContext struct {
// contains filtered or unexported fields
}
A2AContext retrieves credentials with the Safeguard Application-to-Application (A2A) service. Unlike Client, it is not a token session: it authenticates with a client certificate over mutual TLS on every call, and each retrieval is authorized by a per-account API key passed as an argument. The API key identifies which registered account the call targets; the certificate proves the calling application's identity.
A2AContext is created with NewA2AContext, which parses the certificate in memory without contacting the appliance; networked and certificate-dependent state is validated on the first retrieval. It is safe for concurrent use, and its transports must be released with Close.
func NewA2AContext ¶
func NewA2AContext(host string, certPEM []byte, password Secret, opts ...A2AOption) (*A2AContext, error)
NewA2AContext builds an A2AContext for host that authenticates with the client certificate in certPEM over mutual TLS. certPEM is concatenated PEM carrying the leaf certificate, any intermediate chain, and the private key; supply the key separately with WithA2APrivateKeyPEM when it lives in its own PEM input. password decrypts an encrypted PEM private key, whether it uses the modern encrypted PKCS#8 (PBES2) format or the legacy DEK-Info format. Like the Certificate credential, only PEM material is accepted: PKCS#12 (.pfx/.p12) input is rejected with a clear error, so convert it first (for example, `openssl pkcs12 -in cert.pfx -nodes -out cert.pem`).
The certificate is parsed and validated here, so a bad certificate or password surfaces immediately; no network call is made until the first retrieval.
func (*A2AContext) APIVersion ¶
func (a *A2AContext) APIVersion() string
APIVersion returns the default API version the context uses.
func (*A2AContext) BrokerAccessRequest ¶
func (a *A2AContext) BrokerAccessRequest(ctx context.Context, brokerAPIKey Secret, req BrokeredAccessRequest) (*AccessRequest, error)
BrokerAccessRequest creates an access request on behalf of another user over the A2A service, authorized by brokerAPIKey -- the API key of the registration's access request broker, which is distinct from an account's retrieval API key. The registration must list the context's certificate user among its broker users, and an access policy must grant the requested user access to the target, or the appliance rejects the call. The returned AccessRequest reports the request's identifier and state.
func (*A2AContext) Close ¶
func (a *A2AContext) Close() error
Close is terminal: it releases the transport pools. After Close the context cannot be used. Close is idempotent.
func (*A2AContext) GetRetrievableAccounts ¶
func (a *A2AContext) GetRetrievableAccounts(ctx context.Context, filter string) ([]A2ARetrievableAccount, error)
GetRetrievableAccounts lists the accounts the context's client certificate is registered to retrieve credentials for, across every A2A registration bound to the certificate. Unlike the Retrieve methods it is authorized by the client certificate alone -- no per-account API key -- because it enumerates all registrations for the certificate user. The optional filter is an OData filter expression applied to each registration's accounts; pass "" for no filter.
The returned entries carry the per-account APIKey, so a caller can discover an account here and pass its APIKey straight to RetrievePassword, RetrievePrivateKey, or RetrieveAPIKey. Because an entry does not record which credential type it was registered for, a caller that registered an account for more than one type must track that mapping itself.
Example ¶
ExampleA2AContext_GetRetrievableAccounts discovers every account the context's client certificate is registered to retrieve, along with the API key that authorizes each retrieval.
package main
import (
"context"
"fmt"
"log"
"os"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
ctx := context.Background()
certPEM, err := os.ReadFile("a2a-client.pem")
if err != nil {
log.Fatal(err)
}
a2a, err := safeguard.NewA2AContext("safeguard.example.com", certPEM, safeguard.Secret{})
if err != nil {
log.Fatal(err)
}
defer func() { _ = a2a.Close() }()
accounts, err := a2a.GetRetrievableAccounts(ctx, "")
if err != nil {
log.Fatal(err)
}
for _, acct := range accounts {
fmt.Printf("%s on %s\n", acct.AccountName, acct.AssetName)
// acct.APIKey can be passed straight to RetrievePassword and friends.
}
}
Output:
func (*A2AContext) Host ¶
func (a *A2AContext) Host() string
Host returns the appliance host the context is bound to.
func (*A2AContext) NewEventListener ¶
func (a *A2AContext) NewEventListener(apiKey Secret) *EventListener
NewEventListener returns a single-connection listener for A2A credential events authorized by apiKey. Events are delivered for the account the API key retrieves. Register handlers, then call Start.
func (*A2AContext) NewPersistentEventListener ¶
func (a *A2AContext) NewPersistentEventListener(apiKey Secret) *PersistentEventListener
NewPersistentEventListener returns a reconnecting listener for A2A credential events authorized by apiKey. An A2A API key does not expire, so the listener simply reconnects after a dropped connection until Stop.
func (*A2AContext) RetrieveAPIKey ¶
RetrieveAPIKey retrieves the API key credentials of the account identified by apiKey. The appliance returns one entry per configured API key.
func (*A2AContext) RetrievePassword ¶
RetrievePassword retrieves the password of the account identified by apiKey. The returned Secret holds the password; it is empty when the account has no stored password.
Example ¶
ExampleA2AContext_RetrievePassword retrieves an account password over the Application-to-Application service. An A2AContext authenticates with a client certificate on every call; the per-account API key selects which registered account the call targets.
package main
import (
"context"
"fmt"
"log"
"os"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
ctx := context.Background()
certPEM, err := os.ReadFile("a2a-client.pem")
if err != nil {
log.Fatal(err)
}
a2a, err := safeguard.NewA2AContext("safeguard.example.com", certPEM, safeguard.Secret{})
if err != nil {
log.Fatal(err)
}
defer func() { _ = a2a.Close() }()
apiKey := safeguard.NewSecretString(os.Getenv("A2A_API_KEY"))
defer apiKey.Zero()
password, err := a2a.RetrievePassword(ctx, apiKey)
if err != nil {
log.Fatal(err)
}
defer password.Zero()
// Use password.Expose()/ExposeString() where the plaintext is required; do
// not log it. Here we only report that a value was retrieved.
fmt.Printf("retrieved a %d-byte password\n", password.Len())
}
Output:
func (*A2AContext) RetrievePrivateKey ¶
func (a *A2AContext) RetrievePrivateKey(ctx context.Context, apiKey Secret, format KeyFormat) (Secret, error)
RetrievePrivateKey retrieves the SSH private key of the account identified by apiKey, encoded in the requested KeyFormat. An empty format selects KeyFormatOpenSSH. The returned Secret holds the private key.
func (*A2AContext) SetPassword ¶
SetPassword stores newPassword as the password of the account identified by apiKey, writing it back to Safeguard over the A2A service. The account's A2A registration must have bidirectional (write-back) access enabled, or the appliance rejects the call.
func (*A2AContext) SetPrivateKey ¶
func (a *A2AContext) SetPrivateKey(ctx context.Context, apiKey Secret, privateKeyPEM Secret, passphrase Secret, format KeyFormat) error
SetPrivateKey stores privateKeyPEM as the SSH private key of the account identified by apiKey. privateKeyPEM is the private key in PEM form and format declares its encoding; an empty format selects KeyFormatOpenSSH. passphrase decrypts an encrypted key and may be an empty Secret for an unencrypted key. Like SetPassword, the account's registration must have bidirectional access enabled.
type A2AOption ¶
type A2AOption func(*a2aConfig) error
A2AOption configures an A2AContext. Options are applied in order and may return an error to reject an invalid configuration.
func WithA2AConnectionOptions ¶
WithA2AConnectionOptions applies standard connection Options (TLS trust, API version, timeouts, logger) to the A2A context. It reuses the same Option set as Connect so A2A callers configure their connection exactly like everyone else, for example WithCABundle, WithInsecureTLS, or WithAPIVersion.
func WithA2APrivateKeyPEM ¶
WithA2APrivateKeyPEM supplies the client certificate's private key as a separate PEM input when it is not concatenated with the certificate in the primary NewA2AContext argument, mirroring Certificate's WithPrivateKeyPEM.
type A2ARetrievableAccount ¶
type A2ARetrievableAccount struct {
// ApplicationName is the AppName of the registration this account belongs to.
ApplicationName string
// Description is the registration's description.
Description string
// Disabled reports whether the account or its registration is disabled.
Disabled bool
// APIKey is the A2A API key that authorizes retrieval of this account's
// credential. Pass it to a Retrieve method.
APIKey Secret
// AssetID is the object ID of the account's asset.
AssetID int
// AssetName is the name of the account's asset.
AssetName string
// AssetNetworkAddress is the network address of the account's asset.
AssetNetworkAddress string
// AssetDescription is the description of the account's asset.
AssetDescription string
// AccountID is the account's object ID.
AccountID int
// AccountName is the account's name.
AccountName string
// DomainName is the account's domain, when it is a directory account.
DomainName string
// AccountType identifies the kind of account.
AccountType string
// AccountDescription is the account's description.
AccountDescription string
}
A2ARetrievableAccount describes one account that the context's client certificate is registered to retrieve credentials for. The APIKey authorizes retrieval of that account's credential and is wrapped in a Secret; the surrounding metadata is not sensitive. The same account may appear more than once -- once per credential type it is registered for -- each with a distinct APIKey; the response does not record which credential type an entry is for.
type APIError ¶
type APIError struct {
// StatusCode is the HTTP status code returned by the appliance.
StatusCode int
// Code is the Safeguard error code from the response body, or 0 if absent.
Code int
// Message is the Safeguard error message from the response body, if any.
Message string
// RequestID is the appliance request/correlation id, or empty if absent.
RequestID string
// contains filtered or unexported fields
}
APIError is returned when a Safeguard API call completes with a non-2xx HTTP status. It carries the HTTP status, the Safeguard error Code and Message when the body was a recognizable Safeguard error object, and the appliance request identifier when present.
Error never includes the raw response body, which may contain returned credentials; use RawBody for a bounded, explicit diagnostic copy.
type APIKey ¶
type APIKey struct {
// ID is the API key's object identifier.
ID int
// Name is the API key's name.
Name string
// Description is the API key's description.
Description string
// ClientID is the OAuth client identifier.
ClientID string
// ClientSecret is the OAuth client secret. It is empty when the appliance
// does not return the secret value for this key.
ClientSecret Secret
// ClientSecretID identifies the client secret.
ClientSecretID string
}
APIKey is one API key credential retrieved for a registered account. The ClientSecret is wrapped in a Secret; the surrounding metadata is not sensitive.
type AccessRequest ¶
type AccessRequest struct {
// ID is the access request's identifier.
ID string
// State is the request's workflow state, for example "RequestAvailable" once
// an auto-approved request is ready or "PendingApproval" when it awaits an
// approver.
State string
// AccessRequestType is the kind of access that was requested.
AccessRequestType AccessRequestType
// AccountID is the target account's object ID.
AccountID int
// AccountName is the target account's name.
AccountName string
// AssetID is the target asset's object ID.
AssetID int
// AssetName is the target asset's name.
AssetName string
// Raw is the unmodified JSON body the appliance returned.
Raw json.RawMessage
}
AccessRequest is the access request the broker created. It exposes the fields callers most commonly need; Raw holds the complete appliance response for anything not modeled here.
type AccessRequestType ¶
type AccessRequestType string
AccessRequestType identifies the kind of access a brokered access request asks for. The values are the names the Safeguard API uses on the wire.
const ( // AccessRequestPassword requests release of an account password. AccessRequestPassword AccessRequestType = "Password" // AccessRequestSSHKey requests release of an account SSH key. AccessRequestSSHKey AccessRequestType = "SshKey" // AccessRequestSSH requests an SSH session. AccessRequestSSH AccessRequestType = "Ssh" // AccessRequestRemoteDesktop requests a remote desktop (RDP) session. AccessRequestRemoteDesktop AccessRequestType = "RemoteDesktop" // AccessRequestRemoteDesktopApplication requests a remote desktop application session. AccessRequestRemoteDesktopApplication AccessRequestType = "RemoteDesktopApplication" // AccessRequestTelnet requests a Telnet session. AccessRequestTelnet AccessRequestType = "Telnet" // AccessRequestAPIKey requests release of an account API key. AccessRequestAPIKey AccessRequestType = "ApiKey" // AccessRequestFile requests release of a stored file. AccessRequestFile AccessRequestType = "File" )
type AuthenticationError ¶
type AuthenticationError struct{ *APIError }
AuthenticationError is an APIError for an HTTP 401 response. Detect it with errors.As; errors.As(&APIError) also matches via Unwrap.
func (*AuthenticationError) Unwrap ¶
func (e *AuthenticationError) Unwrap() error
Unwrap returns the embedded *APIError.
type AuthorizationError ¶
type AuthorizationError struct{ *APIError }
AuthorizationError is an APIError for an HTTP 403 response.
func (*AuthorizationError) Unwrap ¶
func (e *AuthorizationError) Unwrap() error
Unwrap returns the embedded *APIError.
type BrokeredAccessRequest ¶
type BrokeredAccessRequest struct {
// AccessRequestType is the kind of access requested; it is required.
AccessRequestType AccessRequestType
// ForUserID identifies the user the request is made for by object ID. It
// takes precedence over ForUser.
ForUserID int
// ForUser identifies the user the request is made for by name. It is ignored
// when ForUserID is set.
ForUser string
// ForProvider names the identity provider that resolves ForUser, for example
// "local"; it is ignored when ForUserID is set.
ForProvider string
// AssetID identifies the target asset by object ID. It takes precedence over
// AssetName.
AssetID int
// AssetName identifies the target asset by name. It is ignored when AssetID
// is set.
AssetName string
// AccountID identifies the target account by object ID. It takes precedence
// over AccountName.
AccountID int
// AccountName identifies the target account by name; omit it to request
// access to the asset itself. It is ignored when AccountID is set.
AccountName string
// AccountDomainName disambiguates AccountName when the account is directory
// managed. It is ignored when AccountID is set.
AccountDomainName string
// IsEmergency marks the request as an emergency access request.
IsEmergency bool
// ReasonCodeID selects a predefined reason code by object ID.
ReasonCodeID int
// ReasonCode selects a predefined reason code by name; it is ignored when
// ReasonCodeID is set.
ReasonCode string
// ReasonComment is a free-text justification for the request.
ReasonComment string
// TicketNumber associates the request with an external ticket.
TicketNumber string
// RequestedFor is when the access should begin; a zero time requests access
// immediately.
RequestedFor time.Time
// RequestedDurationDays, RequestedDurationHours, and RequestedDurationMinutes
// set how long the access should last; leave them zero for the policy
// default.
RequestedDurationDays int
RequestedDurationHours int
RequestedDurationMinutes int
}
BrokeredAccessRequest describes an access request the broker creates on behalf of another user. Identify the account by AccountID (or AccountName, optionally with AccountDomainName) and the asset by AssetID (or AssetName). Identify the user the request is for by ForUserID, or by ForUser with an optional ForProvider to disambiguate the identity provider. Zero-valued fields are omitted from the request.
type CertOption ¶
type CertOption func(*certConfig) error
CertOption configures certificate login.
func WithCertificateProvider ¶
func WithCertificateProvider(provider string) CertOption
WithCertificateProvider overrides the authentication provider used for certificate login. The default is the built-in certificate provider.
func WithPrivateKeyPEM ¶
func WithPrivateKeyPEM(keyPEM []byte) CertOption
WithPrivateKeyPEM supplies the private key as a separate PEM input when it is not concatenated with the certificate in the primary Certificate argument.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a concurrency-safe Safeguard API client. It is safe for use by multiple goroutines: token state is immutable and swapped atomically, and the transport pools are internally synchronized.
A freshly built client starts in an anonymous session, which is sufficient for the Notification service and other anonymous endpoints; Connect establishes an authenticated session from a credential.
func Connect ¶
Connect authenticates to the Safeguard appliance at host using cred and returns a ready client bound to a fresh session. Connection options (TLS trust, API version, timeouts, logger) are applied before authentication runs, so certificate parsing and the login exchange happen under the caller's TLS policy. On any failure Connect releases the client it built and returns the error; on success the returned client owns transports and an in-memory token that must be released with Close.
Connect is the only way to obtain an authenticated client. Reconnecting means calling Connect again for a new client with a new session epoch; there is no in-place reconnect.
Example (Certificate) ¶
ExampleConnect_certificate authenticates with a client certificate over mutual TLS. The PEM material carries the leaf certificate, any chain, and the private key; password decrypts an encrypted key and may be empty otherwise.
package main
import (
"context"
"log"
"os"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
ctx := context.Background()
certPEM, err := os.ReadFile("client.pem")
if err != nil {
log.Fatal(err)
}
keyPassword := safeguard.NewSecretString("key-passphrase")
defer keyPassword.Zero()
client, err := safeguard.Connect(ctx, "safeguard.example.com",
safeguard.Certificate(certPEM, keyPassword))
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
}
Output:
Example (Pkce) ¶
ExampleConnect_pkce authenticates with the PKCE non-interactive ("headless") flow, which does not depend on the Resource Owner Grant and is the recommended flow for automation.
package main
import (
"context"
"log"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
ctx := context.Background()
password := safeguard.NewSecretString("correct horse battery staple")
defer password.Zero()
client, err := safeguard.Connect(ctx, "safeguard.example.com",
safeguard.PKCEHeadless("local", "Admin", password))
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
}
Output:
Example (Token) ¶
ExampleConnect_token reuses an existing Safeguard user token. Such a session is not refreshable, so a 401 is surfaced rather than silently retried.
package main
import (
"context"
"log"
"os"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
ctx := context.Background()
userToken := safeguard.NewSecretString(os.Getenv("SAFEGUARD_TOKEN"))
defer userToken.Zero()
client, err := safeguard.Connect(ctx, "safeguard.example.com",
safeguard.Token(userToken))
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
}
Output:
func (*Client) APIVersion ¶
APIVersion returns the default API version the client uses.
func (*Client) Close ¶
Close is terminal: it releases the transport pools and clears the in-memory session. Close installs a terminal epoch so an in-flight refresh cannot resurrect the session (doRefresh publishes only on the observed epoch, and epoch 0 never matches). The displaced token is released to the garbage collector rather than zeroed in place, because a concurrent request may still be reading its bytes and zeroing an aliased backing array under a reader is a data race. After Close the client cannot be used. Close is idempotent.
func (*Client) Delete ¶
func (c *Client) Delete(ctx context.Context, s Service, relURL string, opts ...ReqOption) (Response, error)
Delete performs a DELETE request.
func (*Client) Download ¶
func (c *Client) Download(ctx context.Context, s Service, relURL string, w io.Writer, opts ...ReqOption) (Response, error)
Download performs a GET and streams the response body to w, returning a Response that carries the status, headers, and request id but a nil Body (the payload went to w). On a non-2xx status the bounded error payload is read into Response.Body and returned with a typed *APIError. The default Accept is application/octet-stream; override it with WithAccept.
Example ¶
ExampleClient_Download streams a response body to an io.Writer without buffering it in memory, which suits large payloads such as backups or reports.
package main
import (
"context"
"log"
"os"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
var client *safeguard.Client // obtained from safeguard.Connect
ctx := context.Background()
f, err := os.Create("backup.sgb")
if err != nil {
log.Fatal(err)
}
defer func() { _ = f.Close() }()
if _, err := client.Download(ctx, safeguard.Appliance, "Backups/current/Download", f); err != nil {
log.Fatal(err)
}
}
Output:
func (*Client) Get ¶
func (c *Client) Get(ctx context.Context, s Service, relURL string, opts ...ReqOption) (Response, error)
Get performs a GET request.
Example ¶
ExampleClient_Get retrieves a resource and reads the raw response body. Get is shorthand for Invoke with the GET method and no request body.
package main
import (
"context"
"fmt"
"log"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
var client *safeguard.Client // obtained from safeguard.Connect
ctx := context.Background()
resp, err := client.Get(ctx, safeguard.Core, "Users",
safeguard.WithQueryParam("filter", "Disabled eq false"))
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d: %s\n", resp.StatusCode, resp.Body)
}
Output:
func (*Client) Invoke ¶
func (c *Client) Invoke(ctx context.Context, m HTTPMethod, s Service, relURL string, body any, opts ...ReqOption) (Response, error)
Invoke performs a Safeguard API call and returns the Response. The body is encoded by type: nil is an empty body; string and json.RawMessage are sent as application/json; []byte and io.Reader are sent as application/octet-stream; any other value is JSON-marshaled. A caller may override the content type with WithHeader. On a non-2xx status Invoke returns the populated Response along with a typed *APIError (or its 401/403/404 specializations). The response body is always read and closed.
func (*Client) Logout ¶
Logout ends this client's session. It makes a best-effort appliance-side Token/Logout call to revoke the user token, then clears the local session and invalidates its epoch so an in-flight refresh cannot resurrect it. A failure of the appliance call is ignored: the local session is cleared regardless. Logout is idempotent and is a no-op for an anonymous session.
func (*Client) NewEventListener ¶
func (c *Client) NewEventListener() *EventListener
NewEventListener returns a single-connection listener for this client's user events. Register handlers, then call Start. The listener stops if the connection ends; use NewPersistentEventListener for automatic reconnect.
func (*Client) NewPersistentEventListener ¶
func (c *Client) NewPersistentEventListener() *PersistentEventListener
NewPersistentEventListener returns a reconnecting listener for this client's user events. It stops permanently if the client logs out or re-authenticates as a different identity.
Example ¶
ExampleClient_NewPersistentEventListener subscribes to Safeguard events and keeps the subscription alive across reconnects until the listener is stopped.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
safeguard "github.com/OneIdentity/safeguard-go"
)
func main() {
var client *safeguard.Client // obtained from safeguard.Connect
ctx := context.Background()
listener := client.NewPersistentEventListener()
listener.RegisterEventHandler("AssetAccountPasswordUpdated", func(name string, data json.RawMessage) {
fmt.Printf("event %s: %s\n", name, data)
})
if err := listener.Start(ctx); err != nil {
log.Fatal(err)
}
defer listener.Stop()
// Block until the listener stops (context cancelled, or a terminal error).
<-listener.Done()
if err := listener.Err(); err != nil {
log.Printf("listener stopped: %v", err)
}
}
Output:
func (*Client) Post ¶
func (c *Client) Post(ctx context.Context, s Service, relURL string, body any, opts ...ReqOption) (Response, error)
Post performs a POST request with the given body.
func (*Client) Put ¶
func (c *Client) Put(ctx context.Context, s Service, relURL string, body any, opts ...ReqOption) (Response, error)
Put performs a PUT request with the given body.
func (*Client) RefreshToken ¶
RefreshToken exchanges the current credential for a fresh user token by re-running the full login exchange (single flight: concurrent callers share one refresh). It reports ErrNotAuthenticated for an anonymous or absent session and ErrNotRefreshable when the credential cannot mint a replacement token (a bare user token, or an OAuth authorization-code flow such as PKCE, browser, or device code).
func (*Client) Stream ¶
func (c *Client) Stream(ctx context.Context, m HTTPMethod, s Service, relURL string, body any, opts ...ReqOption) (io.ReadCloser, Response, error)
Stream performs a request and returns the response body as an io.ReadCloser that the caller must Close. The response body is streamed, not buffered. If the appliance answers 401 before the body is delivered, Stream may refresh the token and replay the request once, but only when the request body is replayable (nil, a string, []byte, a json.RawMessage, or a marshaled value); a caller-supplied io.Reader body is consumed on first send and is never replayed. Once Stream returns a reader, that stream itself is never retried. On a non-2xx status Stream closes the body, returns a nil reader, and reports a typed *APIError along with a Response whose Body holds the (bounded) error payload. Unlike Invoke, Stream does not apply WithRequestTimeout; a streaming caller controls cancellation through ctx.
func (*Client) TokenLifetimeRemaining ¶
TokenLifetimeRemaining reports the remaining lifetime of the current user token. The appliance is consulted once via the Core LoginMessage endpoint, whose X-TokenLifetimeRemaining header carries the remaining minutes. It returns ErrNotAuthenticated for an anonymous or absent session, and zero with no error when the lifetime cannot be determined.
func (*Client) Upload ¶
func (c *Client) Upload(ctx context.Context, s Service, relURL string, r io.Reader, opts ...ReqOption) (Response, error)
Upload sends the contents of r as an application/octet-stream POST body without buffering it, and returns the (fully read) response. The content type may be overridden with WithHeader.
type Credential ¶
type Credential interface {
// contains filtered or unexported methods
}
Credential is a sealed Safeguard authentication strategy produced by one of this package's credential constructors: UsernamePassword, Certificate, PKCEHeadless, Token, Anonymous, or AuthorizedSession (the seam the browser and devicecode add-on packages use). Its only method is unexported, so the set of credentials is closed and callers cannot implement their own. Pass a Credential to Connect.
func Anonymous ¶
func Anonymous() Credential
Anonymous returns a credential that establishes a session carrying no user token, sufficient for the Notification service and other anonymous endpoints.
func AuthorizedSession ¶
func AuthorizedSession(login LoginFunc) Credential
AuthorizedSession returns a credential that completes an interactive OAuth login by running login and adopting the Safeguard user token it produces. It is the seam the optional browser and devicecode add-on packages use to hand a finished authorization back to Connect: because Credential is sealed, an add-on cannot implement its own credential, so it supplies its interactive flow as a LoginFunc and lets this credential install the result.
The resulting session is intentionally not refreshable, matching SafeguardDotNet and PySafeguard, which treat browser and device-code logins as existing-token connections that cannot silently re-authenticate: RefreshToken reports ErrNotRefreshable and a 401 is surfaced rather than retried. To obtain a new token, run the interactive flow again. For a bare, caller-supplied user token with no interactive step, use Token instead.
func Certificate ¶
func Certificate(certPEM []byte, password Secret, opts ...CertOption) Credential
Certificate returns a credential that authenticates with a client certificate over mutual TLS. certPEM is a concatenated PEM byte slice carrying the leaf certificate, any intermediate chain, and the private key; supply the key separately with WithPrivateKeyPEM when it lives in its own PEM input. password decrypts an encrypted PEM private key, whether it uses the modern encrypted PKCS#8 (PBES2) format that current OpenSSL produces by default or the legacy DEK-Info format. Like PySafeguard, this SDK accepts PEM material only: PKCS#12 (.pfx/.p12) input is rejected with a clear error, so convert it first (for example, `openssl pkcs12 -in cert.pfx -nodes -out cert.pem`). The certificate material is parsed and validated at Connect time, so a bad certificate or password surfaces as a Connect error rather than a panic. The resulting session is refreshable.
func PKCEHeadless ¶
func PKCEHeadless(provider, username string, password Secret, opts ...PKCEOption) Credential
PKCEHeadless returns a credential that authenticates with the PKCE non-interactive ("headless") OAuth flow: the SDK drives the appliance's RSTS form controller directly, with no browser. An empty provider selects the default local provider; a non-default provider may be given as its display name, its RSTS provider id, or a unique substring of that id, and is resolved against the appliance's authentication providers the same way SafeguardDotNet and safeguard-ps resolve it. The resulting session is intentionally not refreshable, matching the reference SDKs' treatment of OAuth authorization-code flows, so RefreshToken reports ErrNotRefreshable and a 401 is surfaced rather than silently retried. Supply WithSecondaryFactor to satisfy multi-factor authentication. The password is copied into the credential; the caller retains ownership of the supplied Secret.
PKCEHeadless is the recommended flow for test automation because it does not depend on the Resource Owner Grant, which appliances commonly disable.
func Token ¶
func Token(userToken Secret) Credential
Token returns a credential that uses an existing Safeguard user token directly, skipping the RSTS/LoginResponse exchange. Such a session is intentionally not refreshable: the SDK has no credential with which to mint a replacement, so RefreshToken reports ErrNotRefreshable. The token is copied into the credential.
func UsernamePassword ¶
func UsernamePassword(provider, username string, password Secret) Credential
UsernamePassword returns a credential that authenticates with the Resource Owner Grant (username and password). An empty provider selects the default local provider. The resulting session is refreshable. The password is copied into the credential; the caller retains ownership of the supplied Secret.
type EventHandlerFunc ¶
type EventHandlerFunc func(name string, data json.RawMessage)
EventHandlerFunc receives an event it was registered for: name is the resolved event name (with the numeric-name workaround applied) and data is the raw JSON payload for the caller to decode. Handlers run on a dispatcher goroutine off the read loop; a panic in a handler is recovered and does not stop the listener. A handler should not block indefinitely, as a slow handler applies backpressure to later events.
type EventListener ¶
type EventListener struct {
// contains filtered or unexported fields
}
EventListener is a single-connection Safeguard event listener. It negotiates a SignalR-over-WebSocket connection, dispatches events to registered handlers, and stops when the connection ends. Register handlers before calling Start. EventListener is safe for concurrent handler registration before Start; use PersistentEventListener when automatic reconnect is required.
func (*EventListener) Done ¶
func (l *EventListener) Done() <-chan struct{}
Done returns a channel closed when the listener has stopped, whether from Stop, a cancelled context, or a connection error. It returns nil before Start.
func (*EventListener) Err ¶
func (l *EventListener) Err() error
Err returns the error that stopped the listener, or nil if it stopped cleanly or is still running.
func (*EventListener) RegisterEventHandler ¶
func (l *EventListener) RegisterEventHandler(name string, h EventHandlerFunc)
RegisterEventHandler registers h to receive events named name. Registering the same name more than once adds an additional handler. Matching is case-insensitive.
func (*EventListener) Start ¶
func (l *EventListener) Start(ctx context.Context) error
Start connects the listener and begins dispatching events. It performs the negotiate and handshake synchronously, returning an error if the connection cannot be established, then services the stream on a background goroutine. The listener stops when the connection ends; Done reports that, and Err reports the cause. ctx bounds the whole listener lifetime, not just the connect.
func (*EventListener) Stop ¶
func (l *EventListener) Stop()
Stop ends the listener and waits for it to stop. It cancels the listener, which tears down the connection and closes Done. It is safe to call more than once, before Start returns, and from within an event handler (a handler that calls Stop does not deadlock: the read loop that closes Done runs on a separate goroutine and is not blocked by the handler).
type HTTPMethod ¶
type HTTPMethod string
HTTPMethod is an HTTP verb used with the Invoke surface.
const ( // MethodGet is the HTTP GET method. MethodGet HTTPMethod = http.MethodGet // MethodPost is the HTTP POST method. MethodPost HTTPMethod = http.MethodPost // MethodPut is the HTTP PUT method. MethodPut HTTPMethod = http.MethodPut // MethodDelete is the HTTP DELETE method. MethodDelete HTTPMethod = http.MethodDelete )
Supported HTTP methods.
type KeyFormat ¶
type KeyFormat string
KeyFormat selects the encoding of an SSH private key returned by RetrievePrivateKey. The values match the Safeguard API's PascalCase names.
const ( // KeyFormatOpenSSH requests the OpenSSH private-key format. It is the default // when RetrievePrivateKey is given an empty KeyFormat. KeyFormatOpenSSH KeyFormat = "OpenSsh" // KeyFormatSSH2 requests the SSH2 (RFC 4716) private-key format. KeyFormatSSH2 KeyFormat = "Ssh2" // KeyFormatPuTTY requests the PuTTY private-key format. KeyFormatPuTTY KeyFormat = "Putty" )
type LoginFunc ¶
type LoginFunc func(ctx context.Context, t LoginTransport) (userToken Secret, err error)
LoginFunc runs an interactive OAuth login against the appliance using t and returns the resulting Safeguard user token. It is implemented by the browser and devicecode add-on packages and handed to AuthorizedSession; end users call those packages' Connect functions rather than writing a LoginFunc themselves. The returned Secret is owned by the SDK after a nil error; the login must not retain or zero it.
type LoginTransport ¶
type LoginTransport interface {
// Do issues an HTTP request on the client's server-trust transport.
Do(req *http.Request) (*http.Response, error)
// Host is the appliance host the client is bound to.
Host() string
// APIVersion is the default Core API version segment, for example "v4".
APIVersion() string
}
LoginTransport is the appliance connection handed to an interactive add-on login flow (the browser and devicecode packages). It carries the client's server-trust HTTP transport, which already honors the connection's TLS policy and timeouts, together with the appliance coordinates a flow needs to build RSTS and Core URLs. Its Do method satisfies the internal broker's transport interface, so an add-on can pass a LoginTransport straight into the internal auth helpers without building its own transport.
type NotFoundError ¶
type NotFoundError struct{ *APIError }
NotFoundError is an APIError for an HTTP 404 response.
func (*NotFoundError) Unwrap ¶
func (e *NotFoundError) Unwrap() error
Unwrap returns the embedded *APIError.
type Option ¶
type Option func(*clientConfig) error
Option configures a client connection. Options are applied in order and may return an error to reject an invalid configuration.
func WithAPIVersion ¶
WithAPIVersion overrides the default Safeguard API version for every request.
func WithCABundle ¶
WithCABundle trusts the certificates in pemBytes (an internal PKI/appliance CA) for server verification, replacing the system trust store rather than adding to it: once a bundle is set, only the certificates it contains are trusted to verify the appliance's certificate chain. It is the secure way to trust a self-signed or privately issued appliance certificate.
func WithHTTPTimeouts ¶
WithHTTPTimeouts overrides the granular transport timeouts. Zero-valued fields keep their defaults.
func WithInsecureTLS ¶
func WithInsecureTLS() Option
WithInsecureTLS disables TLS chain and hostname verification on every transport, including the event WebSocket. It exists only for bootstrapping self-signed appliances in development and test; it is loud and dangerous and must never be used in production. It cannot be combined with WithServerCertValidator, and it never enables HTTP redirect following.
func WithLogger ¶
WithLogger attaches a structured logger. Nothing is logged by default and no global logger is used; secrets never appear in logs.
func WithServerCertValidator ¶
func WithServerCertValidator(v ServerCertValidator) Option
WithServerCertValidator adds an additive validation callback that runs after normal chain and hostname verification. It cannot be combined with WithInsecureTLS.
type PKCEOption ¶
type PKCEOption func(*pkceConfig) error
PKCEOption configures a PKCE headless login.
func WithSecondaryFactor ¶
func WithSecondaryFactor(fn SecondaryFactorFunc) PKCEOption
WithSecondaryFactor supplies the callback used to satisfy multi-factor authentication during a PKCE headless login. Without it, a login that reaches a secondary factor fails with ErrSecondaryFactorRequired.
type PersistentEventListener ¶
type PersistentEventListener struct {
// contains filtered or unexported fields
}
PersistentEventListener is a Safeguard event listener that reconnects automatically with exponential backoff and jitter, preserving its registered handlers across reconnects. For a user session it stops permanently once the owning session is logged out or replaced, rather than reconnecting under a different identity.
func (*PersistentEventListener) Done ¶
func (l *PersistentEventListener) Done() <-chan struct{}
Done returns a channel closed when the listener has permanently stopped. It returns nil before Start.
func (*PersistentEventListener) Err ¶
func (l *PersistentEventListener) Err() error
Err returns the terminal error that stopped the listener, if any.
func (*PersistentEventListener) RegisterEventHandler ¶
func (l *PersistentEventListener) RegisterEventHandler(name string, h EventHandlerFunc)
RegisterEventHandler registers h to receive events named name. Handlers survive reconnects. Matching is case-insensitive.
func (*PersistentEventListener) Start ¶
func (l *PersistentEventListener) Start(ctx context.Context) error
Start begins the listener's connect-and-reconnect loop on a background goroutine and returns immediately. The loop retries failed connects with backoff and reconnects after a dropped connection, until Stop is called, ctx is cancelled, or the owning session ends. Done reports a terminal stop and Err reports its cause.
func (*PersistentEventListener) Stop ¶
func (l *PersistentEventListener) Stop()
Stop ends the listener and waits for its loop to finish. It is idempotent and is safe to call from within an event handler (a handler that calls Stop does not deadlock).
type ReqOption ¶
type ReqOption func(*requestConfig) error
ReqOption configures a single request. Options are applied in order and may return an error to reject an invalid request configuration.
func WithAPIVersionOverride ¶
WithAPIVersionOverride overrides the API version for this request only.
func WithAccept ¶
WithAccept overrides the Accept header (for example "text/csv" for CSV report endpoints). The default is application/json.
func WithHeader ¶
WithHeader sets an additional request header. Setting the reserved Authorization header is rejected with ErrReservedHeader: authorization is controlled exclusively by the transport axis and must never ride the wrong transport.
func WithHost ¶
WithHost overrides the target host for this request only (parity with PySafeguard host_override). The alternate host uses the same TLS configuration and must use the https scheme (a non-https host is rejected, as Safeguard is https-only).
func WithQueryParam ¶
WithQueryParam adds a single query parameter to the request.
func WithQueryParams ¶
WithQueryParams adds multiple query parameters to the request.
func WithRequestTimeout ¶
WithRequestTimeout applies a timeout to this request by deriving a context deadline. It does not set a global client timeout and is safe for non-stream calls; streaming callers should manage cancellation through their own context.
type Response ¶
type Response struct {
// StatusCode is the HTTP status code of the response.
StatusCode int
// Headers holds the response headers. Multi-value headers are preserved.
Headers http.Header
// Body is the fully buffered response body for non-streaming calls; it is nil
// for streaming responses.
Body []byte
// RequestID is the appliance request/correlation identifier, or empty when the
// response did not include one.
RequestID string
}
Response is the complete result of an Invoke-style call: the HTTP status, the response headers, the fully read (non-streaming) body, and the appliance request identifier when one is present.
For streaming calls the body is delivered separately and Body is nil; the headers and RequestID still describe the response.
func (Response) BodyString ¶
BodyString returns the response body as a string. It is a convenience for callers that expect a textual (JSON/CSV) body. Response deliberately does not implement fmt.Stringer: bodies can contain retrieved credentials, so exposing the body is an explicit call rather than something an implicit %v/%s format or a log of a Response would trigger.
type SecondaryFactorFunc ¶
SecondaryFactorFunc supplies a multi-factor one-time code given the appliance's prompt. It is invoked only when the primary login step reports that a secondary factor is required. Returning an error aborts the login.
type Secret ¶
type Secret struct {
// contains filtered or unexported fields
}
Secret holds sensitive bytes (passwords, tokens, API keys, retrieved credentials) and makes reading them a deliberate act. Its purpose is to prevent accidental disclosure, not to provide cryptographic or memory-hardened protection; set expectations accordingly.
What it guards against (the common ways an SDK leaks a credential):
- fmt: String, GoString, and Format render "[REDACTED]" for every verb, so %v/%s/%+v/%#v, prints, and error messages never reveal the bytes.
- Serialization: MarshalJSON and MarshalText emit "[REDACTED]", so a Secret embedded in a struct is not written out in cleartext.
- Logging: LogValue (slog.LogValuer) redacts the value in structured logs.
Hygiene it adds: NewSecret copies its input and Expose returns a copy, so no backing array is aliased; Equal compares in constant time; Zero best-effort wipes the buffer to shrink the in-memory plaintext window.
What it deliberately does NOT provide:
- No encryption at rest in memory: the bytes are plaintext on the Go heap while held. It is not a defense against an adversary who can read process memory, a core dump, swap, or an attached debugger.
- Once exposed, derived values cannot be reclaimed: an ExposeString result is an immutable Go string that cannot be zeroed, and a parsed key (for example a tls.Certificate) is out of Secret's control.
- Zero is best-effort only: Go's runtime may copy or move the backing array, so disposal is not a hard guarantee.
Callers obtain the underlying bytes only through the explicit Expose or ExposeString methods. Secret is a value type.
func NewSecret ¶
NewSecret returns a Secret that copies b. The caller retains ownership of b and may zero it after this call without affecting the Secret.
func NewSecretString ¶
NewSecretString returns a Secret containing a copy of the bytes of s.
func (*Secret) Close ¶
Close wipes the Secret and always returns nil; it lets a Secret satisfy io.Closer for use with defer.
func (Secret) Equal ¶
Equal reports whether s and other hold identical bytes. It uses a constant-time comparison (crypto/subtle) so it does not leak, through timing, where two same-length secrets first differ. A length mismatch returns false.
func (Secret) Expose ¶
Expose returns a copy of the secret's bytes. The caller owns the returned slice and may zero it when finished. Returns nil for an empty Secret.
func (Secret) ExposeString ¶
ExposeString returns the secret's bytes as a string copy. Prefer Expose when the caller can zero the bytes afterward; a string cannot be zeroed.
func (Secret) Format ¶
Format implements fmt.Formatter so every verb renders the redaction placeholder instead of the underlying bytes.
func (Secret) LogValue ¶
LogValue implements slog.LogValuer so structured logs render the placeholder.
func (Secret) MarshalJSON ¶
MarshalJSON renders the Secret as the redaction placeholder string so it is never serialized in cleartext.
func (Secret) MarshalText ¶
MarshalText renders the Secret as the redaction placeholder.
type ServerCertValidator ¶
type ServerCertValidator func(leaf *x509.Certificate, verifiedChains [][]*x509.Certificate) error
ServerCertValidator is an additive server-certificate check. It runs only after the normal chain and hostname verification succeeds and can therefore only further restrict trust, never relax it (parity with the .NET RemoteCertificateValidationCallback). Returning a non-nil error fails the handshake. leaf is the server's leaf certificate; verifiedChains are the chains the standard verifier accepted.
type Service ¶
type Service string
Service identifies a Safeguard API service that a request is routed to.
Authorization is never inferred from the Service or the URL; the service only selects the base URL path. See the package overview's "Transport and authorization" section for how TLS identity and authorization are chosen independently of the service.
const ( // Core is the primary Safeguard for Privileged Passwords API service. Core Service = "core" // Appliance is the appliance-management API service. Appliance Service = "appliance" // Notification is the anonymous/notification API service (no token required). Notification Service = "notification" // A2A is the Application-to-Application credential-retrieval service. A2A Service = "a2a" // Event is the SignalR event service. Event Service = "event" // RSTS is the embedded secure token service; it has no version path segment. RSTS Service = "rsts" // Management is the appliance management service. Management Service = "management" )
The Safeguard API services.
type Timeouts ¶
type Timeouts struct {
// Dial bounds establishing the TCP connection.
Dial time.Duration
// TLSHandshake bounds completing the TLS handshake.
TLSHandshake time.Duration
// ResponseHeader bounds waiting for the first response header byte after the
// request is written. It does not bound reading the response body.
ResponseHeader time.Duration
}
Timeouts configures the granular transport timeouts. None of these is a global request deadline: a global timeout would break long-lived streams and event connections. Per-request deadlines are set through context and WithRequestTimeout.
type TransportError ¶
type TransportError struct {
// Op is a short description of the operation that failed (for example "dial"
// or "request").
Op string
// Err is the underlying error.
Err error
}
TransportError wraps a network, TLS, or protocol failure that occurred before a usable HTTP response was received.
func (*TransportError) Unwrap ¶
func (e *TransportError) Unwrap() error
Unwrap returns the underlying error so errors.Is/As reach the cause.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package browser provides an interactive Safeguard login that opens the user's system web browser to complete OAuth authorization (the authorization-code flow with PKCE).
|
Package browser provides an interactive Safeguard login that opens the user's system web browser to complete OAuth authorization (the authorization-code flow with PKCE). |
|
Package devicecode provides an interactive Safeguard login using the OAuth device authorization grant: the appliance issues a short user code and a verification URL, the user visits the URL on any device and enters the code, and the SDK polls until authorization completes.
|
Package devicecode provides an interactive Safeguard login using the OAuth device authorization grant: the appliance issues a short user code and a verification URL, the user visits the URL on any device and enters the code, and the SDK polls until authorization completes. |
|
internal
|
|
|
auth
Package auth is the internal Safeguard authentication broker.
|
Package auth is the internal Safeguard authentication broker. |
|
livetest
Package livetest provides shared helpers for the SDK's live-appliance end-to-end tests.
|
Package livetest provides shared helpers for the SDK's live-appliance end-to-end tests. |
|
samples
|
|
|
a2a-apikey
command
Command a2a-apikey demonstrates Application-to-Application (A2A) API-key retrieval.
|
Command a2a-apikey demonstrates Application-to-Application (A2A) API-key retrieval. |
|
a2a-broker
command
Command a2a-broker demonstrates brokering an access request on behalf of another user.
|
Command a2a-broker demonstrates brokering an access request on behalf of another user. |
|
a2a-discover
command
Command a2a-discover demonstrates GetRetrievableAccounts, which lists every account the context's client certificate is registered to retrieve, across all of its A2A registrations.
|
Command a2a-discover demonstrates GetRetrievableAccounts, which lists every account the context's client certificate is registered to retrieve, across all of its A2A registrations. |
|
a2a-events
command
Command a2a-events demonstrates an A2A credential-change event listener.
|
Command a2a-events demonstrates an A2A credential-change event listener. |
|
a2a-password
command
Command a2a-password demonstrates Application-to-Application (A2A) password retrieval.
|
Command a2a-password demonstrates Application-to-Application (A2A) password retrieval. |
|
a2a-privatekey
command
Command a2a-privatekey demonstrates Application-to-Application (A2A) SSH private-key retrieval.
|
Command a2a-privatekey demonstrates Application-to-Application (A2A) SSH private-key retrieval. |
|
a2a-set-password
command
Command a2a-set-password demonstrates SetPassword, the A2A write-back that stores a new password for an account.
|
Command a2a-set-password demonstrates SetPassword, the A2A write-back that stores a new password for an account. |
|
anonymous
command
Command anonymous demonstrates an anonymous session, which carries no user token and is sufficient for the Notification service and other unauthenticated endpoints such as the appliance status.
|
Command anonymous demonstrates an anonymous session, which carries no user token and is sufficient for the Notification service and other unauthenticated endpoints such as the appliance status. |
|
browser
command
Command browser demonstrates an interactive external-browser (PKCE) login.
|
Command browser demonstrates an interactive external-browser (PKCE) login. |
|
certificate
command
Command certificate demonstrates a client-certificate login over mutual TLS.
|
Command certificate demonstrates a client-certificate login over mutual TLS. |
|
devicecode
command
Command devicecode demonstrates a device authorization grant login.
|
Command devicecode demonstrates a device authorization grant login. |
|
download
command
Command download demonstrates Download, which streams a response body straight to an io.Writer without buffering it in memory — suited to large payloads such as backups or reports.
|
Command download demonstrates Download, which streams a response body straight to an io.Writer without buffering it in memory — suited to large payloads such as backups or reports. |
|
events
command
Command events demonstrates a one-shot SignalR event listener.
|
Command events demonstrates a one-shot SignalR event listener. |
|
events-persistent
command
Command events-persistent demonstrates a reconnecting SignalR event listener.
|
Command events-persistent demonstrates a reconnecting SignalR event listener. |
|
invoke
command
Command invoke is a small general-purpose client that issues one request with any HTTP method against any Safeguard service, demonstrating the generic Invoke surface (Get/Post/Put/Delete).
|
Command invoke is a small general-purpose client that issues one request with any HTTP method against any Safeguard service, demonstrating the generic Invoke surface (Get/Post/Put/Delete). |
|
invoke-typed
command
Command invoke-typed demonstrates InvokeTyped, which decodes a successful JSON response directly into a Go value instead of returning the raw body.
|
Command invoke-typed demonstrates InvokeTyped, which decodes a successful JSON response directly into a Go value instead of returning the raw body. |
|
password
command
Command password demonstrates a Resource Owner Grant (username/password) login and a single Core API call.
|
Command password demonstrates a Resource Owner Grant (username/password) login and a single Core API call. |
|
pkce
command
Command pkce demonstrates a PKCE non-interactive ("headless") login.
|
Command pkce demonstrates a PKCE non-interactive ("headless") login. |
|
token
command
Command token demonstrates reusing an existing Safeguard user token instead of performing a login exchange.
|
Command token demonstrates reusing an existing Safeguard user token instead of performing a login exchange. |