authorize: implement allowed_idp_claims (#1542)

* add arbitrary claims to session

* add support for maps

* update flattened claims

* fix eol

* fix trailing whitespace

* fix tests
This commit is contained in:
Caleb Doxsey 2020-10-23 14:05:37 -06:00 committed by GitHub
parent 2a97e92d50
commit 153e438eb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1369 additions and 743 deletions

View file

@ -19,6 +19,7 @@ import (
"gopkg.in/square/go-jose.v2/jwt"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/internal/identity/manager"
"github.com/pomerium/pomerium/internal/identity/oidc"
"github.com/pomerium/pomerium/internal/log"
@ -379,14 +380,20 @@ func (a *Authenticate) getOAuthCallback(w http.ResponseWriter, r *http.Request)
// Successful Authentication Response: rfc6749#section-4.1.2 & OIDC#3.1.2.5
//
// Exchange the supplied Authorization Code for a valid user session.
s := sessions.State{ID: uuid.New().String()}
accessToken, err := a.provider.Load().Authenticate(ctx, code, &s)
var claims identity.Claims
accessToken, err := a.provider.Load().Authenticate(ctx, code, &claims)
if err != nil {
return nil, fmt.Errorf("error redeeming authenticate code: %w", err)
}
s := sessions.State{ID: uuid.New().String()}
err = claims.Claims(&s)
if err != nil {
return nil, fmt.Errorf("error unmarshaling session state: %w", err)
}
// save the session and access token to the databroker
err = a.saveSessionToDataBroker(ctx, &s, accessToken)
err = a.saveSessionToDataBroker(ctx, &s, claims, accessToken)
if err != nil {
return nil, httputil.NewError(http.StatusInternalServerError, err)
}
@ -526,7 +533,7 @@ func (a *Authenticate) Dashboard(w http.ResponseWriter, r *http.Request) error {
return a.templates.ExecuteTemplate(w, "dashboard.html", input)
}
func (a *Authenticate) saveSessionToDataBroker(ctx context.Context, sessionState *sessions.State, accessToken *oauth2.Token) error {
func (a *Authenticate) saveSessionToDataBroker(ctx context.Context, sessionState *sessions.State, claims identity.Claims, accessToken *oauth2.Token) error {
state := a.state.Load()
options := a.options.Load()
@ -546,6 +553,7 @@ func (a *Authenticate) saveSessionToDataBroker(ctx context.Context, sessionState
},
OauthToken: manager.ToOAuthToken(accessToken),
}
s.AddClaims(claims.Flatten())
// if no user exists yet, create a new one
currentUser, _ := user.Get(ctx, state.dataBrokerClient, s.GetUserId())

View file

@ -12,6 +12,7 @@ groups := input.databroker_data.groups
all_allowed_domains := get_allowed_domains(route_policy)
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)
# allow public
allow {
@ -65,6 +66,14 @@ allow {
email_in_domain(input.session.impersonate_email, all_allowed_domains[domain])
}
# allow by arbitrary idp claims
allow {
are_claims_allowed(all_allowed_idp_claims[_], session.claims)
}
allow {
are_claims_allowed(all_allowed_idp_claims[_], user.claims)
}
# allow pomerium urls
allow {
contains(input.http.url, "/.pomerium/")
@ -181,3 +190,21 @@ get_allowed_groups(policy) = v {
[u | u := policy.sub_policies[_].allowed_groups[_]]
)[_] }
}
get_allowed_idp_claims(policy) = v {
v := array.concat(
[policy.allowed_idp_claims],
[u | u := policy.sub_policies[_].allowed_idp_claims]
)
}
are_claims_allowed(a, b) {
is_object(a)
is_object(b)
avs := a[ak]
bvs := object.get(b, ak, null)
is_array(avs)
is_array(bvs)
avs[_] == bvs[_]
}

View file

@ -169,6 +169,25 @@ test_impersonate_domain_allowed {
input.session as { "id": "session1", "impersonate_email": "y@example1.com" }
}
test_idp_claims_allowed {
allow with
data.route_policies as [{
"source": "example.com",
"allowed_idp_claims": {
"some.claim": ["a", "b"]
}
}] with
input.databroker_data as {
"session": {
"claims": {
"some.claim": ["b"]
}
}
} with
input.http as { "url": "http://example.com" } with
input.session as { "id": "session1", "impersonate_email": "" }
}
test_example {
not allow with
data.route_policies as [
@ -344,3 +363,14 @@ test_sub_policy {
})
z == {"g1", "g2", "g3", "g4"}
}
test_are_claims_allowed {
are_claims_allowed({"a": ["1"]}, {"a": ["1"]})
not are_claims_allowed({"a": ["2"]}, {"a": ["1"]})
are_claims_allowed({"a": ["1", "2", "3"]}, {"a": ["1"]})
are_claims_allowed({"a": ["1"]}, {"a": ["1", "2", "3"]})
not are_claims_allowed({"a": ["4", "5", "6"]}, {"a": ["1"]})
are_claims_allowed({"a.b.c": ["1"], "d.e.f": ["2"]}, {"d.e.f": ["2"]})
}

File diff suppressed because one or more lines are too long

View file

@ -16,6 +16,7 @@ import (
"github.com/golang/protobuf/ptypes"
"github.com/mitchellh/hashstructure"
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/cryptutil"
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
@ -26,9 +27,10 @@ type Policy struct {
From string `mapstructure:"from" yaml:"from"`
To string `mapstructure:"to" yaml:"to"`
// Identity related policy
AllowedUsers []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty" json:"allowed_users,omitempty"`
AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty" json:"allowed_groups,omitempty"`
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"`
AllowedUsers []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty" json:"allowed_users,omitempty"`
AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty" json:"allowed_groups,omitempty"`
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"`
AllowedIDPClaims identity.FlattenedClaims `mapstructure:"allowed_idp_claims" yaml:"allowed_idp_claims,omitempty" json:"allowed_idp_claims,omitempty"`
Source *StringURL `yaml:",omitempty" json:"source,omitempty" hash:"ignore"`
Destination *url.URL `yaml:",omitempty" json:"destination,omitempty" hash:"ignore"`
@ -134,12 +136,13 @@ type Policy struct {
// A SubPolicy is a protobuf Policy within a protobuf Route.
type SubPolicy struct {
ID string `mapstructure:"id" yaml:"id" json:"id"`
Name string `mapstructure:"name" yaml:"name" json:"name"`
AllowedUsers []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty" json:"allowed_users,omitempty"`
AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty" json:"allowed_groups,omitempty"`
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"`
Rego []string `mapstructure:"rego" yaml:"rego" json:"rego,omitempty"`
ID string `mapstructure:"id" yaml:"id" json:"id"`
Name string `mapstructure:"name" yaml:"name" json:"name"`
AllowedUsers []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty" json:"allowed_users,omitempty"`
AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty" json:"allowed_groups,omitempty"`
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"`
AllowedIDPClaims identity.FlattenedClaims `mapstructure:"allowed_idp_claims" yaml:"allowed_idp_claims,omitempty" json:"allowed_idp_claims,omitempty"`
Rego []string `mapstructure:"rego" yaml:"rego" json:"rego,omitempty"`
}
// NewPolicyFromProto creates a new Policy from a protobuf policy config route.
@ -152,6 +155,7 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
AllowedUsers: pb.GetAllowedUsers(),
AllowedGroups: pb.GetAllowedGroups(),
AllowedDomains: pb.GetAllowedDomains(),
AllowedIDPClaims: identity.NewFlattenedClaimsFromPB(pb.GetAllowedIdpClaims()),
Prefix: pb.GetPrefix(),
Path: pb.GetPath(),
Regex: pb.GetRegex(),
@ -178,12 +182,13 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
}
for _, sp := range pb.GetPolicies() {
p.SubPolicies = append(p.SubPolicies, SubPolicy{
ID: sp.GetId(),
Name: sp.GetName(),
AllowedUsers: sp.GetAllowedUsers(),
AllowedGroups: sp.GetAllowedGroups(),
AllowedDomains: sp.GetAllowedDomains(),
Rego: sp.GetRego(),
ID: sp.GetId(),
Name: sp.GetName(),
AllowedUsers: sp.GetAllowedUsers(),
AllowedGroups: sp.GetAllowedGroups(),
AllowedDomains: sp.GetAllowedDomains(),
AllowedIDPClaims: identity.NewFlattenedClaimsFromPB(sp.GetAllowedIdpClaims()),
Rego: sp.GetRego(),
})
}
return p, p.Validate()
@ -195,12 +200,13 @@ func (p *Policy) ToProto() *configpb.Route {
sps := make([]*configpb.Policy, 0, len(p.SubPolicies))
for _, sp := range p.SubPolicies {
sps = append(sps, &configpb.Policy{
Id: sp.ID,
Name: sp.Name,
AllowedUsers: sp.AllowedUsers,
AllowedGroups: sp.AllowedGroups,
AllowedDomains: sp.AllowedDomains,
Rego: sp.Rego,
Id: sp.ID,
Name: sp.Name,
AllowedUsers: sp.AllowedUsers,
AllowedGroups: sp.AllowedGroups,
AllowedDomains: sp.AllowedDomains,
AllowedIdpClaims: sp.AllowedIDPClaims.ToPB(),
Rego: sp.Rego,
})
}
return &configpb.Route{
@ -210,6 +216,7 @@ func (p *Policy) ToProto() *configpb.Route {
AllowedUsers: p.AllowedUsers,
AllowedGroups: p.AllowedGroups,
AllowedDomains: p.AllowedDomains,
AllowedIdpClaims: p.AllowedIDPClaims.ToPB(),
Prefix: p.Prefix,
Path: p.Path,
Regex: p.Regex,

View file

@ -9,6 +9,6 @@ import (
const Luascripts = "luascripts" // static asset namespace
func init() {
data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x88\xbd\xbbP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00 \x00clean-upstream.luaUT\x05\x00\x01P\xfb\xce^\x94S\xc1n\x9c0\x10\xbd\xf3\x15O\xf4PV%\x91z\xdd\xc8\xff\xd0{\xd5\"\x17f\x17\xab`\xbb\xf68\x9b\xe4\xd0o\xaf\x00\xc3\xe2\x00\xaa\xe2\x03\x1e\xcb\xf3\xde<\xde\x8c/A\xd7\xac\x8c\x86\xa3\xde<SeMON\x85\xbe\xaa\x8d\xf9\xad\xa8\x98\xb6J\xcb\x9eJL\x87S\x06\x00\x0f\x0f\xe8\x82Dc\xc8\xeb\xcf\x0c\x1f\xac5\x8ea\xec\xc0&;\xd4\xd2rp\x84\xab3\xc1\xfa\x19\xe2\x0dn\x04G\xb6\x935\x81oj\xf8\x1a\xb4R7\x1da..^^\xdf \x19\xdc\x12H70\x971\xf4\xec\x94\xbe\x8eT\x93\x12\x88\x18\x9c\xaf>\xfcZk\xc5\xe3#r\xf1\xfd\xe7\xd3\x8f/O\xc8K\xe4\xf9\xe9\xa3\xb8\x15\xca\x11\x07\xa7#&#\xddd\xd9\xe2[+}e\x1d]\xd4K\xe1\xd9\x95\x98\xe2\x04\xe7\xd9\xe1\xaf\x80V\x1d\xa4n\x86\xe3y(\xfb\xb5\xc4\xa7\x98\x0d!\"\xf0\x1d;\xe9g\xf3Z\x19]9\xfa\x13\xc8s\x11\xf7jrl*\xd3\x99ZvhI6\xe4<\x04\xd2\x9cs\xbc(\xd6\xc9=\xb1l$\xcbm\xf6|S\x9c\xb2U~\x9c\x8e\xb5Sb!9_\x89\x8b|\x7f\x80\xa2\x83\xea\xb2G\xc1-\xe9\xf1\xfa^hiPT=q'\\\x91/fFc\x13\xaaai\xba-\\\x07\xb3\xbdU\x94\x8e\xf8\xbcf)ql\x179\xe5\xbd\xc8\x1d0\xf4o\xde\xb7\x06\xca\xc0\xadq\xeaM\x8e\xdd\xfd\x9f\x85I\xf6\xc6\xc9\x94k\xc7\xcb\xf7\xc5\x12K\xf7\xb8\x0f\xa0q\xbe!\x90\x7f\x8b\xd2\x90\xaf[\xb1z\x03 \xb0\xdc\xe59m\x9buwx\xf8\xb3cqks\x0f\x1f\x8a\xb7F\xfb\xa1\xbbS\xb0<\x95\x11\xf1/\x00\x00\xff\xffPK\x07\x08\xfb\x06j<\xa2\x01\x00\x00\xf0\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x88\xbd\xbbP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00 \x00ext-authz-set-cookie.luaUT\x05\x00\x01P\xfb\xce^\x8c\x91An\x830\x10E\xf7\x9cb\xc4\xcaHI\x0e\x80\x94\x03t\xd1\x13T\x955\xc5C\xb0j\x8fS{\x88\x9aM\xcf^AM\x04\x0dM\x19 \x01\xe2\xff?\xf8\xbf\xb6\xe7Fl` \xbe\x84\xab\x0e\xac#}\xf4\x94D\xe5\xbb\xee\x90\x8d\xa3\xaa\x00\x00p\xa1A\x07\x1d\xa1\xa1\x98\xe0\x08KM\x9d?\xa8\xb9\xd8\\\x19\xbdm\xb4'\xc1{G\x92H\xe8\x9f\xb8\x0d\xaa\xaa\xb3\xf4\x99\x04\x0d\n\xe6\x18\xdbN\x0b\xeb\x13\x89*?\xf7\xe7\xe0)\xda\xde\xef\x13\xc9\xbe \xe1\xddRY\xc1\xd7\x11\xd8:\x90\x8ex\xf4\x0d3_^\xa7\xc1=\x1e\xf3\xd0Z'\x14\xd3\xa1\x139\x1f\\\x8f\xe5\x0e\xca)U'\x12\x9dSw\xb7\xa4\xbb\xd9\xf2OU\xf1[\x1d\xc9\x87\x0b\xfdi\x18\xf5\xc4\xa6\x18\xaeb\x8dM:\x07N\xa4\xa6\x87\x7f\xe8,D\xdb\xf0,-\x1b\xf8\xfc\xe4\xc8\x9b\x83\xe3\xb2\xef\xd3\x83\xbeoh\x07_&\x87l\x86\xd7\x97U\x12\xaf\xab|\xa7Z\xd1\x18U\xce\x8a\xdc=\x08Z\x96\xfc\x1d\x00\x00\xff\xffPK\x07\x08\x93\xe7\xad\x94\x07\x01\x00\x00\x00\x03\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\xc8\x92*Q\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00 \x00remove-impersonate-headers.luaUT\x05\x00\x01\xd9nZ_|\x92\xd1n\xb30\x0c\x85\xefy\n\x8b\xff&H\xf4\x97v\x8b\xc4\xb3DiqKTH\x98\xed\xb0M\xd3\xde}\xca\x02)\xack\xb9I\xc4\xf9|\xec#g\xf0'3\xc09\xb8\x93X\xef\x80\xc5\x90\xb0~\xb3\xd2+\x16\xaa\xd3\x8f\xaa\x00\x00 \x94@\x11\xa1\x86\xc3Q\xbd\xd4\xf0/\xa9\xd0\xb6\x89+\xd0uE\x91\xcd\xd0\xcd\xfeC{\xa7 _\x03\xb2\xa8\xe5\xd4\xbdq\xdd\x80\xc95\x0d\xd0\xa3\xe9\x90\x18Z\xd83\xcd\"\xa8-<\xa2\x98\xce\x88\xb9\xa7WEU\xc5\x86'\x1c\xfd\x8c\xda\x8e\x13\x12{g\x04\xf5\xad\xdfZ\xd2\\PT\xf9\x18-\xd3\x04\xf6\xfc\xccNzt?\xd8\xad\xb9x\x9d\n\xa0\x85\xcf\xaf,\x9e=\xc1\xb5\x86\x19\xac\x83\xc9Xb\xb5xT\xd0\xf9L-\x1d\xb7[\xb9\xd6PnZ\x1f\xf0]\xc8\x1c\xca\n\xa2a\xdc\xc4N\xbd\x90\x0fS\xf9\xb7\x16\x18\xa9\xdc\x8f\xbc~b\x8e\x03\xfe\xb7\x8e\x91D\xe5\x045\\\xab\x1d\x1a\xd7\xbd\xbd?I\x97M\xee\xf2-\xb9\x9b$\xab\xb9*~\xdb\xc7\xf3\xe1\xcb\xe2\xc9;F\xb5^\xf2\xdb\x8a\x05\xdf\x01\x00\x00\xff\xffPK\x07\x08y\x19$\xa3\x1b\x01\x00\x00\xdd\x02\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x88\xbd\xbbP\xfb\x06j<\xa2\x01\x00\x00\xf0\x04\x00\x00\x12\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\x00\x00\x00\x00clean-upstream.luaUT\x05\x00\x01P\xfb\xce^PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x88\xbd\xbbP\x93\xe7\xad\x94\x07\x01\x00\x00\x00\x03\x00\x00\x18\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\xeb\x01\x00\x00ext-authz-set-cookie.luaUT\x05\x00\x01P\xfb\xce^PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\xc8\x92*Qy\x19$\xa3\x1b\x01\x00\x00\xdd\x02\x00\x00\x1e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81A\x03\x00\x00remove-impersonate-headers.luaUT\x05\x00\x01\xd9nZ_PK\x05\x06\x00\x00\x00\x00\x03\x00\x03\x00\xed\x00\x00\x00\xb1\x04\x00\x00\x00\x00"
data := "PK\x03\x04\x14\x00\x08\x00\x08\x00]\xa2PQ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00 \x00clean-upstream.luaUT\x05\x00\x013\x00\x8a_\x94S\xc1n\x9c0\x10\xbd\xf3\x15O\xf4PV%\x91z\xdd\xc8\xff\xd0{\xd5\"\x17f\x17\xab`\xbb\xf68\x9b\xe4\xd0o\xaf\x00\xc3\xe2\x00\xaa\xe2\x03\x1e\xcb\xf3\xde<\xde\x8c/A\xd7\xac\x8c\x86\xa3\xde<SeMON\x85\xbe\xaa\x8d\xf9\xad\xa8\x98\xb6J\xcb\x9eJL\x87S\x06\x00\x0f\x0f\xe8\x82Dc\xc8\xeb\xcf\x0c\x1f\xac5\x8ea\xec\xc0&;\xd4\xd2rp\x84\xab3\xc1\xfa\x19\xe2\x0dn\x04G\xb6\x935\x81oj\xf8\x1a\xb4R7\x1da..^^\xdf \x19\xdc\x12H70\x971\xf4\xec\x94\xbe\x8eT\x93\x12\x88\x18\x9c\xaf>\xfcZk\xc5\xe3#r\xf1\xfd\xe7\xd3\x8f/O\xc8K\xe4\xf9\xe9\xa3\xb8\x15\xca\x11\x07\xa7#&#\xddd\xd9\xe2[+}e\x1d]\xd4K\xe1\xd9\x95\x98\xe2\x04\xe7\xd9\xe1\xaf\x80V\x1d\xa4n\x86\xe3y(\xfb\xb5\xc4\xa7\x98\x0d!\"\xf0\x1d;\xe9g\xf3Z\x19]9\xfa\x13\xc8s\x11\xf7jrl*\xd3\x99ZvhI6\xe4<\x04\xd2\x9cs\xbc(\xd6\xc9=\xb1l$\xcbm\xf6|S\x9c\xb2U~\x9c\x8e\xb5Sb!9_\x89\x8b|\x7f\x80\xa2\x83\xea\xb2G\xc1-\xe9\xf1\xfa^hiPT=q'\\\x91/fFc\x13\xaaai\xba-\\\x07\xb3\xbdU\x94\x8e\xf8\xbcf)ql\x179\xe5\xbd\xc8\x1d0\xf4o\xde\xb7\x06\xca\xc0\xadq\xeaM\x8e\xdd\xfd\x9f\x85I\xf6\xc6\xc9\x94k\xc7\xcb\xf7\xc5\x12K\xf7\xb8\x0f\xa0q\xbe!\x90\x7f\x8b\xd2\x90\xaf[\xb1z\x03 \xb0\xdc\xe59m\x9buwx\xf8\xb3cqks\x0f\x1f\x8a\xb7F\xfb\xa1\xbbS\xb0<\x95\x11\xf1/\x00\x00\xff\xffPK\x07\x08\xfb\x06j<\xa2\x01\x00\x00\xf0\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00]\xa2PQ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00 \x00ext-authz-set-cookie.luaUT\x05\x00\x013\x00\x8a_\x8c\x91An\x830\x10E\xf7\x9cb\xc4\xcaHI\x0e\x80\x94\x03t\xd1\x13T\x955\xc5C\xb0j\x8fS{\x88\x9aM\xcf^AM\x04\x0dM\x19 \x01\xe2\xff?\xf8\xbf\xb6\xe7Fl` \xbe\x84\xab\x0e\xac#}\xf4\x94D\xe5\xbb\xee\x90\x8d\xa3\xaa\x00\x00p\xa1A\x07\x1d\xa1\xa1\x98\xe0\x08KM\x9d?\xa8\xb9\xd8\\\x19\xbdm\xb4'\xc1{G\x92H\xe8\x9f\xb8\x0d\xaa\xaa\xb3\xf4\x99\x04\x0d\n\xe6\x18\xdbN\x0b\xeb\x13\x89*?\xf7\xe7\xe0)\xda\xde\xef\x13\xc9\xbe \xe1\xddRY\xc1\xd7\x11\xd8:\x90\x8ex\xf4\x0d3_^\xa7\xc1=\x1e\xf3\xd0Z'\x14\xd3\xa1\x139\x1f\\\x8f\xe5\x0e\xca)U'\x12\x9dSw\xb7\xa4\xbb\xd9\xf2OU\xf1[\x1d\xc9\x87\x0b\xfdi\x18\xf5\xc4\xa6\x18\xaeb\x8dM:\x07N\xa4\xa6\x87\x7f\xe8,D\xdb\xf0,-\x1b\xf8\xfc\xe4\xc8\x9b\x83\xe3\xb2\xef\xd3\x83\xbeoh\x07_&\x87l\x86\xd7\x97U\x12\xaf\xab|\xa7Z\xd1\x18U\xce\x8a\xdc=\x08Z\x96\xfc\x1d\x00\x00\xff\xffPK\x07\x08\x93\xe7\xad\x94\x07\x01\x00\x00\x00\x03\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00]\xa2PQ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00 \x00remove-impersonate-headers.luaUT\x05\x00\x013\x00\x8a_|\x92\xd1n\xb30\x0c\x85\xefy\n\x8b\xff&H\xf4\x97v\x8b\xc4\xb3DiqKTH\x98\xed\xb0M\xd3\xde}\xca\x02)\xack\xb9I\xc4\xf9|\xec#g\xf0'3\xc09\xb8\x93X\xef\x80\xc5\x90\xb0~\xb3\xd2+\x16\xaa\xd3\x8f\xaa\x00\x00 \x94@\x11\xa1\x86\xc3Q\xbd\xd4\xf0/\xa9\xd0\xb6\x89+\xd0uE\x91\xcd\xd0\xcd\xfeC{\xa7 _\x03\xb2\xa8\xe5\xd4\xbdq\xdd\x80\xc95\x0d\xd0\xa3\xe9\x90\x18Z\xd83\xcd\"\xa8-<\xa2\x98\xce\x88\xb9\xa7WEU\xc5\x86'\x1c\xfd\x8c\xda\x8e\x13\x12{g\x04\xf5\xad\xdfZ\xd2\\PT\xf9\x18-\xd3\x04\xf6\xfc\xccNzt?\xd8\xad\xb9x\x9d\n\xa0\x85\xcf\xaf,\x9e=\xc1\xb5\x86\x19\xac\x83\xc9Xb\xb5xT\xd0\xf9L-\x1d\xb7[\xb9\xd6PnZ\x1f\xf0]\xc8\x1c\xca\n\xa2a\xdc\xc4N\xbd\x90\x0fS\xf9\xb7\x16\x18\xa9\xdc\x8f\xbc~b\x8e\x03\xfe\xb7\x8e\x91D\xe5\x045\\\xab\x1d\x1a\xd7\xbd\xbd?I\x97M\xee\xf2-\xb9\x9b$\xab\xb9*~\xdb\xc7\xf3\xe1\xcb\xe2\xc9;F\xb5^\xf2\xdb\x8a\x05\xdf\x01\x00\x00\xff\xffPK\x07\x08y\x19$\xa3\x1b\x01\x00\x00\xdd\x02\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00]\xa2PQ\xfb\x06j<\xa2\x01\x00\x00\xf0\x04\x00\x00\x12\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\x00\x00\x00\x00clean-upstream.luaUT\x05\x00\x013\x00\x8a_PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00]\xa2PQ\x93\xe7\xad\x94\x07\x01\x00\x00\x00\x03\x00\x00\x18\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\xeb\x01\x00\x00ext-authz-set-cookie.luaUT\x05\x00\x013\x00\x8a_PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00]\xa2PQy\x19$\xa3\x1b\x01\x00\x00\xdd\x02\x00\x00\x1e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81A\x03\x00\x00remove-impersonate-headers.luaUT\x05\x00\x013\x00\x8a_PK\x05\x06\x00\x00\x00\x00\x03\x00\x03\x00\xed\x00\x00\x00\xb1\x04\x00\x00\x00\x00"
fs.RegisterWithNamespace("luascripts", data)
}

140
internal/identity/claims.go Normal file
View file

@ -0,0 +1,140 @@
package identity
import (
"encoding/json"
"fmt"
"reflect"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/structpb"
"github.com/pomerium/pomerium/pkg/protoutil"
)
// Claims are JWT claims.
type Claims map[string]interface{}
// NewClaimsFromRaw creates a new Claims map from a map of raw messages.
func NewClaimsFromRaw(raw map[string]json.RawMessage) Claims {
claims := make(Claims)
for k, rawv := range raw {
var v interface{}
if err := json.Unmarshal(rawv, &v); err == nil {
claims[k] = v
}
}
return claims
}
// UnmarshalJSON unmarshals the raw json data into the claims object.
func (claims *Claims) UnmarshalJSON(data []byte) error {
if *claims == nil {
*claims = make(Claims)
}
var m map[string]interface{}
err := json.Unmarshal(data, &m)
if err != nil {
return err
}
for k, v := range m {
(*claims)[k] = v
}
return nil
}
// Claims takes the claims data and fills v.
func (claims Claims) Claims(v interface{}) error {
bs, err := json.Marshal(claims)
if err != nil {
return err
}
return json.Unmarshal(bs, v)
}
// Flatten flattens the claims to a FlattenedClaims map. For example:
//
// { "a": { "b": { "c": 12345 } } } => { "a.b.c": [12345] }
//
func (claims Claims) Flatten() FlattenedClaims {
flattened := make(FlattenedClaims)
for k, v := range claims {
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Map:
subClaims := make(Claims)
iter := rv.MapRange()
for iter.Next() {
subClaims[fmt.Sprint(iter.Key().Interface())] = iter.Value().Interface()
}
for sk, sv := range subClaims.Flatten() {
flattened[k+"."+sk] = sv
}
case reflect.Slice:
slc := make([]interface{}, rv.Len())
for i := 0; i < rv.Len(); i++ {
slc[i] = rv.Index(i).Interface()
}
flattened[k] = slc
default:
flattened[k] = []interface{}{v}
}
}
return flattened
}
// ToAnyMap converts the claims into a map of string => any.
func (claims Claims) ToAnyMap() map[string]*anypb.Any {
m := map[string]*anypb.Any{}
for k, v := range claims {
m[k] = protoutil.ToAny(v)
}
return m
}
// FlattenedClaims are a set claims flattened into a single-level map.
type FlattenedClaims map[string][]interface{}
// NewFlattenedClaimsFromPB creates a new FlattenedClaims from the protobuf struct type.
func NewFlattenedClaimsFromPB(m map[string]*structpb.ListValue) FlattenedClaims {
claims := make(FlattenedClaims)
if m == nil {
return claims
}
bs, _ := json.Marshal(m)
_ = json.Unmarshal(bs, &claims)
return claims
}
// ToPB converts the flattened claims into a protobuf type.
func (claims FlattenedClaims) ToPB() map[string]*structpb.ListValue {
if claims == nil {
return nil
}
m := make(map[string]*structpb.ListValue)
for k, vs := range claims {
svs := make([]*structpb.Value, len(vs))
for i, v := range vs {
svs[i] = protoutil.ToStruct(v)
}
m[k] = &structpb.ListValue{Values: svs}
}
return m
}
// UnmarshalJSON unmarshals JSON into the flattened claims.
func (claims *FlattenedClaims) UnmarshalJSON(data []byte) error {
var unflattened Claims
err := json.Unmarshal(data, &unflattened)
if err != nil {
return err
}
if *claims == nil {
*claims = make(FlattenedClaims)
}
for k, v := range unflattened.Flatten() {
(*claims)[k] = v
}
return nil
}

View file

@ -0,0 +1,28 @@
package identity
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestClaims_Flatten(t *testing.T) {
var claims Claims
_ = json.Unmarshal([]byte(`
{
"a": {
"aa": {
"aaa": 12345
},
"ab": [1, 2, 3, 4, 5]
}
}
`), &claims)
flattened := claims.Flatten()
assert.Equal(t, FlattenedClaims{
"a.aa.aaa": {12345.0},
"a.ab": {1.0, 2.0, 3.0, 4.0, 5.0},
}, flattened)
}

View file

@ -6,8 +6,8 @@ import (
"github.com/golang/protobuf/ptypes"
"github.com/google/btree"
"google.golang.org/protobuf/types/known/anypb"
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/pkg/grpc/session"
"github.com/pomerium/pomerium/pkg/grpc/user"
)
@ -45,17 +45,7 @@ func (u *User) UnmarshalJSON(data []byte) error {
delete(raw, "email")
}
u.User.Claims = make(map[string]*anypb.Any)
for k, rawv := range raw {
var v interface{}
if json.Unmarshal(rawv, &v) != nil {
continue
}
if anyv, err := toAny(v); err == nil {
u.User.Claims[k] = anyv
}
}
u.AddClaims(identity.NewClaimsFromRaw(raw).Flatten())
return nil
}
@ -141,17 +131,7 @@ func (s *Session) UnmarshalJSON(data []byte) error {
delete(raw, "iat")
}
s.Session.Claims = make(map[string]*anypb.Any)
for k, rawv := range raw {
var v interface{}
if json.Unmarshal(rawv, &v) != nil {
continue
}
if anyv, err := toAny(v); err == nil {
s.Session.Claims[k] = anyv
}
}
s.AddClaims(identity.NewClaimsFromRaw(raw).Flatten())
return nil
}

View file

@ -8,10 +8,10 @@ import (
"github.com/golang/protobuf/ptypes"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/wrapperspb"
"google.golang.org/protobuf/types/known/structpb"
"github.com/pomerium/pomerium/pkg/grpc/session"
"github.com/pomerium/pomerium/pkg/protoutil"
)
func TestUser_UnmarshalJSON(t *testing.T) {
@ -25,9 +25,8 @@ func TestUser_UnmarshalJSON(t *testing.T) {
assert.NotNil(t, u.User)
assert.Equal(t, "joe", u.User.Name)
assert.Equal(t, "joe@test.com", u.User.Email)
anyv, _ := ptypes.MarshalAny(&wrapperspb.StringValue{Value: "xyz"})
assert.Equal(t, map[string]*anypb.Any{
"some-other-claim": anyv,
assert.Equal(t, map[string]*structpb.ListValue{
"some-other-claim": {Values: []*structpb.Value{protoutil.ToStruct("xyz")}},
}, u.Claims)
}
@ -72,8 +71,7 @@ func TestSession_UnmarshalJSON(t *testing.T) {
assert.Equal(t, "subject", s.Session.IdToken.Subject)
assert.Equal(t, pbtm, s.Session.IdToken.ExpiresAt)
assert.Equal(t, pbtm, s.Session.IdToken.IssuedAt)
anyv, _ := ptypes.MarshalAny(&wrapperspb.StringValue{Value: "xyz"})
assert.Equal(t, map[string]*anypb.Any{
"some-other-claim": anyv,
assert.Equal(t, map[string]*structpb.ListValue{
"some-other-claim": {Values: []*structpb.Value{protoutil.ToStruct("xyz")}},
}, s.Claims)
}

View file

@ -1,90 +1,14 @@
package manager
import (
"fmt"
"strings"
"github.com/golang/protobuf/ptypes"
structpb "github.com/golang/protobuf/ptypes/struct"
"golang.org/x/oauth2"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/pomerium/pomerium/pkg/grpc/session"
)
func toAny(value interface{}) (*anypb.Any, error) {
switch v := value.(type) {
case bool:
return ptypes.MarshalAny(&wrapperspb.BoolValue{Value: v})
case []byte:
return ptypes.MarshalAny(&wrapperspb.BytesValue{Value: v})
case float64:
return ptypes.MarshalAny(&wrapperspb.DoubleValue{Value: v})
case float32:
return ptypes.MarshalAny(&wrapperspb.FloatValue{Value: v})
case int32:
return ptypes.MarshalAny(&wrapperspb.Int32Value{Value: v})
case int64:
return ptypes.MarshalAny(&wrapperspb.Int64Value{Value: v})
case string:
return ptypes.MarshalAny(&wrapperspb.StringValue{Value: v})
case uint32:
return ptypes.MarshalAny(&wrapperspb.UInt32Value{Value: v})
case uint64:
return ptypes.MarshalAny(&wrapperspb.UInt64Value{Value: v})
case []interface{}:
lst := &structpb.ListValue{}
for _, c := range v {
if cv, err := toValue(c); err == nil {
lst.Values = append(lst.Values, cv)
}
}
return ptypes.MarshalAny(lst)
}
return nil, fmt.Errorf("unknown type %T", value)
}
func toValue(value interface{}) (*structpb.Value, error) {
switch v := value.(type) {
case bool:
return &structpb.Value{
Kind: &structpb.Value_BoolValue{BoolValue: v},
}, nil
case float64:
return &structpb.Value{
Kind: &structpb.Value_NumberValue{NumberValue: v},
}, nil
case float32:
return &structpb.Value{
Kind: &structpb.Value_NumberValue{NumberValue: float64(v)},
}, nil
case int32:
return &structpb.Value{
Kind: &structpb.Value_NumberValue{NumberValue: float64(v)},
}, nil
case int64:
return &structpb.Value{
Kind: &structpb.Value_NumberValue{NumberValue: float64(v)},
}, nil
case string:
return &structpb.Value{
Kind: &structpb.Value_StringValue{StringValue: v},
}, nil
case uint32:
return &structpb.Value{
Kind: &structpb.Value_NumberValue{NumberValue: float64(v)},
}, nil
case uint64:
return &structpb.Value{
Kind: &structpb.Value_NumberValue{NumberValue: float64(v)},
}, nil
}
return nil, fmt.Errorf("unknown type %T", value)
}
func toSessionSchedulerKey(userID, sessionID string) string {
return userID + "\037" + sessionID
}

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@ package pomerium.config;
option go_package = "github.com/pomerium/pomerium/pkg/grpc/config";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
message Config {
string name = 1;
@ -20,6 +21,7 @@ message Route {
repeated string allowed_users = 4 [ deprecated = true ];
repeated string allowed_groups = 5 [ deprecated = true ];
repeated string allowed_domains = 6 [ deprecated = true ];
map<string, google.protobuf.ListValue> allowed_idp_claims = 32 [ deprecated = true ];
string prefix = 7;
string path = 8;
@ -62,6 +64,7 @@ message Policy {
repeated string allowed_users = 3;
repeated string allowed_groups = 4;
repeated string allowed_domains = 5;
map<string, google.protobuf.ListValue> allowed_idp_claims = 7;
repeated string rego = 6;
}

View file

@ -7,7 +7,9 @@ import (
"github.com/golang/protobuf/ptypes"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/structpb"
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
)
@ -57,3 +59,13 @@ func Set(ctx context.Context, client databroker.DataBrokerServiceClient, s *Sess
}
return res, nil
}
// AddClaims adds the flattened claims to the session.
func (x *Session) AddClaims(claims identity.FlattenedClaims) {
if x.Claims == nil {
x.Claims = make(map[string]*structpb.ListValue)
}
for k, svs := range claims.ToPB() {
x.Claims[k] = svs
}
}

View file

@ -8,7 +8,7 @@ package session
import (
proto "github.com/golang/protobuf/proto"
any "github.com/golang/protobuf/ptypes/any"
_struct "github.com/golang/protobuf/ptypes/struct"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@ -174,13 +174,13 @@ 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]*any.Any `protobuf:"bytes,8,rep,name=claims,proto3" json:"claims,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
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"`
}
func (x *Session) Reset() {
@ -257,7 +257,7 @@ func (x *Session) GetOauthToken() *OAuthToken {
return nil
}
func (x *Session) GetClaims() map[string]*any.Any {
func (x *Session) GetClaims() map[string]*_struct.ListValue {
if x != nil {
return x.Claims
}
@ -268,60 +268,60 @@ var File_session_proto protoreflect.FileDescriptor
var file_session_proto_rawDesc = []byte{
0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaf, 0x01, 0x0a, 0x07, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74,
0x18, 0x03, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 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, 0x22, 0xae, 0x01, 0x0a, 0x0a, 0x4f, 0x41, 0x75, 0x74, 0x68,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63,
0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f,
0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72,
0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 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, 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, 0xf1, 0x02, 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, 0x69, 0x64, 0x12, 0x17, 0x0a,
0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65,
0x73, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41,
0x74, 0x12, 0x2b, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x44,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x07, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34,
0x0a, 0x0b, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4f, 0x41,
0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0a, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x18, 0x08,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x65, 0x73, 0x73, 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, 0x1a, 0x4f, 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, 0x2a, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79,
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,
0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63,
0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaf, 0x01, 0x0a, 0x07, 0x49, 0x44, 0x54, 0x6f,
0x6b, 0x65, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73,
0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73,
0x5f, 0x61, 0x74, 0x18, 0x03, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74,
0x12, 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, 0x22, 0xae, 0x01, 0x0a, 0x0a, 0x4f, 0x41,
0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65,
0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78,
0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 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, 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, 0xf7, 0x02, 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, 0x69, 0x64,
0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70,
0x69, 0x72, 0x65, 0x73, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72,
0x65, 0x73, 0x41, 0x74, 0x12, 0x2b, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x2e, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x07, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65,
0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x2e, 0x4f, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0a, 0x6f, 0x61, 0x75,
0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d,
0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x65, 0x73, 0x73, 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, 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,
}
var (
@ -343,7 +343,7 @@ var file_session_proto_goTypes = []interface{}{
(*Session)(nil), // 2: session.Session
nil, // 3: session.Session.ClaimsEntry
(*timestamp.Timestamp)(nil), // 4: google.protobuf.Timestamp
(*any.Any)(nil), // 5: google.protobuf.Any
(*_struct.ListValue)(nil), // 5: google.protobuf.ListValue
}
var file_session_proto_depIdxs = []int32{
4, // 0: session.IDToken.expires_at:type_name -> google.protobuf.Timestamp
@ -353,7 +353,7 @@ var file_session_proto_depIdxs = []int32{
0, // 4: session.Session.id_token:type_name -> session.IDToken
1, // 5: session.Session.oauth_token:type_name -> session.OAuthToken
3, // 6: session.Session.claims:type_name -> session.Session.ClaimsEntry
5, // 7: session.Session.ClaimsEntry.value:type_name -> google.protobuf.Any
5, // 7: session.Session.ClaimsEntry.value:type_name -> google.protobuf.ListValue
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name

View file

@ -3,8 +3,9 @@ syntax = "proto3";
package session;
option go_package = "github.com/pomerium/pomerium/pkg/grpc/session";
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";
message IDToken {
string issuer = 1;
@ -27,5 +28,5 @@ message Session {
google.protobuf.Timestamp expires_at = 4;
IDToken id_token = 6;
OAuthToken oauth_token = 7;
map<string, google.protobuf.Any> claims = 8;
map<string, google.protobuf.ListValue> claims = 9;
}

View file

@ -5,16 +5,16 @@ import (
context "context"
"fmt"
"github.com/golang/protobuf/ptypes"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/structpb"
"github.com/pomerium/pomerium/internal/protoutil"
"github.com/pomerium/pomerium/internal/identity"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
)
// Get gets a user from the databroker.
func Get(ctx context.Context, client databroker.DataBrokerServiceClient, userID string) (*User, error) {
any, _ := ptypes.MarshalAny(new(User))
any, _ := anypb.New(new(User))
res, err := client.Get(ctx, &databroker.GetRequest{
Type: any.GetTypeUrl(),
@ -25,18 +25,13 @@ func Get(ctx context.Context, client databroker.DataBrokerServiceClient, userID
}
var u User
err = ptypes.UnmarshalAny(res.GetRecord().GetData(), &u)
err = res.GetRecord().GetData().UnmarshalTo(&u)
if err != nil {
return nil, fmt.Errorf("error unmarshaling user from databroker: %w", err)
}
return &u, nil
}
// GetClaim gets a claim.
func (user *User) GetClaim(claim string) interface{} {
return protoutil.AnyToInterface(user.GetClaims()[claim])
}
// Set sets a user in the databroker.
func Set(ctx context.Context, client databroker.DataBrokerServiceClient, u *User) (*databroker.Record, error) {
any, _ := anypb.New(u)
@ -64,3 +59,13 @@ func SetServiceAccount(ctx context.Context, client databroker.DataBrokerServiceC
}
return res.GetRecord(), nil
}
// AddClaims adds the flattened claims to the user.
func (x *User) AddClaims(claims identity.FlattenedClaims) {
if x.Claims == nil {
x.Claims = make(map[string]*structpb.ListValue)
}
for k, svs := range claims.ToPB() {
x.Claims[k] = svs
}
}

View file

@ -8,7 +8,7 @@ package user
import (
proto "github.com/golang/protobuf/proto"
any "github.com/golang/protobuf/ptypes/any"
_struct "github.com/golang/protobuf/ptypes/struct"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@ -27,22 +27,77 @@ const (
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Claim struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"`
}
func (x *Claim) Reset() {
*x = Claim{}
if protoimpl.UnsafeEnabled {
mi := &file_user_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Claim) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Claim) ProtoMessage() {}
func (x *Claim) ProtoReflect() protoreflect.Message {
mi := &file_user_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Claim.ProtoReflect.Descriptor instead.
func (*Claim) Descriptor() ([]byte, []int) {
return file_user_proto_rawDescGZIP(), []int{0}
}
func (x *Claim) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *Claim) GetValues() []string {
if x != nil {
return x.Values
}
return nil
}
type User struct {
state protoimpl.MessageState
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"`
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email,omitempty"`
Claims map[string]*any.Any `protobuf:"bytes,8,rep,name=claims,proto3" json:"claims,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email,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"`
}
func (x *User) Reset() {
*x = User{}
if protoimpl.UnsafeEnabled {
mi := &file_user_proto_msgTypes[0]
mi := &file_user_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -55,7 +110,7 @@ func (x *User) String() string {
func (*User) ProtoMessage() {}
func (x *User) ProtoReflect() protoreflect.Message {
mi := &file_user_proto_msgTypes[0]
mi := &file_user_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -68,7 +123,7 @@ func (x *User) ProtoReflect() protoreflect.Message {
// Deprecated: Use User.ProtoReflect.Descriptor instead.
func (*User) Descriptor() ([]byte, []int) {
return file_user_proto_rawDescGZIP(), []int{0}
return file_user_proto_rawDescGZIP(), []int{1}
}
func (x *User) GetVersion() string {
@ -99,7 +154,7 @@ func (x *User) GetEmail() string {
return ""
}
func (x *User) GetClaims() map[string]*any.Any {
func (x *User) GetClaims() map[string]*_struct.ListValue {
if x != nil {
return x.Claims
}
@ -120,7 +175,7 @@ type ServiceAccount struct {
func (x *ServiceAccount) Reset() {
*x = ServiceAccount{}
if protoimpl.UnsafeEnabled {
mi := &file_user_proto_msgTypes[1]
mi := &file_user_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -133,7 +188,7 @@ func (x *ServiceAccount) String() string {
func (*ServiceAccount) ProtoMessage() {}
func (x *ServiceAccount) ProtoReflect() protoreflect.Message {
mi := &file_user_proto_msgTypes[1]
mi := &file_user_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -146,7 +201,7 @@ func (x *ServiceAccount) ProtoReflect() protoreflect.Message {
// Deprecated: Use ServiceAccount.ProtoReflect.Descriptor instead.
func (*ServiceAccount) Descriptor() ([]byte, []int) {
return file_user_proto_rawDescGZIP(), []int{1}
return file_user_proto_rawDescGZIP(), []int{2}
}
func (x *ServiceAccount) GetId() string {
@ -181,39 +236,42 @@ var File_user_proto protoreflect.FileDescriptor
var file_user_proto_rawDesc = []byte{
0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73,
0x65, 0x72, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdb,
0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 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, 0x69,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x2e, 0x0a, 0x06, 0x63,
0x6c, 0x61, 0x69, 0x6d, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x75, 0x73,
0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x1a, 0x4f, 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, 0x2a, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e,
0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xad, 0x01, 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, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69,
0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65,
0x73, 0x41, 0x74, 0x12, 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,
0x65, 0x72, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x22, 0x31, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 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, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69,
0x6c, 0x12, 0x2e, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x43, 0x6c,
0x61, 0x69, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d,
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, 0x22, 0xad, 0x01, 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,
0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f,
0x61, 0x74, 0x18, 0x03, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12,
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,
}
var (
@ -228,19 +286,20 @@ func file_user_proto_rawDescGZIP() []byte {
return file_user_proto_rawDescData
}
var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_user_proto_goTypes = []interface{}{
(*User)(nil), // 0: user.User
(*ServiceAccount)(nil), // 1: user.ServiceAccount
nil, // 2: user.User.ClaimsEntry
(*timestamp.Timestamp)(nil), // 3: google.protobuf.Timestamp
(*any.Any)(nil), // 4: google.protobuf.Any
(*Claim)(nil), // 0: user.Claim
(*User)(nil), // 1: user.User
(*ServiceAccount)(nil), // 2: user.ServiceAccount
nil, // 3: user.User.ClaimsEntry
(*timestamp.Timestamp)(nil), // 4: google.protobuf.Timestamp
(*_struct.ListValue)(nil), // 5: google.protobuf.ListValue
}
var file_user_proto_depIdxs = []int32{
2, // 0: user.User.claims:type_name -> user.User.ClaimsEntry
3, // 1: user.ServiceAccount.expires_at:type_name -> google.protobuf.Timestamp
3, // 2: user.ServiceAccount.issued_at:type_name -> google.protobuf.Timestamp
4, // 3: user.User.ClaimsEntry.value:type_name -> google.protobuf.Any
3, // 0: user.User.claims:type_name -> user.User.ClaimsEntry
4, // 1: user.ServiceAccount.expires_at:type_name -> google.protobuf.Timestamp
4, // 2: user.ServiceAccount.issued_at:type_name -> google.protobuf.Timestamp
5, // 3: user.User.ClaimsEntry.value:type_name -> google.protobuf.ListValue
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
@ -255,7 +314,7 @@ func file_user_proto_init() {
}
if !protoimpl.UnsafeEnabled {
file_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*User); i {
switch v := v.(*Claim); i {
case 0:
return &v.state
case 1:
@ -267,6 +326,18 @@ func file_user_proto_init() {
}
}
file_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*User); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServiceAccount); i {
case 0:
return &v.state
@ -285,7 +356,7 @@ func file_user_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_user_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},

View file

@ -3,15 +3,20 @@ syntax = "proto3";
package user;
option go_package = "github.com/pomerium/pomerium/pkg/grpc/user";
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";
message Claim {
string key = 1;
repeated string values = 2;
}
message User {
string version = 1;
string id = 2;
string name = 3;
string email = 4;
map<string, google.protobuf.Any> claims = 8;
map<string, google.protobuf.ListValue> claims = 9;
}
message ServiceAccount {

View file

@ -4,28 +4,21 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/pomerium/pomerium/pkg/grpc/user"
)
func TestScrubber(t *testing.T) {
s := NewScrubber("pomerium").Whitelist("user.User", "version", "id")
c1, _ := anypb.New(wrapperspb.String("claim1"))
u := s.ScrubProto(&user.User{
Version: "v1",
Id: "u1",
Name: "name1",
Email: "user@example.com",
Claims: map[string]*anypb.Any{
"key1": c1,
},
}).(*user.User)
assert.Equal(t, "v1", u.Version)
assert.Equal(t, "u1", u.Id)
assert.Equal(t, s.hmacString("name1"), u.Name)
assert.Equal(t, s.hmacString("user@example.com"), u.Email)
assert.Equal(t, s.hmacString("claim1"), u.GetClaim("key1"))
}

108
pkg/protoutil/any.go Normal file
View file

@ -0,0 +1,108 @@
package protoutil
import (
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
// ToAny converts any type into an any value.
func ToAny(value interface{}) *anypb.Any {
switch v := value.(type) {
case bool:
return NewAnyBool(v)
case []byte:
return NewAnyBytes(v)
case float32:
return NewAnyFloat(v)
case float64:
return NewAnyDouble(v)
case int:
return NewAnyInt64(int64(v))
case int8:
return NewAnyInt32(int32(v))
case int16:
return NewAnyInt32(int32(v))
case int32:
return NewAnyInt32(v)
case int64:
return NewAnyInt64(v)
case string:
return NewAnyString(v)
case uint:
return NewAnyUInt64(uint64(v))
case uint8:
return NewAnyUInt32(uint32(v))
case uint16:
return NewAnyUInt32(uint32(v))
case uint32:
return NewAnyUInt32(v)
case uint64:
return NewAnyUInt64(v)
default:
a, err := anypb.New(ToStruct(value))
if err != nil {
return NewAnyNull()
}
return a
}
}
// NewAnyBool creates a new any type from a bool.
func NewAnyBool(v bool) *anypb.Any {
a, _ := anypb.New(wrapperspb.Bool(v))
return a
}
// NewAnyBytes creates a new any type from bytes.
func NewAnyBytes(v []byte) *anypb.Any {
a, _ := anypb.New(wrapperspb.Bytes(v))
return a
}
// NewAnyDouble creates a new any type from a float64.
func NewAnyDouble(v float64) *anypb.Any {
a, _ := anypb.New(wrapperspb.Double(v))
return a
}
// NewAnyFloat creates a new any type from a float32.
func NewAnyFloat(v float32) *anypb.Any {
a, _ := anypb.New(wrapperspb.Float(v))
return a
}
// NewAnyInt64 creates a new any type from an int64.
func NewAnyInt64(v int64) *anypb.Any {
a, _ := anypb.New(wrapperspb.Int64(v))
return a
}
// NewAnyInt32 creates a new any type from an int32.
func NewAnyInt32(v int32) *anypb.Any {
a, _ := anypb.New(wrapperspb.Int32(v))
return a
}
// NewAnyNull creates a new any type from a null struct.
func NewAnyNull() *anypb.Any {
a, _ := anypb.New(NewStructNull())
return a
}
// NewAnyString creates a new any type from a string.
func NewAnyString(v string) *anypb.Any {
a, _ := anypb.New(wrapperspb.String(v))
return a
}
// NewAnyUInt64 creates a new any type from an uint64.
func NewAnyUInt64(v uint64) *anypb.Any {
a, _ := anypb.New(wrapperspb.UInt64(v))
return a
}
// NewAnyUInt32 creates a new any type from an uint32.
func NewAnyUInt32(v uint32) *anypb.Any {
a, _ := anypb.New(wrapperspb.UInt32(v))
return a
}

87
pkg/protoutil/any_test.go Normal file
View file

@ -0,0 +1,87 @@
package protoutil
import (
"testing"
"github.com/pomerium/pomerium/internal/testutil"
)
func TestToAny(t *testing.T) {
testCases := []struct {
name string
value interface{}
expect string
}{
{"bool", true, `{
"@type": "type.googleapis.com/google.protobuf.BoolValue",
"value": true
}`},
{"float64", 1.2345, `{
"@type": "type.googleapis.com/google.protobuf.DoubleValue",
"value": 1.2345
}`},
{"float32", float32(0.4000000059604645), `{
"@type": "type.googleapis.com/google.protobuf.FloatValue",
"value": 0.4
}`},
{"int", int(1), `{
"@type": "type.googleapis.com/google.protobuf.Int64Value",
"value": "1"
}`},
{"int8", int8(1), `{
"@type": "type.googleapis.com/google.protobuf.Int32Value",
"value": 1
}`},
{"int16", int16(1), `{
"@type": "type.googleapis.com/google.protobuf.Int32Value",
"value": 1
}`},
{"int32", int32(1), `{
"@type": "type.googleapis.com/google.protobuf.Int32Value",
"value": 1
}`},
{"int64", int64(1), `{
"@type": "type.googleapis.com/google.protobuf.Int64Value",
"value": "1"
}`},
{"string", "test", `{
"@type": "type.googleapis.com/google.protobuf.StringValue",
"value": "test"
}`},
{"uint", uint(1), `{
"@type": "type.googleapis.com/google.protobuf.UInt64Value",
"value": "1"
}`},
{"uint8", uint8(1), `{
"@type": "type.googleapis.com/google.protobuf.UInt32Value",
"value": 1
}`},
{"uint16", uint16(1), `{
"@type": "type.googleapis.com/google.protobuf.UInt32Value",
"value": 1
}`},
{"uint32", uint32(1), `{
"@type": "type.googleapis.com/google.protobuf.UInt32Value",
"value": 1
}`},
{"uint64", uint64(1), `{
"@type": "type.googleapis.com/google.protobuf.UInt64Value",
"value": "1"
}`},
{"[]interface{}", []interface{}{1, 2, 3, 4}, `{
"@type": "type.googleapis.com/google.protobuf.Value",
"value": [1,2,3,4]
}`},
{"map[string]interface{}", map[string]interface{}{"k1": "v1", "k2": "v2"}, `{
"@type": "type.googleapis.com/google.protobuf.Value",
"value": {"k1": "v1", "k2": "v2"}
}`},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
actual := ToAny(tc.value)
testutil.AssertProtoJSONEqual(t, tc.expect, actual)
})
}
}

108
pkg/protoutil/struct.go Normal file
View file

@ -0,0 +1,108 @@
// Package protoutil contains functions for working with protobuf types.
package protoutil
import (
"fmt"
"reflect"
"google.golang.org/protobuf/types/known/structpb"
)
// ToStruct converts any value into a structpb Value.
func ToStruct(value interface{}) *structpb.Value {
if value == nil {
return NewStructNull()
}
switch v := value.(type) {
case bool:
return NewStructBool(v)
case float64:
return NewStructNumber(v)
case float32:
return NewStructNumber(float64(v))
case int:
return NewStructNumber(float64(v))
case int8:
return NewStructNumber(float64(v))
case int16:
return NewStructNumber(float64(v))
case int32:
return NewStructNumber(float64(v))
case int64:
return NewStructNumber(float64(v))
case string:
return NewStructString(v)
case uint:
return NewStructNumber(float64(v))
case uint8:
return NewStructNumber(float64(v))
case uint16:
return NewStructNumber(float64(v))
case uint32:
return NewStructNumber(float64(v))
case uint64:
return NewStructNumber(float64(v))
}
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice:
svs := make([]*structpb.Value, rv.Len())
for i := range svs {
svs[i] = ToStruct(rv.Index(i).Interface())
}
return NewStructList(svs...)
case reflect.Map:
svm := make(map[string]*structpb.Value)
iter := rv.MapRange()
for iter.Next() {
svm[fmt.Sprint(iter.Key().Interface())] = ToStruct(iter.Value().Interface())
}
return NewStructMap(svm)
}
return NewStructNull()
}
// NewStructBool creates a new bool struct value.
func NewStructBool(v bool) *structpb.Value {
return &structpb.Value{
Kind: &structpb.Value_BoolValue{BoolValue: v},
}
}
// NewStructMap creates a new map struct value.
func NewStructMap(v map[string]*structpb.Value) *structpb.Value {
return &structpb.Value{
Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: v}},
}
}
// NewStructNull creates a new null struct value.
func NewStructNull() *structpb.Value {
return &structpb.Value{
Kind: &structpb.Value_NullValue{},
}
}
// NewStructNumber creates a new number struct value.
func NewStructNumber(v float64) *structpb.Value {
return &structpb.Value{
Kind: &structpb.Value_NumberValue{NumberValue: v},
}
}
// NewStructList creates a new list struct value.
func NewStructList(vs ...*structpb.Value) *structpb.Value {
return &structpb.Value{
Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: vs}},
}
}
// NewStructString creates a new string struct value.
func NewStructString(v string) *structpb.Value {
return &structpb.Value{
Kind: &structpb.Value_StringValue{StringValue: v},
}
}

View file

@ -0,0 +1,39 @@
package protoutil
import (
"testing"
"github.com/pomerium/pomerium/internal/testutil"
)
func TestToValue(t *testing.T) {
testCases := []struct {
name string
value interface{}
expect string
}{
{"bool", true, "true"},
{"float64", 1.2345, "1.2345"},
{"float32", float32(0.4000000059604645), "0.4000000059604645"},
{"int", int(1), "1"},
{"int8", int8(1), "1"},
{"int16", int16(1), "1"},
{"int32", int32(1), "1"},
{"int64", int64(1), "1"},
{"string", "test", `"test"`},
{"uint", uint(1), "1"},
{"uint8", uint8(1), "1"},
{"uint16", uint16(1), "1"},
{"uint32", uint32(1), "1"},
{"uint64", uint64(1), "1"},
{"[]interface{}", []interface{}{1, 2, 3, 4}, `[1,2,3,4]`},
{"map[string]interface{}", map[string]interface{}{"k1": "v1", "k2": "v2"}, `{"k1":"v1","k2":"v2"}`},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
actual := ToStruct(tc.value)
testutil.AssertProtoJSONEqual(t, tc.expect, actual)
})
}
}