authorize: move impersonation into session/service account (#1765)

* move impersonation into session/service account

* replace frontend statik

* fix data race

* move JWT filling to separate function, break up functions

* maybe fix data race

* fix code climate issue
This commit is contained in:
Caleb Doxsey 2021-01-11 15:40:08 -07:00 committed by GitHub
parent 1466f4e5a0
commit a6bc9f492f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 328 additions and 162 deletions

View file

@ -10,7 +10,6 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"github.com/golang/protobuf/proto"
@ -25,8 +24,6 @@ import (
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/pkg/cryptutil"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
"github.com/pomerium/pomerium/pkg/grpc/session"
"github.com/pomerium/pomerium/pkg/grpc/user"
)
const (
@ -180,46 +177,7 @@ func (e *Evaluator) JWTPayload(req *Request) map[string]interface{} {
payload := map[string]interface{}{
"iss": e.authenticateHost,
}
if u, err := url.Parse(req.HTTP.URL); err == nil {
payload["aud"] = u.Hostname()
}
if s, ok := req.DataBrokerData.Get("type.googleapis.com/session.Session", req.Session.ID).(*session.Session); ok {
payload["jti"] = s.GetId()
if tm, err := ptypes.Timestamp(s.GetIdToken().GetExpiresAt()); err == nil {
payload["exp"] = tm.Unix()
}
if tm, err := ptypes.Timestamp(s.GetIdToken().GetIssuedAt()); err == nil {
payload["iat"] = tm.Unix()
}
if u, ok := req.DataBrokerData.Get("type.googleapis.com/user.User", s.GetUserId()).(*user.User); ok {
payload["sub"] = u.GetId()
payload["user"] = u.GetId()
payload["email"] = u.GetEmail()
}
if du, ok := req.DataBrokerData.Get("type.googleapis.com/directory.User", s.GetUserId()).(*directory.User); ok {
if du.GetEmail() != "" {
payload["email"] = du.GetEmail()
}
var groupNames []string
for _, groupID := range du.GetGroupIds() {
if dg, ok := req.DataBrokerData.Get("type.googleapis.com/directory.Group", groupID).(*directory.Group); ok {
groupNames = append(groupNames, dg.Name)
}
}
var groups []string
groups = append(groups, du.GetGroupIds()...)
groups = append(groups, groupNames...)
payload["groups"] = groups
}
}
if req.Session.ImpersonateEmail != "" {
payload["email"] = req.Session.ImpersonateEmail
}
if len(req.Session.ImpersonateGroups) > 0 {
payload["groups"] = req.Session.ImpersonateGroups
}
req.fillJWTPayload(payload)
return payload
}
@ -305,10 +263,18 @@ func (e *Evaluator) newInput(req *Request, isValidClientCertificate bool) *input
if i.DataBrokerData.Session == nil {
i.DataBrokerData.Session = req.DataBrokerData.Get(serviceAccountTypeURL, req.Session.ID)
}
if obj, ok := i.DataBrokerData.Session.(interface{ GetUserId() string }); ok {
i.DataBrokerData.User = req.DataBrokerData.Get(userTypeURL, obj.GetUserId())
var userIDs []string
if obj, ok := i.DataBrokerData.Session.(interface{ GetUserId() string }); ok && obj.GetUserId() != "" {
userIDs = append(userIDs, obj.GetUserId())
}
if obj, ok := i.DataBrokerData.Session.(interface{ GetImpersonateUserId() string }); ok && obj.GetImpersonateUserId() != "" {
userIDs = append(userIDs, obj.GetImpersonateUserId())
}
user, ok := req.DataBrokerData.Get(directoryUserTypeURL, obj.GetUserId()).(*directory.User)
for _, userID := range userIDs {
i.DataBrokerData.User = req.DataBrokerData.Get(userTypeURL, userID)
user, ok := req.DataBrokerData.Get(directoryUserTypeURL, userID).(*directory.User)
if ok {
var groups []string
for _, groupID := range user.GetGroupIds() {
@ -331,31 +297,6 @@ func (e *Evaluator) newInput(req *Request, isValidClientCertificate bool) *input
return i
}
type (
// Request is the request data used for the evaluator.
Request struct {
DataBrokerData DataBrokerData `json:"databroker_data"`
HTTP RequestHTTP `json:"http"`
Session RequestSession `json:"session"`
CustomPolicies []string
}
// RequestHTTP is the HTTP field in the request.
RequestHTTP struct {
Method string `json:"method"`
URL string `json:"url"`
Headers map[string]string `json:"headers"`
ClientCertificate string `json:"client_certificate"`
}
// RequestSession is the session field in the request.
RequestSession struct {
ID string `json:"id"`
ImpersonateEmail string `json:"impersonate_email"`
ImpersonateGroups []string `json:"impersonate_groups"`
}
)
// Result is the result of evaluation.
type Result struct {
Status int

View file

@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"gopkg.in/square/go-jose.v2/jwt"
"github.com/pomerium/pomerium/config"
@ -58,9 +59,7 @@ func TestJSONMarshal(t *testing.T) {
ClientCertificate: "CLIENT_CERTIFICATE",
},
Session: RequestSession{
ID: "SESSION_ID",
ImpersonateEmail: "y@example.com",
ImpersonateGroups: []string{"group1"},
ID: "SESSION_ID",
},
}, true))
assert.JSONEq(t, `{
@ -79,9 +78,7 @@ func TestJSONMarshal(t *testing.T) {
"url": "https://example.com"
},
"session": {
"id": "SESSION_ID",
"impersonate_email": "y@example.com",
"impersonate_groups": ["group1"]
"id": "SESSION_ID"
},
"is_valid_client_certificate": true
}`, string(bs))
@ -158,6 +155,7 @@ func TestEvaluator_JWTPayload(t *testing.T) {
ExpiresAt: nowPb,
IssuedAt: nowPb,
},
ExpiresAt: nowPb,
},
},
},
@ -174,6 +172,31 @@ func TestEvaluator_JWTPayload(t *testing.T) {
"iat": now.Unix(),
},
},
{
"with service account",
&Request{
DataBrokerData: DataBrokerData{
"type.googleapis.com/user.ServiceAccount": map[string]interface{}{
"SERVICE_ACCOUNT_ID": &user.ServiceAccount{
Id: "SERVICE_ACCOUNT_ID",
IssuedAt: nowPb,
ExpiresAt: nowPb,
},
},
},
HTTP: RequestHTTP{URL: "https://example.com"},
Session: RequestSession{
ID: "SERVICE_ACCOUNT_ID",
},
},
map[string]interface{}{
"iss": "authn.example.com",
"jti": "SERVICE_ACCOUNT_ID",
"aud": "example.com",
"exp": now.Unix(),
"iat": now.Unix(),
},
},
{
"with user",
&Request{
@ -252,12 +275,22 @@ func TestEvaluator_JWTPayload(t *testing.T) {
&Request{
HTTP: RequestHTTP{URL: "https://example.com"},
Session: RequestSession{
ImpersonateEmail: "user@example.com",
ImpersonateGroups: []string{"admin", "test"},
ID: "SESSION_ID",
},
DataBrokerData: DataBrokerData{
"type.googleapis.com/session.Session": map[string]interface{}{
"SESSION_ID": &session.Session{
Id: "SESSION_ID",
UserId: "USER_ID",
ImpersonateEmail: proto.String("user@example.com"),
ImpersonateGroups: []string{"admin", "test"},
},
},
},
},
map[string]interface{}{
"iss": "authn.example.com",
"jti": "SESSION_ID",
"aud": "example.com",
"email": "user@example.com",
"groups": []string{"admin", "test"},

View file

@ -14,7 +14,7 @@ all_allowed_groups := get_allowed_groups(route_policy)
all_allowed_users := get_allowed_users(route_policy)
all_allowed_idp_claims := get_allowed_idp_claims(route_policy)
is_impersonating := count(input.session.impersonate_email) > 0
is_impersonating := count(session.impersonate_email) > 0
# allow public
allow {
@ -52,14 +52,14 @@ allow {
# allow by impersonate email
allow {
is_impersonating
all_allowed_users[_] = input.session.impersonate_email
all_allowed_users[_] = session.impersonate_email
}
# allow by impersonate group
allow {
is_impersonating
some group
input.session.impersonate_groups[_] = group
session.impersonate_groups[_] = group
all_allowed_groups[_] = group
}
@ -74,7 +74,7 @@ allow {
allow {
is_impersonating
some domain
email_in_domain(input.session.impersonate_email, all_allowed_domains[domain])
email_in_domain(session.impersonate_email, all_allowed_domains[domain])
}
# allow by arbitrary idp claims

View file

@ -15,7 +15,7 @@ test_email_allowed {
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "" }
input.session as { "id": "session1" }
}
test_impersonate_email_not_allowed {
@ -26,14 +26,14 @@ test_impersonate_email_not_allowed {
}] with
input.databroker_data as {
"session": {
"user_id": "user1"
"user_id": "user1", "impersonate_email": "y@example.com"
},
"user": {
"email": "x@example.com"
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "y@example.com" }
input.session as { "id": "session1" }
}
test_impersonate_email_allowed {
@ -44,14 +44,14 @@ test_impersonate_email_allowed {
}] with
input.databroker_data as {
"session": {
"user_id": "user1"
"user_id": "user1", "impersonate_email": "y@example.com"
},
"user": {
"email": "x@example.com"
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "y@example.com" }
input.session as { "id": "session1" }
}
test_group_allowed {
@ -81,7 +81,7 @@ test_impersonate_groups_not_allowed {
}] with
input.databroker_data as {
"session": {
"user_id": "user1"
"user_id": "user1", "impersonate_email": "y@example.com", "impersonate_groups": ["2"]
},
"user": {
"email": "x@example.com"
@ -89,7 +89,7 @@ test_impersonate_groups_not_allowed {
"groups": ["1"]
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "y@example.com", "impersonate_groups": ["2"] }
input.session as { "id": "session1" }
}
test_impersonate_groups_allowed {
@ -100,7 +100,7 @@ test_impersonate_groups_allowed {
}] with
input.databroker_data as {
"session": {
"user_id": "user1"
"user_id": "user1", "impersonate_email": "y@example.com", "impersonate_groups": ["2"]
},
"user": {
"email": "x@example.com"
@ -110,7 +110,7 @@ test_impersonate_groups_allowed {
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "y@example.com", "impersonate_groups": ["2"] }
input.session as { "id": "session1" }
}
test_domain_allowed {
@ -121,14 +121,14 @@ test_domain_allowed {
}] with
input.databroker_data as {
"session": {
"user_id": "user1"
"user_id": "user1", "impersonate_email": ""
},
"user": {
"email": "x@example.com"
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "" }
input.session as { "id": "session1" }
}
test_impersonate_domain_not_allowed {
@ -139,14 +139,14 @@ test_impersonate_domain_not_allowed {
}] with
input.databroker_data as {
"session": {
"user_id": "user1"
"user_id": "user1", "impersonate_email": "y@example1.com"
},
"user": {
"email": "x@example.com"
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "y@example1.com" }
input.session as { "id": "session1" }
}
test_impersonate_domain_allowed {
@ -157,14 +157,14 @@ test_impersonate_domain_allowed {
}] with
input.databroker_data as {
"session": {
"user_id": "user1"
"user_id": "user1", "impersonate_email": "y@example1.com"
},
"user": {
"email": "x@example.com"
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "y@example1.com" }
input.session as { "id": "session1" }
}
test_idp_claims_allowed {
@ -183,7 +183,7 @@ test_idp_claims_allowed {
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "" }
input.session as { "id": "session1" }
}
test_example {
@ -395,7 +395,7 @@ test_any_authenticated_user_allowed {
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "" }
input.session as { "id": "session1" }
}
test_any_authenticated_user_denied {
not allow with
@ -404,5 +404,5 @@ test_any_authenticated_user_denied {
"AllowAnyAuthenticatedUser": true
}] with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "" }
input.session as { "id": "session1" }
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,100 @@
package evaluator
import (
"net/url"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/pomerium/pomerium/internal/directory"
"github.com/pomerium/pomerium/pkg/grpc/session"
"github.com/pomerium/pomerium/pkg/grpc/user"
)
type (
// Request is the request data used for the evaluator.
Request struct {
DataBrokerData DataBrokerData `json:"databroker_data"`
HTTP RequestHTTP `json:"http"`
Session RequestSession `json:"session"`
CustomPolicies []string
}
// RequestHTTP is the HTTP field in the request.
RequestHTTP struct {
Method string `json:"method"`
URL string `json:"url"`
Headers map[string]string `json:"headers"`
ClientCertificate string `json:"client_certificate"`
}
// RequestSession is the session field in the request.
RequestSession struct {
ID string `json:"id"`
}
)
type sessionOrServiceAccount interface {
GetId() string
GetExpiresAt() *timestamppb.Timestamp
GetIssuedAt() *timestamppb.Timestamp
GetUserId() string
GetImpersonateEmail() string
GetImpersonateGroups() []string
GetImpersonateUserId() string
}
func (req *Request) fillJWTPayload(payload map[string]interface{}) {
if u, err := url.Parse(req.HTTP.URL); err == nil {
payload["aud"] = u.Hostname()
}
if s, ok := req.DataBrokerData.Get("type.googleapis.com/session.Session", req.Session.ID).(*session.Session); ok {
req.fillJWTPayloadSessionOrServiceAccount(payload, s)
}
if sa, ok := req.DataBrokerData.Get("type.googleapis.com/user.ServiceAccount", req.Session.ID).(*user.ServiceAccount); ok {
req.fillJWTPayloadSessionOrServiceAccount(payload, sa)
}
}
func (req *Request) fillJWTPayloadSessionOrServiceAccount(payload map[string]interface{}, s sessionOrServiceAccount) {
payload["jti"] = s.GetId()
if s.GetExpiresAt().IsValid() {
payload["exp"] = s.GetExpiresAt().AsTime().Unix()
}
if s.GetIssuedAt().IsValid() {
payload["iat"] = s.GetIssuedAt().AsTime().Unix()
}
userID := s.GetUserId()
if s.GetImpersonateUserId() != "" {
userID = s.GetImpersonateUserId()
}
if u, ok := req.DataBrokerData.Get("type.googleapis.com/user.User", userID).(*user.User); ok {
payload["sub"] = u.GetId()
payload["user"] = u.GetId()
payload["email"] = u.GetEmail()
}
if du, ok := req.DataBrokerData.Get("type.googleapis.com/directory.User", userID).(*directory.User); ok {
if du.GetEmail() != "" {
payload["email"] = du.GetEmail()
}
var groupNames []string
for _, groupID := range du.GetGroupIds() {
if dg, ok := req.DataBrokerData.Get("type.googleapis.com/directory.Group", groupID).(*directory.Group); ok {
groupNames = append(groupNames, dg.Name)
}
}
var groups []string
groups = append(groups, du.GetGroupIds()...)
groups = append(groups, groupNames...)
payload["groups"] = groups
}
if s.GetImpersonateEmail() != "" {
payload["email"] = s.GetImpersonateEmail()
}
if len(s.GetImpersonateGroups()) > 0 {
payload["groups"] = s.GetImpersonateGroups()
}
}

View file

@ -230,9 +230,7 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(in *envoy_service_auth_v
}
if sessionState != nil {
req.Session = evaluator.RequestSession{
ID: sessionState.ID,
ImpersonateEmail: sessionState.ImpersonateEmail,
ImpersonateGroups: sessionState.ImpersonateGroups,
ID: sessionState.ID,
}
}
p := a.getMatchingPolicy(requestURL)

View file

@ -92,9 +92,7 @@ func Test_getEvaluatorRequest(t *testing.T) {
)
expect := &evaluator.Request{
Session: evaluator.RequestSession{
ID: "SESSION_ID",
ImpersonateEmail: "foo@example.com",
ImpersonateGroups: []string{"admin", "test"},
ID: "SESSION_ID",
},
HTTP: evaluator.RequestHTTP{
Method: "GET",

View file

@ -22,14 +22,12 @@ import (
var (
errObtainCertFailed = errors.New("obtain cert failed")
errRenewCertFailed = errors.New("renew cert failed")
checkInterval = time.Minute * 10
acmeTemplate = certmagic.DefaultACME
)
// Manager manages TLS certificates.
type Manager struct {
src config.Source
src config.Source
acmeTemplate certmagic.ACMEManager
mu sync.RWMutex
config *config.Config
@ -42,6 +40,14 @@ type Manager struct {
// New creates a new autocert manager.
func New(src config.Source) (*Manager, error) {
return newManager(context.Background(), src, certmagic.DefaultACME, time.Minute*10)
}
func newManager(ctx context.Context,
src config.Source,
acmeTemplate certmagic.ACMEManager,
checkInterval time.Duration,
) (*Manager, error) {
// set certmagic default storage cache, otherwise cert renewal loop will be based off
// certmagic's own default location
certmagic.Default.Storage = &certmagic.FileStorage{
@ -50,8 +56,9 @@ func New(src config.Source) (*Manager, error) {
certmagic.Default.Logger = log.ZapLogger().With(zap.String("service", "autocert"))
mgr := &Manager{
src: src,
certmagic: certmagic.NewDefault(),
src: src,
acmeTemplate: acmeTemplate,
certmagic: certmagic.NewDefault(),
}
err := mgr.update(src.GetConfig())
if err != nil {
@ -71,11 +78,16 @@ func New(src config.Source) (*Manager, error) {
ticker := time.NewTicker(checkInterval)
defer ticker.Stop()
for range ticker.C {
err := mgr.renewConfigCerts()
if err != nil {
log.Error().Err(err).Msg("autocert: error updating config")
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
err := mgr.renewConfigCerts()
if err != nil {
log.Error().Err(err).Msg("autocert: error updating config")
return
}
}
}
}()
@ -92,7 +104,7 @@ func (mgr *Manager) getCertMagicConfig(options *config.Options) (*certmagic.Conf
return nil, fmt.Errorf("config: failed caching cert: %w", err)
}
}
acmeMgr := certmagic.NewACMEManager(mgr.certmagic, acmeTemplate)
acmeMgr := certmagic.NewACMEManager(mgr.certmagic, mgr.acmeTemplate)
acmeMgr.Agreed = true
if options.AutocertOptions.UseStaging {
acmeMgr.CA = acmeMgr.TestCA

View file

@ -2,6 +2,7 @@ package autocert
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
@ -122,6 +123,9 @@ func newMockACME(srv *httptest.Server) http.Handler {
}
func TestConfig(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var mockACME http.Handler
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mockACME.ServeHTTP(w, r)
@ -140,23 +144,12 @@ func TestConfig(t *testing.T) {
addr := li.Addr().String()
_ = li.Close()
oAcmeTemplate := acmeTemplate
defer func() { acmeTemplate = oAcmeTemplate }()
acmeTemplate = certmagic.ACMEManager{
CA: srv.URL + "/acme/directory",
TestCA: srv.URL + "/acme/directory",
}
oCheckInterval := checkInterval
defer func() { checkInterval = oCheckInterval }()
checkInterval = time.Second
p1 := config.Policy{
From: "http://from.example.com", To: "http://to.example.com",
}
_ = p1.Validate()
mgr, err := New(config.NewStaticSource(&config.Config{
mgr, err := newManager(ctx, config.NewStaticSource(&config.Config{
Options: &config.Options{
AutocertOptions: config.AutocertOptions{
Enable: true,
@ -167,7 +160,10 @@ func TestConfig(t *testing.T) {
HTTPRedirectAddr: addr,
Policies: []config.Policy{p1},
},
}))
}), certmagic.ACMEManager{
CA: srv.URL + "/acme/directory",
TestCA: srv.URL + "/acme/directory",
}, time.Second)
if !assert.NoError(t, err) {
return
}

View file

@ -8,6 +8,7 @@ import (
"github.com/golang/protobuf/ptypes"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
@ -71,3 +72,8 @@ func (x *Session) SetRawIDToken(rawIDToken string) {
}
x.IdToken.Raw = rawIDToken
}
// GetIssuedAt returns the issued at timestamp for the id token.
func (x *Session) GetIssuedAt() *timestamppb.Timestamp {
return x.GetIdToken().GetIssuedAt()
}

View file

@ -182,14 +182,17 @@ type Session struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
UserId string `protobuf:"bytes,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
ExpiresAt *timestamp.Timestamp `protobuf:"bytes,4,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
IdToken *IDToken `protobuf:"bytes,6,opt,name=id_token,json=idToken,proto3" json:"id_token,omitempty"`
OauthToken *OAuthToken `protobuf:"bytes,7,opt,name=oauth_token,json=oauthToken,proto3" json:"oauth_token,omitempty"`
Claims map[string]*_struct.ListValue `protobuf:"bytes,9,rep,name=claims,proto3" json:"claims,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Audience []string `protobuf:"bytes,10,rep,name=audience,proto3" json:"audience,omitempty"`
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
UserId string `protobuf:"bytes,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
ExpiresAt *timestamp.Timestamp `protobuf:"bytes,4,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
IdToken *IDToken `protobuf:"bytes,6,opt,name=id_token,json=idToken,proto3" json:"id_token,omitempty"`
OauthToken *OAuthToken `protobuf:"bytes,7,opt,name=oauth_token,json=oauthToken,proto3" json:"oauth_token,omitempty"`
Claims map[string]*_struct.ListValue `protobuf:"bytes,9,rep,name=claims,proto3" json:"claims,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Audience []string `protobuf:"bytes,10,rep,name=audience,proto3" json:"audience,omitempty"`
ImpersonateUserId *string `protobuf:"bytes,11,opt,name=impersonate_user_id,json=impersonateUserId,proto3,oneof" json:"impersonate_user_id,omitempty"`
ImpersonateEmail *string `protobuf:"bytes,12,opt,name=impersonate_email,json=impersonateEmail,proto3,oneof" json:"impersonate_email,omitempty"`
ImpersonateGroups []string `protobuf:"bytes,13,rep,name=impersonate_groups,json=impersonateGroups,proto3" json:"impersonate_groups,omitempty"`
}
func (x *Session) Reset() {
@ -280,6 +283,27 @@ func (x *Session) GetAudience() []string {
return nil
}
func (x *Session) GetImpersonateUserId() string {
if x != nil && x.ImpersonateUserId != nil {
return *x.ImpersonateUserId
}
return ""
}
func (x *Session) GetImpersonateEmail() string {
if x != nil && x.ImpersonateEmail != nil {
return *x.ImpersonateEmail
}
return ""
}
func (x *Session) GetImpersonateGroups() []string {
if x != nil {
return x.ImpersonateGroups
}
return nil
}
var File_session_proto protoreflect.FileDescriptor
var file_session_proto_rawDesc = []byte{
@ -311,7 +335,7 @@ var file_session_proto_rawDesc = []byte{
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78,
0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65,
0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x93, 0x03, 0x0a,
0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xd7, 0x04, 0x0a,
0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
@ -331,16 +355,28 @@ var file_session_proto_rawDesc = []byte{
0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x61, 0x69,
0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12,
0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28,
0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x1a, 0x55, 0x0a, 0x0b, 0x43,
0x6c, 0x61, 0x69, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
0x38, 0x01, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69,
0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x13, 0x69,
0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x65,
0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01,
0x12, 0x30, 0x0a, 0x11, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f,
0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x10, 0x69,
0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x88,
0x01, 0x01, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74,
0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11,
0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70,
0x73, 0x1a, 0x55, 0x0a, 0x0b, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x69, 0x6d, 0x70,
0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64,
0x42, 0x14, 0x0a, 0x12, 0x5f, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65,
0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f,
0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f,
0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -423,6 +459,7 @@ func file_session_proto_init() {
}
}
}
file_session_proto_msgTypes[2].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{

View file

@ -30,4 +30,8 @@ message Session {
OAuthToken oauth_token = 7;
map<string, google.protobuf.ListValue> claims = 9;
repeated string audience = 10;
optional string impersonate_user_id = 11;
optional string impersonate_email = 12;
repeated string impersonate_groups = 13;
}

View file

@ -166,10 +166,13 @@ type ServiceAccount struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
ExpiresAt *timestamp.Timestamp `protobuf:"bytes,3,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
IssuedAt *timestamp.Timestamp `protobuf:"bytes,4,opt,name=issued_at,json=issuedAt,proto3" json:"issued_at,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
ExpiresAt *timestamp.Timestamp `protobuf:"bytes,3,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
IssuedAt *timestamp.Timestamp `protobuf:"bytes,4,opt,name=issued_at,json=issuedAt,proto3" json:"issued_at,omitempty"`
ImpersonateUserId *string `protobuf:"bytes,5,opt,name=impersonate_user_id,json=impersonateUserId,proto3,oneof" json:"impersonate_user_id,omitempty"`
ImpersonateEmail *string `protobuf:"bytes,6,opt,name=impersonate_email,json=impersonateEmail,proto3,oneof" json:"impersonate_email,omitempty"`
ImpersonateGroups []string `protobuf:"bytes,7,rep,name=impersonate_groups,json=impersonateGroups,proto3" json:"impersonate_groups,omitempty"`
}
func (x *ServiceAccount) Reset() {
@ -232,6 +235,27 @@ func (x *ServiceAccount) GetIssuedAt() *timestamp.Timestamp {
return nil
}
func (x *ServiceAccount) GetImpersonateUserId() string {
if x != nil && x.ImpersonateUserId != nil {
return *x.ImpersonateUserId
}
return ""
}
func (x *ServiceAccount) GetImpersonateEmail() string {
if x != nil && x.ImpersonateEmail != nil {
return *x.ImpersonateEmail
}
return ""
}
func (x *ServiceAccount) GetImpersonateGroups() []string {
if x != nil {
return x.ImpersonateGroups
}
return nil
}
var File_user_proto protoreflect.FileDescriptor
var file_user_proto_rawDesc = []byte{
@ -257,7 +281,7 @@ var file_user_proto_rawDesc = []byte{
0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xad, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x72,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf1, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75,
0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73,
@ -268,10 +292,23 @@ var file_user_proto_rawDesc = []byte{
0x37, 0x0a, 0x09, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08,
0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f,
0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70,
0x63, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x12, 0x33, 0x0a, 0x13, 0x69, 0x6d, 0x70, 0x65,
0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f,
0x6e, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a,
0x11, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6d, 0x61,
0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x10, 0x69, 0x6d, 0x70, 0x65,
0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x88, 0x01, 0x01, 0x12,
0x2d, 0x0a, 0x12, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x67,
0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6d, 0x70,
0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x42, 0x16,
0x0a, 0x14, 0x5f, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x75,
0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x69, 0x6d, 0x70, 0x65, 0x72,
0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x2c, 0x5a, 0x2a,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72,
0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67,
0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
@ -350,6 +387,7 @@ func file_user_proto_init() {
}
}
}
file_user_proto_msgTypes[2].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{

View file

@ -24,4 +24,8 @@ message ServiceAccount {
string user_id = 2;
google.protobuf.Timestamp expires_at = 3;
google.protobuf.Timestamp issued_at = 4;
optional string impersonate_user_id = 5;
optional string impersonate_email = 6;
repeated string impersonate_groups = 7;
}

View file

@ -32,9 +32,8 @@ func TestSignedJWT(t *testing.T) {
grpc.StreamInterceptor(StreamRequireSignedJWT(base64.StdEncoding.EncodeToString(key))),
grpc.UnaryInterceptor(UnaryRequireSignedJWT(base64.StdEncoding.EncodeToString(key))),
)
go srv.Serve(li)
reflection.Register(srv)
go srv.Serve(li)
t.Run("unauthenticated", func(t *testing.T) {
cc, err := grpc.Dial(li.Addr().String(),