terraform/svchost/disco/oauth_client.go

179 lines
5.7 KiB
Go

package disco
import (
"fmt"
"net/url"
"strings"
"golang.org/x/oauth2"
)
// OAuthClient represents an OAuth client configuration, which is used for
// unusual services that require an entire OAuth client configuration as part
// of their service discovery, rather than just a URL.
type OAuthClient struct {
// ID is the identifier for the client, to be used as "client_id" in
// OAuth requests.
ID string
// Authorization URL is the URL of the authorization endpoint that must
// be used for this OAuth client, as defined in the OAuth2 specifications.
//
// Not all grant types use the authorization endpoint, so it may be omitted
// if none of the grant types in SupportedGrantTypes require it.
AuthorizationURL *url.URL
// Token URL is the URL of the token endpoint that must be used for this
// OAuth client, as defined in the OAuth2 specifications.
//
// Not all grant types use the token endpoint, so it may be omitted
// if none of the grant types in SupportedGrantTypes require it.
TokenURL *url.URL
// MinPort and MaxPort define a range of TCP ports on localhost that this
// client is able to use as redirect_uri in an authorization request.
// Terraform will select a port from this range for the temporary HTTP
// server it creates to receive the authorization response, giving
// a URL like http://localhost:NNN/ where NNN is the selected port number.
//
// Terraform will reject any port numbers in this range less than 1024,
// to respect the common convention (enforced on some operating systems)
// that lower port numbers are reserved for "privileged" services.
MinPort, MaxPort uint16
// SupportedGrantTypes is a set of the grant types that the client may
// choose from. This includes an entry for each distinct type advertised
// by the server, even if a particular keyword is not supported by the
// current version of Terraform.
SupportedGrantTypes OAuthGrantTypeSet
}
// Endpoint returns an oauth2.Endpoint value ready to be used with the oauth2
// library, representing the URLs from the receiver.
func (c *OAuthClient) Endpoint() oauth2.Endpoint {
ep := oauth2.Endpoint{
// We don't actually auth because we're not a server-based OAuth client,
// so this instead just means that we include client_id as an argument
// in our requests.
AuthStyle: oauth2.AuthStyleInParams,
}
if c.AuthorizationURL != nil {
ep.AuthURL = c.AuthorizationURL.String()
}
if c.TokenURL != nil {
ep.TokenURL = c.TokenURL.String()
}
return ep
}
// OAuthGrantType is an enumeration of grant type strings that a host can
// advertise support for.
//
// Values of this type don't necessarily match with a known constant of the
// type, because they may represent grant type keywords defined in a later
// version of Terraform which this version doesn't yet know about.
type OAuthGrantType string
const (
// OAuthAuthzCodeGrant represents an authorization code grant, as
// defined in IETF RFC 6749 section 4.1.
OAuthAuthzCodeGrant = OAuthGrantType("authz_code")
// OAuthOwnerPasswordGrant represents a resource owner password
// credentials grant, as defined in IETF RFC 6749 section 4.3.
OAuthOwnerPasswordGrant = OAuthGrantType("password")
)
// UsesAuthorizationEndpoint returns true if the receiving grant type makes
// use of the authorization endpoint from the client configuration, and thus
// if the authorization endpoint ought to be required.
func (t OAuthGrantType) UsesAuthorizationEndpoint() bool {
switch t {
case OAuthAuthzCodeGrant:
return true
case OAuthOwnerPasswordGrant:
return false
default:
// We'll default to false so that we don't impose any requirements
// on any grant type keywords that might be defined for future
// versions of Terraform.
return false
}
}
// UsesTokenEndpoint returns true if the receiving grant type makes
// use of the token endpoint from the client configuration, and thus
// if the authorization endpoint ought to be required.
func (t OAuthGrantType) UsesTokenEndpoint() bool {
switch t {
case OAuthAuthzCodeGrant:
return true
case OAuthOwnerPasswordGrant:
return true
default:
// We'll default to false so that we don't impose any requirements
// on any grant type keywords that might be defined for future
// versions of Terraform.
return false
}
}
// OAuthGrantTypeSet represents a set of OAuthGrantType values.
type OAuthGrantTypeSet map[OAuthGrantType]struct{}
// NewOAuthGrantTypeSet constructs a new grant type set from the given list
// of grant type keyword strings. Any duplicates in the list are ignored.
func NewOAuthGrantTypeSet(keywords ...string) OAuthGrantTypeSet {
ret := make(OAuthGrantTypeSet, len(keywords))
for _, kw := range keywords {
ret[OAuthGrantType(kw)] = struct{}{}
}
return ret
}
// Has returns true if the given grant type is in the receiving set.
func (s OAuthGrantTypeSet) Has(t OAuthGrantType) bool {
_, ok := s[t]
return ok
}
// RequiresAuthorizationEndpoint returns true if any of the grant types in
// the set are known to require an authorization endpoint.
func (s OAuthGrantTypeSet) RequiresAuthorizationEndpoint() bool {
for t := range s {
if t.UsesAuthorizationEndpoint() {
return true
}
}
return false
}
// RequiresTokenEndpoint returns true if any of the grant types in
// the set are known to require a token endpoint.
func (s OAuthGrantTypeSet) RequiresTokenEndpoint() bool {
for t := range s {
if t.UsesTokenEndpoint() {
return true
}
}
return false
}
// GoString implements fmt.GoStringer.
func (s OAuthGrantTypeSet) GoString() string {
var buf strings.Builder
i := 0
buf.WriteString("disco.NewOAuthGrantTypeSet(")
for t := range s {
if i > 0 {
buf.WriteString(", ")
}
fmt.Fprintf(&buf, "%q", string(t))
i++
}
buf.WriteString(")")
return buf.String()
}