// Package auth contains types and functions to manage authentication // credentials for service hosts. package auth import ( "fmt" "net/http" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform-svchost" ) // Credentials is a list of CredentialsSource objects that can be tried in // turn until one returns credentials for a host, or one returns an error. // // A Credentials is itself a CredentialsSource, wrapping its members. // In principle one CredentialsSource can be nested inside another, though // there is no good reason to do so. // // The write operations on a Credentials are tried only on the first object, // under the assumption that it is the primary store. type Credentials []CredentialsSource // NoCredentials is an empty CredentialsSource that always returns nil // when asked for credentials. var NoCredentials CredentialsSource = Credentials{} // A CredentialsSource is an object that may be able to provide credentials // for a given host. // // Credentials lookups are not guaranteed to be concurrency-safe. Callers // using these facilities in concurrent code must use external concurrency // primitives to prevent race conditions. type CredentialsSource interface { // ForHost returns a non-nil HostCredentials if the source has credentials // available for the host, and a nil HostCredentials if it does not. // // If an error is returned, progress through a list of CredentialsSources // is halted and the error is returned to the user. ForHost(host svchost.Hostname) (HostCredentials, error) // StoreForHost takes a HostCredentialsWritable and saves it as the // credentials for the given host. // // If credentials are already stored for the given host, it will try to // replace those credentials but may produce an error if such replacement // is not possible. StoreForHost(host svchost.Hostname, credentials HostCredentialsWritable) error // ForgetForHost discards any stored credentials for the given host. It // does nothing and returns successfully if no credentials are saved // for that host. ForgetForHost(host svchost.Hostname) error } // HostCredentials represents a single set of credentials for a particular // host. type HostCredentials interface { // PrepareRequest modifies the given request in-place to apply the // receiving credentials. The usual behavior of this method is to // add some sort of Authorization header to the request. PrepareRequest(req *http.Request) // Token returns the authentication token. Token() string } // HostCredentialsWritable is an extension of HostCredentials for credentials // objects that can be serialized as a JSON-compatible object value for // storage. type HostCredentialsWritable interface { HostCredentials // ToStore returns a cty.Value, always of an object type, // representing data that can be serialized to represent this object // in persistent storage. // // The resulting value may uses only cty values that can be accepted // by the cty JSON encoder, though the caller may elect to instead store // it in some other format that has a JSON-compatible type system. ToStore() cty.Value } // ForHost iterates over the contained CredentialsSource objects and // tries to obtain credentials for the given host from each one in turn. // // If any source returns either a non-nil HostCredentials or a non-nil error // then this result is returned. Otherwise, the result is nil, nil. func (c Credentials) ForHost(host svchost.Hostname) (HostCredentials, error) { for _, source := range c { creds, err := source.ForHost(host) if creds != nil || err != nil { return creds, err } } return nil, nil } // StoreForHost passes the given arguments to the same operation on the // first CredentialsSource in the receiver. func (c Credentials) StoreForHost(host svchost.Hostname, credentials HostCredentialsWritable) error { if len(c) == 0 { return fmt.Errorf("no credentials store is available") } return c[0].StoreForHost(host, credentials) } // ForgetForHost passes the given arguments to the same operation on the // first CredentialsSource in the receiver. func (c Credentials) ForgetForHost(host svchost.Hostname) error { if len(c) == 0 { return fmt.Errorf("no credentials store is available") } return c[0].ForgetForHost(host) }