svchost/auth: New API for storing and forgetting credentials
This new functionality will be used as part of implementing the "terraform login" and "terraform logout" commands. As of this commit, the storage codepaths are all just stubs. Subsequent commits will implement these new methods for each of the different physical credentials sources.
This commit is contained in:
parent
f3fe3bfb5f
commit
821d0401bc
|
@ -43,3 +43,19 @@ func (s *cachingCredentialsSource) ForHost(host svchost.Hostname) (HostCredentia
|
|||
s.cache[host] = result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *cachingCredentialsSource) StoreForHost(host svchost.Hostname, credentials HostCredentialsWritable) error {
|
||||
// We'll delete the cache entry even if the store fails, since that just
|
||||
// means that the next read will go to the real store and get a chance to
|
||||
// see which object (old or new) is actually present.
|
||||
delete(s.cache, host)
|
||||
return s.source.StoreForHost(host, credentials)
|
||||
}
|
||||
|
||||
func (s *cachingCredentialsSource) ForgetForHost(host svchost.Hostname) error {
|
||||
// We'll delete the cache entry even if the store fails, since that just
|
||||
// means that the next read will go to the real store and get a chance to
|
||||
// see if the object is still present.
|
||||
delete(s.cache, host)
|
||||
return s.source.ForgetForHost(host)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/svchost"
|
||||
)
|
||||
|
||||
|
@ -14,6 +17,9 @@ import (
|
|||
// 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
|
||||
|
@ -33,6 +39,19 @@ type CredentialsSource interface {
|
|||
// 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
|
||||
|
@ -47,6 +66,22 @@ type HostCredentials interface {
|
|||
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.
|
||||
//
|
||||
|
@ -61,3 +96,23 @@ func (c Credentials) ForHost(host svchost.Hostname) (HostCredentials, error) {
|
|||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -78,3 +78,11 @@ func (s *helperProgramCredentialsSource) ForHost(host svchost.Hostname) (HostCre
|
|||
|
||||
return HostCredentialsFromMap(m), nil
|
||||
}
|
||||
|
||||
func (s *helperProgramCredentialsSource) StoreForHost(host svchost.Hostname, credentials HostCredentialsWritable) error {
|
||||
return fmt.Errorf("credentials helper cannot currently store new credentials")
|
||||
}
|
||||
|
||||
func (s *helperProgramCredentialsSource) ForgetForHost(host svchost.Hostname) error {
|
||||
return fmt.Errorf("credentials helper cannot currently forget existing credentials")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/svchost"
|
||||
)
|
||||
|
||||
|
@ -26,3 +28,11 @@ func (s staticCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s staticCredentialsSource) StoreForHost(host svchost.Hostname, credentials HostCredentialsWritable) error {
|
||||
return fmt.Errorf("can't store new credentials in a static credentials source")
|
||||
}
|
||||
|
||||
func (s staticCredentialsSource) ForgetForHost(host svchost.Hostname) error {
|
||||
return fmt.Errorf("can't discard credentials from a static credentials source")
|
||||
}
|
||||
|
|
|
@ -2,13 +2,23 @@ package auth
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// HostCredentialsToken is a HostCredentials implementation that represents a
|
||||
// single "bearer token", to be sent to the server via an Authorization header
|
||||
// with the auth type set to "Bearer"
|
||||
// with the auth type set to "Bearer".
|
||||
//
|
||||
// To save a token as the credentials for a host, convert the token string to
|
||||
// this type and use the result as a HostCredentialsWritable implementation.
|
||||
type HostCredentialsToken string
|
||||
|
||||
// Interface implementation assertions. Compilation will fail here if
|
||||
// HostCredentialsToken does not fully implement these interfaces.
|
||||
var _ HostCredentials = HostCredentialsToken("")
|
||||
var _ HostCredentialsWritable = HostCredentialsToken("")
|
||||
|
||||
// PrepareRequest alters the given HTTP request by setting its Authorization
|
||||
// header to the string "Bearer " followed by the encapsulated authentication
|
||||
// token.
|
||||
|
@ -23,3 +33,11 @@ func (tc HostCredentialsToken) PrepareRequest(req *http.Request) {
|
|||
func (tc HostCredentialsToken) Token() string {
|
||||
return string(tc)
|
||||
}
|
||||
|
||||
// ToStore returns a credentials object with a single attribute "token" whose
|
||||
// value is the token string.
|
||||
func (tc HostCredentialsToken) ToStore() cty.Value {
|
||||
return cty.ObjectVal(map[string]cty.Value{
|
||||
"token": cty.StringVal(string(tc)),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,16 +3,29 @@ package auth
|
|||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestHostCredentialsToken(t *testing.T) {
|
||||
creds := HostCredentialsToken("foo-bar")
|
||||
req := &http.Request{}
|
||||
|
||||
creds.PrepareRequest(req)
|
||||
{
|
||||
req := &http.Request{}
|
||||
creds.PrepareRequest(req)
|
||||
authStr := req.Header.Get("authorization")
|
||||
if got, want := authStr, "Bearer foo-bar"; got != want {
|
||||
t.Errorf("wrong Authorization header value %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
authStr := req.Header.Get("authorization")
|
||||
if got, want := authStr, "Bearer foo-bar"; got != want {
|
||||
t.Errorf("wrong Authorization header value %q; want %q", got, want)
|
||||
{
|
||||
got := creds.ToStore()
|
||||
want := cty.ObjectVal(map[string]cty.Value{
|
||||
"token": cty.StringVal("foo-bar"),
|
||||
})
|
||||
if !want.RawEquals(got) {
|
||||
t.Errorf("wrong storable object value\ngot: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue