grpc-go/credentials/google/google.go

179 lines
5.9 KiB
Go

/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package google defines credentials for google cloud services.
package google
import (
"context"
"fmt"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/alts"
"google.golang.org/grpc/credentials/oauth"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
)
const defaultCloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform"
var logger = grpclog.Component("credentials")
// DefaultCredentialsOptions constructs options to build DefaultCredentials.
type DefaultCredentialsOptions struct {
// PerRPCCreds is a per RPC credentials that is passed to a bundle.
PerRPCCreds credentials.PerRPCCredentials
// ALTSPerRPCCreds is a per RPC credentials that, if specified, will
// supercede PerRPCCreds above for and only for ALTS connections.
ALTSPerRPCCreds credentials.PerRPCCredentials
}
// NewDefaultCredentialsWithOptions returns a credentials bundle that is
// configured to work with google services.
//
// This API is experimental.
func NewDefaultCredentialsWithOptions(opts DefaultCredentialsOptions) credentials.Bundle {
if opts.PerRPCCreds == nil {
var err error
// If the ADC ends up being Compute Engine Credentials, this context
// won't be used. Otherwise, the context dictates all the subsequent
// token requests via HTTP. So we cannot have any deadline or timeout.
opts.PerRPCCreds, err = newADC(context.TODO())
if err != nil {
logger.Warningf("NewDefaultCredentialsWithOptions: failed to create application oauth: %v", err)
}
}
if opts.ALTSPerRPCCreds != nil {
opts.PerRPCCreds = &dualPerRPCCreds{
perRPCCreds: opts.PerRPCCreds,
altsPerRPCCreds: opts.ALTSPerRPCCreds,
}
}
c := &creds{opts: opts}
bundle, err := c.NewWithMode(internal.CredsBundleModeFallback)
if err != nil {
logger.Warningf("NewDefaultCredentialsWithOptions: failed to create new creds: %v", err)
}
return bundle
}
// NewDefaultCredentials returns a credentials bundle that is configured to work
// with google services.
//
// This API is experimental.
func NewDefaultCredentials() credentials.Bundle {
return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{})
}
// NewComputeEngineCredentials returns a credentials bundle that is configured to work
// with google services. This API must only be used when running on GCE. Authentication configured
// by this API represents the GCE VM's default service account.
//
// This API is experimental.
func NewComputeEngineCredentials() credentials.Bundle {
return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{
PerRPCCreds: oauth.NewComputeEngine(),
})
}
// creds implements credentials.Bundle.
type creds struct {
opts DefaultCredentialsOptions
// Supported modes are defined in internal/internal.go.
mode string
// The active transport credentials associated with this bundle.
transportCreds credentials.TransportCredentials
// The active per RPC credentials associated with this bundle.
perRPCCreds credentials.PerRPCCredentials
}
func (c *creds) TransportCredentials() credentials.TransportCredentials {
return c.transportCreds
}
func (c *creds) PerRPCCredentials() credentials.PerRPCCredentials {
if c == nil {
return nil
}
return c.perRPCCreds
}
var (
newTLS = func() credentials.TransportCredentials {
return credentials.NewTLS(nil)
}
newALTS = func() credentials.TransportCredentials {
return alts.NewClientCreds(alts.DefaultClientOptions())
}
newADC = func(ctx context.Context) (credentials.PerRPCCredentials, error) {
return oauth.NewApplicationDefault(ctx, defaultCloudPlatformScope)
}
)
// NewWithMode should make a copy of Bundle, and switch mode. Modifying the
// existing Bundle may cause races.
func (c *creds) NewWithMode(mode string) (credentials.Bundle, error) {
newCreds := &creds{
opts: c.opts,
mode: mode,
}
// Create transport credentials.
switch mode {
case internal.CredsBundleModeFallback:
newCreds.transportCreds = newClusterTransportCreds(newTLS(), newALTS())
case internal.CredsBundleModeBackendFromBalancer, internal.CredsBundleModeBalancer:
// Only the clients can use google default credentials, so we only need
// to create new ALTS client creds here.
newCreds.transportCreds = newALTS()
default:
return nil, fmt.Errorf("unsupported mode: %v", mode)
}
if mode == internal.CredsBundleModeFallback || mode == internal.CredsBundleModeBackendFromBalancer {
newCreds.perRPCCreds = newCreds.opts.PerRPCCreds
}
return newCreds, nil
}
// dualPerRPCCreds implements credentials.PerRPCCredentials by embedding the
// fallback PerRPCCredentials and the ALTS one. It pickes one of them based on
// the channel type.
type dualPerRPCCreds struct {
perRPCCreds credentials.PerRPCCredentials
altsPerRPCCreds credentials.PerRPCCredentials
}
func (d *dualPerRPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
ri, ok := credentials.RequestInfoFromContext(ctx)
if !ok {
return nil, fmt.Errorf("request info not found from context")
}
if authType := ri.AuthInfo.AuthType(); authType == "alts" {
return d.altsPerRPCCreds.GetRequestMetadata(ctx, uri...)
}
// This ensures backward compatibility even if authType is not "tls".
return d.perRPCCreds.GetRequestMetadata(ctx, uri...)
}
func (d *dualPerRPCCreds) RequireTransportSecurity() bool {
return d.altsPerRPCCreds.RequireTransportSecurity() || d.perRPCCreds.RequireTransportSecurity()
}