blob: f489f8780e73b419b50b8e16808ae74022982b2f [file] [log] [blame]
// Package header supports extracting the email of an authorized user from a
// protobuf in an HTTP Header.
package protoheader
import (
"context"
"encoding/base64"
"errors"
"net/http"
"strings"
"go.skia.org/infra/go/secret"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/kube/go/authproxy/auth"
"google.golang.org/protobuf/proto"
)
const (
// HeaderSecretName is the name of the GCP secret for login.
HeaderSecretName = "authproxy-protoheader-name"
// LoginURNSecretName is the name of the GCP secret for the login URL.
LoginURNSecretName = "authproxy-loginurl"
// Project is the project where the above secrets are stored in.
Project = "skia-infra-public"
)
var (
errDotInHeaderRequired = errors.New("Failed to find a '.' separated header value.")
)
// ProtoHeader implements auth.Auth.
type ProtoHeader struct {
headerName string
loginURL string
}
// New creates a ProtoHeader.
func New(ctx context.Context, secretClient secret.Client) (ProtoHeader, error) {
var ret ProtoHeader
headerName, err := secretClient.Get(ctx, Project, HeaderSecretName, secret.VersionLatest)
if err != nil {
return ret, skerr.Wrapf(err, "failed loading secrets from GCP secret manager; failed to retrieve secret %q", HeaderSecretName)
}
ret.headerName = headerName
loginURL, err := secretClient.Get(ctx, Project, LoginURNSecretName, secret.VersionLatest)
if err != nil {
return ret, skerr.Wrapf(err, "failed loading secrets from GCP secret manager; failed to retrieve secret %q", LoginURNSecretName)
}
ret.loginURL = loginURL
return ret, nil
}
// Init implements auth.Auth.
func (p ProtoHeader) Init(ctx context.Context) error {
return nil
}
// LoggedInAs implements auth.Auth.
func (p ProtoHeader) LoggedInAs(r *http.Request) (string, error) {
// The header value contains a base64 encoded proto and then it's signed
// which adds a signature, separated by a period.
headerValue := r.Header.Get(p.headerName)
parts := strings.Split(headerValue, ".")
if len(parts) != 2 {
return "", errDotInHeaderRequired
}
// Only use the base64 encoded data before the period.
b, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return "", skerr.Wrapf(err, "decoding base64 header: %q", p.headerName)
}
var h Header
err = proto.Unmarshal(b, &h)
if err != nil {
return "", skerr.Wrapf(err, "decoding proto %q", p.headerName)
}
return h.Email, nil
}
// LoginURL implements auth.Auth.
func (p ProtoHeader) LoginURL(w http.ResponseWriter, r *http.Request) string {
return p.loginURL
}
// Confirm we implement the interface.
var _ auth.Auth = ProtoHeader{}