diff --git a/constants.go b/constants.go index 436b122eaf30a..ed60a94c50295 100644 --- a/constants.go +++ b/constants.go @@ -445,6 +445,10 @@ const ( // allowed kubernetes groups TraitKubeGroups = "kubernetes_groups" + // TraitKubeUsers is the name the role variable used to store + // allowed kubernetes users + TraitKubeUsers = "kubernetes_users" + // TraitInternalLoginsVariable is the variable used to store allowed // logins for local accounts. TraitInternalLoginsVariable = "{{internal.logins}}" diff --git a/e b/e index 1e87639954fc0..0b124e0452348 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit 1e87639954fc0f50f1422119b9aabc81c47331ef +Subproject commit 0b124e0452348926f139c3787dfae85f59e72df8 diff --git a/integration/kube_integration_test.go b/integration/kube_integration_test.go index 9caedae4b42ef..a1440cb94f254 100644 --- a/integration/kube_integration_test.go +++ b/integration/kube_integration_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016-2019 Gravitational, Inc. +Copyright 2016-2020 Gravitational, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -155,6 +155,7 @@ func (s *KubeSuite) TestKubeExec(c *check.C) { Allow: services.RoleConditions{ Logins: []string{username}, KubeGroups: []string{teleport.KubeSystemMasters}, + KubeUsers: []string{"alice@example.com"}, }, }) t.AddUserWithRole(username, role) @@ -166,7 +167,8 @@ func (s *KubeSuite) TestKubeExec(c *check.C) { c.Assert(err, check.IsNil) defer t.Stop(true) - // impersonating client requests will be denied + // impersonating client requests will be denied if the headers + // are referencing users or groups not allowed by the existing roles impersonatingProxyClient, impersonatingProxyClientConfig, err := kubeProxyClient(kubeProxyConfig{ t: t, username: username, @@ -180,6 +182,23 @@ func (s *KubeSuite) TestKubeExec(c *check.C) { }) c.Assert(err, check.NotNil) + // scoped client requests will be allowed, as long as the impersonation headers + // are referencing users and groups allowed by existing roles + scopedProxyClient, scopedProxyClientConfig, err := kubeProxyClient(kubeProxyConfig{ + t: t, + username: username, + impersonation: &rest.ImpersonationConfig{ + UserName: role.GetKubeUsers(services.Allow)[0], + Groups: role.GetKubeGroups(services.Allow), + }, + }) + c.Assert(err, check.IsNil) + + _, err = scopedProxyClient.CoreV1().Pods(kubeSystemNamespace).List(metav1.ListOptions{ + LabelSelector: kubeDNSLabels.AsSelector().String(), + }) + c.Assert(err, check.IsNil) + // set up kube configuration using proxy proxyClient, proxyClientConfig, err := kubeProxyClient(kubeProxyConfig{t: t, username: username}) c.Assert(err, check.IsNil) @@ -266,6 +285,68 @@ loop: }) c.Assert(err, check.NotNil) c.Assert(err.Error(), check.Matches, ".*impersonation request has been denied.*") + + // scoped kube exec is allowed, impersonation headers + // are allowed by the role + term = NewTerminal(250) + term.Type("\aecho hi\n\r\aexit\n\r\a") + out = &bytes.Buffer{} + err = kubeExec(scopedProxyClientConfig, kubeExecArgs{ + podName: pod.Name, + podNamespace: pod.Namespace, + container: kubeDNSContainer, + command: []string{"/bin/sh"}, + stdout: out, + tty: true, + stdin: &term, + }) + c.Assert(err, check.IsNil) +} + +// TestKubeDeny makes sure that deny rule conflicting with allow +// rule takes precendence +func (s *KubeSuite) TestKubeDeny(c *check.C) { + tconf := s.teleKubeConfig(Host) + + t := NewInstance(InstanceConfig{ + ClusterName: Site, + HostID: HostID, + NodeName: Host, + Ports: s.ports.PopIntSlice(5), + Priv: s.priv, + Pub: s.pub, + }) + + username := s.me.Username + role, err := services.NewRole("kubemaster", services.RoleSpecV3{ + Allow: services.RoleConditions{ + Logins: []string{username}, + KubeGroups: []string{teleport.KubeSystemMasters}, + KubeUsers: []string{"alice@example.com"}, + }, + Deny: services.RoleConditions{ + KubeGroups: []string{teleport.KubeSystemMasters}, + KubeUsers: []string{"alice@example.com"}, + }, + }) + t.AddUserWithRole(username, role) + + err = t.CreateEx(nil, tconf) + c.Assert(err, check.IsNil) + + err = t.Start() + c.Assert(err, check.IsNil) + defer t.Stop(true) + + // set up kube configuration using proxy + proxyClient, _, err := kubeProxyClient(kubeProxyConfig{t: t, username: username}) + c.Assert(err, check.IsNil) + + // try get request to fetch available pods + _, err = proxyClient.CoreV1().Pods(kubeSystemNamespace).List(metav1.ListOptions{ + LabelSelector: kubeDNSLabels.AsSelector().String(), + }) + c.Assert(err, check.NotNil) } // TestKubePortForward tests kubernetes port forwarding diff --git a/lib/auth/github.go b/lib/auth/github.go index e3fc86dd825f7..cc9749afe4410 100644 --- a/lib/auth/github.go +++ b/lib/auth/github.go @@ -1,5 +1,5 @@ /* -Copyright 2017 Gravitational, Inc. +Copyright 2017-2020 Gravitational, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -289,9 +289,12 @@ type createUserParams struct { // logins is the list of *nix logins. logins []string - // kubeGroups is the list of Kubernetes this user belongs to. + // kubeGroups is the list of Kubernetes groups this user belongs to. kubeGroups []string + // kubeUsers is the list of Kubernetes users this user belongs to. + kubeUsers []string + // roles is the list of roles this user is assigned to. roles []string @@ -309,14 +312,14 @@ func (s *AuthServer) calculateGithubUser(connector services.GithubConnector, cla } // Calculate logins, kubegroups, roles, and traits. - p.logins, p.kubeGroups = connector.MapClaims(*claims) + p.logins, p.kubeGroups, p.kubeUsers = connector.MapClaims(*claims) if len(p.logins) == 0 { return nil, trace.BadParameter( "user %q does not belong to any teams configured in %q connector", claims.Username, connector.GetName()) } p.roles = modules.GetModules().RolesFromLogins(p.logins) - p.traits = modules.GetModules().TraitsFromLogins(p.logins, p.kubeGroups) + p.traits = modules.GetModules().TraitsFromLogins(p.logins, p.kubeGroups, p.kubeUsers) // Pick smaller for role: session TTL from role or requested TTL. roles, err := services.FetchRoles(p.roles, s.Access, p.traits) diff --git a/lib/auth/kube.go b/lib/auth/kube.go index cfa907b8f3bbf..56b1c58b4edf5 100644 --- a/lib/auth/kube.go +++ b/lib/auth/kube.go @@ -98,7 +98,7 @@ func (s *AuthServer) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) { // extract and encode the kubernetes groups of the authenticated // user in the newly issued certificate - kubernetesGroups, err := roles.CheckKubeGroups(0) + kubernetesGroups, kubernetesUsers, err := roles.CheckKubeGroupsAndUsers(0) if err != nil { return nil, trace.Wrap(err) } @@ -129,6 +129,7 @@ func (s *AuthServer) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) { // otherwise proxies can generate certs for any user. Usage: []string{teleport.UsageKubeOnly}, KubernetesGroups: kubernetesGroups, + KubernetesUsers: kubernetesUsers, } subject, err := identity.Subject() if err != nil { diff --git a/lib/auth/middleware.go b/lib/auth/middleware.go index 7ca09d61ad5c0..23d0965fb1275 100644 --- a/lib/auth/middleware.go +++ b/lib/auth/middleware.go @@ -261,6 +261,7 @@ func (a *AuthMiddleware) GetUser(r *http.Request) (IdentityGetter, error) { Username: identity.Username, Principals: identity.Principals, KubernetesGroups: identity.KubernetesGroups, + KubernetesUsers: identity.KubernetesUsers, RemoteRoles: identity.Groups, Identity: *identity, }, nil diff --git a/lib/auth/permissions.go b/lib/auth/permissions.go index b5c26db663672..01e76264c32e5 100644 --- a/lib/auth/permissions.go +++ b/lib/auth/permissions.go @@ -147,15 +147,17 @@ func (a *authorizer) authorizeRemoteUser(u RemoteUser) (*AuthContext, error) { return nil, trace.AccessDenied("no roles mapped for remote user %q from cluster %q", u.Username, u.ClusterName) } // Set "logins" trait and "kubernetes_groups" for the remote user. This allows Teleport to work by - // passing exact logins and kubernetes groups to the remote cluster. Note that claims (OIDC/SAML) + // passing exact logins, kubernetes groups and users to the remote cluster. Note that claims (OIDC/SAML) // are not passed, but rather the exact logins, this is done to prevent // leaking too much of identity to the remote cluster, and instead of focus // on main cluster's interpretation of this identity traits := map[string][]string{ teleport.TraitLogins: u.Principals, teleport.TraitKubeGroups: u.KubernetesGroups, + teleport.TraitKubeUsers: u.KubernetesUsers, } - log.Debugf("Mapped roles %v of remote user %q to local roles %v and traits %v.", u.RemoteRoles, u.Username, roleNames, traits) + log.Debugf("Mapped roles %v of remote user %q to local roles %v and traits %v.", + u.RemoteRoles, u.Username, roleNames, traits) checker, err := services.FetchRoles(roleNames, a.access, traits) if err != nil { return nil, trace.Wrap(err) @@ -589,6 +591,9 @@ type RemoteUser struct { // KubernetesGroups is a list of Kubernetes groups KubernetesGroups []string `json:"kubernetes_groups"` + // KubernetesUsers is a list of Kubernetes users + KubernetesUsers []string `json:"kubernetes_users"` + // Identity is source x509 used to build this role Identity tlsca.Identity } diff --git a/lib/kube/proxy/forwarder.go b/lib/kube/proxy/forwarder.go index e9a180c46fc49..fff7832ad45ee 100644 --- a/lib/kube/proxy/forwarder.go +++ b/lib/kube/proxy/forwarder.go @@ -221,7 +221,8 @@ func (f *Forwarder) Close() error { // contains information about user, target cluster and authenticated groups type authContext struct { auth.AuthContext - kubeGroups []string + kubeGroups map[string]struct{} + kubeUsers map[string]struct{} cluster cluster clusterConfig services.ClusterConfig // clientIdleTimeout sets information on client idle timeout @@ -234,13 +235,13 @@ type authContext struct { } func (c authContext) String() string { - return fmt.Sprintf("user: %v, groups: %v, cluster: %v", c.User.GetName(), c.kubeGroups, c.cluster.GetName()) + return fmt.Sprintf("user: %v, users: %v, groups: %v, cluster: %v", c.User.GetName(), c.kubeUsers, c.kubeGroups, c.cluster.GetName()) } func (c *authContext) key() string { // it is important that the context key contains user, kubernetes groups and certificate expiry, // so that new logins with different parameters will not reuse this context - return fmt.Sprintf("%v:%v:%v:%v", c.cluster.GetName(), c.User.GetName(), c.kubeGroups, c.disconnectExpiredCert.UTC().Unix()) + return fmt.Sprintf("%v:%v:%v:%v:%v", c.cluster.GetName(), c.User.GetName(), c.kubeUsers, c.kubeGroups, c.disconnectExpiredCert.UTC().Unix()) } // cluster represents cluster information, name of the cluster @@ -357,11 +358,17 @@ func (f *Forwarder) setupContext(ctx auth.AuthContext, req *http.Request, isRemo sessionTTL := roles.AdjustSessionTTL(time.Hour) // check signing TTL and return a list of allowed logins - kubeGroups, err := roles.CheckKubeGroups(sessionTTL) + kubeGroups, kubeUsers, err := roles.CheckKubeGroupsAndUsers(sessionTTL) if err != nil { return nil, trace.Wrap(err) } + // By default, if no kubernetes_users is set (which will be a majority), + // user will impersonate themselves, which is the backwards-compatible behavior. + if len(kubeUsers) == 0 { + kubeUsers = append(kubeUsers, ctx.User.GetName()) + } + // KubeSystemAuthenticated is a builtin group that allows // any user to access common API methods, e.g. discovery methods // required for initial client usage, without it, restricted user's @@ -376,7 +383,6 @@ func (f *Forwarder) setupContext(ctx auth.AuthContext, req *http.Request, isRemo return nil, trace.Wrap(err) } if ctx.Identity.RouteToCluster != "" { - f.Debugf("Client certificate of %v has requested routing to a cluster: %v.", ctx.User.GetName(), ctx.Identity.RouteToCluster) targetCluster, err = f.Tunnel.GetSite(ctx.Identity.RouteToCluster) if err != nil { return nil, trace.Wrap(err) @@ -411,7 +417,8 @@ func (f *Forwarder) setupContext(ctx auth.AuthContext, req *http.Request, isRemo clientIdleTimeout: roles.AdjustClientIdleTimeout(clusterConfig.GetClientIdleTimeout()), sessionTTL: sessionTTL, AuthContext: ctx, - kubeGroups: kubeGroups, + kubeGroups: utils.StringsSet(kubeGroups), + kubeUsers: utils.StringsSet(kubeUsers), clusterConfig: clusterConfig, cluster: cluster{ remoteAddr: utils.NetAddr{AddrNetwork: "tcp", Addr: req.RemoteAddr}, @@ -634,11 +641,21 @@ func (f *Forwarder) portForward(ctx *authContext, w http.ResponseWriter, req *ht return nil, nil } +const ( + // ImpersonateHeaderPrefix is K8s impersonation prefix for impersonation feature: + // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation + ImpersonateHeaderPrefix = "Impersonate-" + // ImpersonateUserHeader is impersonation header for users + ImpersonateUserHeader = "Impersonate-User" + // ImpersonateGroupHeader is K8s impersonation header for user + ImpersonateGroupHeader = "Impersonate-Group" + // ImpersonationRequestDeniedMessage is access denied message for impersonation + ImpersonationRequestDeniedMessage = "impersonation request has been denied" +) + func (f *Forwarder) setupForwardingHeaders(ctx *authContext, sess *clusterSession, req *http.Request) error { - for header := range req.Header { - if strings.HasPrefix(header, "Impersonate-") { - return trace.AccessDenied("impersonation request has been denied") - } + if err := setupImpersonationHeaders(f.Entry, ctx, req.Header, f.creds.cfg.BearerToken); err != nil { + return trace.Wrap(err) } // Setup scheme, override target URL to the destination address @@ -652,16 +669,89 @@ func (f *Forwarder) setupForwardingHeaders(ctx *authContext, sess *clusterSessio req.Header.Add("X-Forwarded-Host", req.Host) req.Header.Add("X-Forwarded-Path", req.URL.Path) + return nil +} + +// setupImpersonationHeaders sets up Impersonate-User and Impersonate-Group headers +func setupImpersonationHeaders(log log.FieldLogger, ctx *authContext, headers http.Header, bearerToken string) error { + var impersonateUser string + var impersonateGroups []string + for header, values := range headers { + if !strings.HasPrefix(header, "Impersonate-") { + continue + } + switch header { + case ImpersonateUserHeader: + if impersonateUser != "" { + return trace.AccessDenied("%v, user already specified to %q", ImpersonationRequestDeniedMessage, impersonateUser) + } + if len(values) == 0 || len(values) > 1 { + return trace.AccessDenied("%v, invalid user header %q", ImpersonationRequestDeniedMessage, values) + } + impersonateUser = values[0] + if _, ok := ctx.kubeUsers[impersonateUser]; !ok { + return trace.AccessDenied("%v, user header %q is not allowed in roles", ImpersonationRequestDeniedMessage, impersonateUser) + } + case ImpersonateGroupHeader: + for _, group := range values { + if _, ok := ctx.kubeGroups[group]; !ok { + return trace.AccessDenied("%v, group header %q value is not allowed in roles", ImpersonationRequestDeniedMessage, group) + } + impersonateGroups = append(impersonateGroups, group) + } + default: + return trace.AccessDenied("%v, unsupported impersonation header %q", ImpersonationRequestDeniedMessage, header) + } + } + + impersonateGroups = utils.Deduplicate(impersonateGroups) + + // By default, if no kubernetes_users is set (which will be a majority), + // user will impersonate themselves, which is the backwards-compatible behavior. + // + // As long as at least one `kubernetes_users` is set, the forwarder will start + // limiting the list of users allowed by the client to impersonate. + // + // If the users' role set does not include actual user name, it will be rejected, + // otherwise there will be no way to exclude the user from the list). + // + // If the `kubernetes_users` role set includes only one user + // (quite frequently that's the real intent), teleport will default to it, + // otherwise it will refuse to select. + // + // This will enable the use case when `kubernetes_users` has just one field to + // link the user identity with the IAM role, for example `IAM#{{external.email}}` + // + if impersonateUser == "" { + switch len(ctx.kubeUsers) { + // this is currently not possible as kube users have at least one + // user (user name), but in case if someone breaks it, catch here + case 0: + return trace.AccessDenied("assumed at least one user to be present") + // if there is deterministic choice, make it to improve user experience + case 1: + for user := range ctx.kubeUsers { + impersonateUser = user + break + } + default: + return trace.AccessDenied( + "please select a user to impersonate, refusing to select a user due to several kuberenetes_users set up for this user") + } + } + if len(impersonateGroups) == 0 { + for group := range ctx.kubeGroups { + impersonateGroups = append(impersonateGroups, group) + } + } + if !ctx.cluster.isRemote { - req.Header.Add("Impersonate-User", ctx.User.GetName()) - f.Debugf("Impersonate User: %v", ctx.User) - for _, group := range ctx.kubeGroups { - req.Header.Add("Impersonate-Group", group) - f.Debugf("Impersonate Group: %v", group) + headers.Add("Impersonate-User", impersonateUser) + for _, group := range impersonateGroups { + headers.Add("Impersonate-Group", group) } - if f.creds.cfg.BearerToken != "" { - f.Debugf("Using Bearer Token Auth") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", f.creds.cfg.BearerToken)) + if bearerToken != "" { + headers.Set("Authorization", fmt.Sprintf("Bearer %v", bearerToken)) } } return nil @@ -993,7 +1083,7 @@ func (f *Forwarder) requestCertificate(ctx authContext) (*bundle, error) { csr := &x509.CertificateRequest{ Subject: pkix.Name{ CommonName: ctx.User.GetName(), - Organization: ctx.kubeGroups, + Organization: utils.StringsSliceFromSet(ctx.kubeGroups), }, } csrBytes, err := x509.CreateCertificateRequest(rand.Reader, csr, privateKey) diff --git a/lib/kube/proxy/roundtrip.go b/lib/kube/proxy/roundtrip.go index 437230ec4cb71..57f9686ed67a4 100644 --- a/lib/kube/proxy/roundtrip.go +++ b/lib/kube/proxy/roundtrip.go @@ -32,6 +32,7 @@ import ( "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/trace" log "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -189,18 +190,8 @@ func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) header.Add(httpstream.HeaderUpgrade, streamspdy.HeaderSpdy31) - // impersonation for remote clusters is handled by remote proxies - if !s.authCtx.cluster.isRemote { - header.Add("Impersonate-User", s.authCtx.User.GetName()) - log.Debugf("Impersonate User: %v", s.authCtx.User) - for _, group := range s.authCtx.kubeGroups { - header.Add("Impersonate-Group", group) - log.Debugf("Impersonate Group: %v", group) - } - if s.bearerToken != "" { - log.Debugf("Using Bearer Token Auth") - header.Set("Authorization", fmt.Sprintf("Bearer %v", s.bearerToken)) - } + if err := setupImpersonationHeaders(log.StandardLogger(), &s.authCtx, header, s.bearerToken); err != nil { + return nil, trace.Wrap(err) } var ( diff --git a/lib/modules/modules.go b/lib/modules/modules.go index d6f795cf47c52..2a86c094ad6a2 100644 --- a/lib/modules/modules.go +++ b/lib/modules/modules.go @@ -44,7 +44,7 @@ type Modules interface { RolesFromLogins([]string) []string // TraitsFromLogins returns traits for external user based on the logins // and kubernetes groups extracted from the connector - TraitsFromLogins([]string, []string) map[string][]string + TraitsFromLogins(logins []string, kubeGroups []string, kubeUsers []string) map[string][]string // SupportsKubernetes returns true if this cluster supports kubernetes SupportsKubernetes() bool // IsBoringBinary checks if the binary was compiled with BoringCrypto. @@ -107,10 +107,11 @@ func (p *defaultModules) RolesFromLogins(logins []string) []string { // extracted from the connector // // By default logins are treated as allowed logins user traits. -func (p *defaultModules) TraitsFromLogins(logins []string, kubeGroups []string) map[string][]string { +func (p *defaultModules) TraitsFromLogins(logins []string, kubeGroups, kubeUsers []string) map[string][]string { return map[string][]string{ teleport.TraitLogins: logins, teleport.TraitKubeGroups: kubeGroups, + teleport.TraitKubeUsers: kubeUsers, } } diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go index 8dd4d403510f2..1d1b75a2bec3b 100644 --- a/lib/modules/modules_test.go +++ b/lib/modules/modules_test.go @@ -44,10 +44,11 @@ func (s *ModulesSuite) TestDefaultModules(c *check.C) { roles := GetModules().RolesFromLogins([]string{"root"}) c.Assert(roles, check.DeepEquals, []string{teleport.AdminRoleName}) - traits := GetModules().TraitsFromLogins([]string{"root"}, []string{"system:masters"}) + traits := GetModules().TraitsFromLogins([]string{"root"}, []string{"system:masters"}, []string{"alice@example.com"}) c.Assert(traits, check.DeepEquals, map[string][]string{ teleport.TraitLogins: []string{"root"}, teleport.TraitKubeGroups: []string{"system:masters"}, + teleport.TraitKubeUsers: []string{"alice@example.com"}, }) isBoring := GetModules().IsBoringBinary() @@ -66,7 +67,7 @@ func (s *ModulesSuite) TestTestModules(c *check.C) { roles := GetModules().RolesFromLogins([]string{"root"}) c.Assert(roles, check.DeepEquals, []string{"root"}) - traits := GetModules().TraitsFromLogins([]string{"root"}, []string{"system:masters"}) + traits := GetModules().TraitsFromLogins([]string{"root"}, []string{"system:masters"}, []string{"alice@example.com"}) c.Assert(traits, check.IsNil) isBoring := GetModules().IsBoringBinary() @@ -97,7 +98,7 @@ func (p *testModules) RolesFromLogins(logins []string) []string { return logins } -func (p *testModules) TraitsFromLogins(logins []string, kubeGroups []string) map[string][]string { +func (p *testModules) TraitsFromLogins(logins []string, kubeGroups []string, kubeUsers []string) map[string][]string { return nil } diff --git a/lib/services/github.go b/lib/services/github.go index d450bcc5040d5..b3c7b54f9a489 100644 --- a/lib/services/github.go +++ b/lib/services/github.go @@ -52,7 +52,7 @@ type GithubConnector interface { SetTeamsToLogins([]TeamMapping) // MapClaims returns the list of allows logins based on the retrieved claims // returns list of logins and kubernetes groups - MapClaims(GithubClaims) ([]string, []string) + MapClaims(GithubClaims) (logins []string, kubeGroups []string, kubeUsers []string) // GetDisplay returns the connector display name GetDisplay() string // SetDisplay sets the connector display name @@ -110,6 +110,9 @@ type TeamMapping struct { Logins []string `json:"logins,omitempty"` // KubeGroups is a list of allowed kubernetes groups for this org/team KubeGroups []string `json:"kubernetes_groups,omitempty"` + // KubeUsers is a list of allowed kubernetes users to impersonate for + // this org/team + KubeUsers []string `json:"kubernetes_users,omitempty"` } // GithubClaims represents Github user information obtained during OAuth2 flow @@ -240,8 +243,8 @@ func (c *GithubConnectorV3) SetDisplay(display string) { // MapClaims returns a list of logins based on the provided claims, // returns a list of logins and list of kubernetes groups -func (c *GithubConnectorV3) MapClaims(claims GithubClaims) ([]string, []string) { - var logins, kubeGroups []string +func (c *GithubConnectorV3) MapClaims(claims GithubClaims) ([]string, []string, []string) { + var logins, kubeGroups, kubeUsers []string for _, mapping := range c.GetTeamsToLogins() { teams, ok := claims.OrganizationToTeams[mapping.Organization] if !ok { @@ -253,10 +256,11 @@ func (c *GithubConnectorV3) MapClaims(claims GithubClaims) ([]string, []string) if team == mapping.Team { logins = append(logins, mapping.Logins...) kubeGroups = append(kubeGroups, mapping.KubeGroups...) + kubeUsers = append(kubeUsers, mapping.KubeUsers...) } } } - return utils.Deduplicate(logins), utils.Deduplicate(kubeGroups) + return utils.Deduplicate(logins), utils.Deduplicate(kubeGroups), utils.Deduplicate(kubeUsers) } var githubConnectorMarshaler GithubConnectorMarshaler = &TeleportGithubConnectorMarshaler{} diff --git a/lib/services/github_test.go b/lib/services/github_test.go index 8b626f591b8db..b7308aada46e4 100644 --- a/lib/services/github_test.go +++ b/lib/services/github_test.go @@ -77,6 +77,7 @@ func (s *GithubSuite) TestMapClaims(c *check.C) { Team: "admins", Logins: []string{"admin", "dev"}, KubeGroups: []string{"system:masters", "kube-devs"}, + KubeUsers: []string{"alice@example.com"}, }, { Organization: "gravitational", @@ -86,15 +87,16 @@ func (s *GithubSuite) TestMapClaims(c *check.C) { }, }, }) - logins, kubeGroups := connector.MapClaims(GithubClaims{ + logins, kubeGroups, kubeUsers := connector.MapClaims(GithubClaims{ OrganizationToTeams: map[string][]string{ "gravitational": []string{"admins"}, }, }) c.Assert(logins, check.DeepEquals, []string{"admin", "dev"}) c.Assert(kubeGroups, check.DeepEquals, []string{"system:masters", "kube-devs"}) + c.Assert(kubeUsers, check.DeepEquals, []string{"alice@example.com"}) - logins, kubeGroups = connector.MapClaims(GithubClaims{ + logins, kubeGroups, kubeUsers = connector.MapClaims(GithubClaims{ OrganizationToTeams: map[string][]string{ "gravitational": []string{"devs"}, }, @@ -102,7 +104,7 @@ func (s *GithubSuite) TestMapClaims(c *check.C) { c.Assert(logins, check.DeepEquals, []string{"dev", "test"}) c.Assert(kubeGroups, check.DeepEquals, []string{"kube-devs"}) - logins, kubeGroups = connector.MapClaims(GithubClaims{ + logins, kubeGroups, kubeUsers = connector.MapClaims(GithubClaims{ OrganizationToTeams: map[string][]string{ "gravitational": []string{"admins", "devs"}, }, diff --git a/lib/services/role.go b/lib/services/role.go index dd540129e3d9a..caf8022764a2f 100644 --- a/lib/services/role.go +++ b/lib/services/role.go @@ -272,6 +272,11 @@ type Role interface { // SetKubeGroups sets kubernetes groups for allow or deny condition. SetKubeGroups(RoleConditionType, []string) + // GetKubeUsers returns kubernetes users to impersonate + GetKubeUsers(RoleConditionType) []string + // SetKubeUsers sets kubernetes users to impersonate for allow or deny condition. + SetKubeUsers(RoleConditionType, []string) + // GetAccessRequestConditions gets allow/deny conditions for access requests. GetAccessRequestConditions(RoleConditionType) AccessRequestConditions // SetAccessRequestConditions sets allow/deny conditions for access requests. @@ -323,6 +328,21 @@ func ApplyTraits(r Role, traits map[string][]string) Role { } r.SetKubeGroups(condition, utils.Deduplicate(outKubeGroups)) + // apply templates to kubernetes users + inKubeUsers := r.GetKubeUsers(condition) + var outKubeUsers []string + for _, user := range inKubeUsers { + variableValues, err := applyValueTraits(user, traits) + if err != nil { + if !trace.IsNotFound(err) { + log.Debugf("Skipping kube user %v: %v.", user, err) + } + continue + } + outKubeUsers = append(outKubeUsers, variableValues...) + } + r.SetKubeUsers(condition, utils.Deduplicate(outKubeUsers)) + inLabels := r.GetNodeLabels(condition) // to avoid unnecessary allocations if inLabels != nil { @@ -362,9 +382,8 @@ func ApplyTraits(r Role, traits map[string][]string) Role { // mapped list of values otherwise, the function guarantees to return // at least one value in case if return value is nil func applyValueTraits(val string, traits map[string][]string) ([]string, error) { - // Extract the variablePrefix and variableName from the role variable. - variablePrefix, variableName, err := parse.IsRoleVariable(val) - + // Extract the variable from the role variable. + variable, err := parse.RoleVariable(val) if err != nil { if !trace.IsNotFound(err) { return nil, trace.Wrap(err) @@ -373,19 +392,21 @@ func applyValueTraits(val string, traits map[string][]string) ([]string, error) } // For internal traits, only internal.logins and internal.kubernetes_groups is supported at the moment. - if variablePrefix == teleport.TraitInternalPrefix { - if variableName != teleport.TraitLogins && variableName != teleport.TraitKubeGroups { - return nil, trace.BadParameter("unsupported variable %q", variableName) + if variable.Namespace() == teleport.TraitInternalPrefix { + if variable.Name() != teleport.TraitLogins && variable.Name() != teleport.TraitKubeGroups && variable.Name() != teleport.TraitKubeUsers { + return nil, trace.BadParameter("unsupported variable %q", variable.Name()) } } // If the variable is not found in the traits, skip it. - variableValues, ok := traits[variableName] - if !ok || len(variableValues) == 0 { - return nil, trace.NotFound("variable %q not found in traits", variableName) + interpolated, err := variable.Interpolate(traits) + if trace.IsNotFound(err) || len(interpolated) == 0 { + return nil, trace.NotFound("variable %q not found in traits", variable.Name()) } - - return append([]string{}, variableValues...), nil + if err != nil { + return nil, trace.Wrap(err) + } + return interpolated, nil } // GetVersion returns resource version @@ -527,6 +548,25 @@ func (r *RoleV3) SetKubeGroups(rct RoleConditionType, groups []string) { } } +// GetKubeUsers returns kubernetes users +func (r *RoleV3) GetKubeUsers(rct RoleConditionType) []string { + if rct == Allow { + return r.Spec.Allow.KubeUsers + } + return r.Spec.Deny.KubeUsers +} + +// SetKubeUsers sets kubernetes user for allow or deny condition. +func (r *RoleV3) SetKubeUsers(rct RoleConditionType, users []string) { + lcopy := utils.CopyStrings(users) + + if rct == Allow { + r.Spec.Allow.KubeUsers = lcopy + } else { + r.Spec.Deny.KubeUsers = lcopy + } +} + // GetAccessRequestConditions gets conditions for access requests. func (r *RoleV3) GetAccessRequestConditions(rct RoleConditionType) AccessRequestConditions { cond := r.Spec.Deny.Request @@ -647,7 +687,7 @@ func (r *RoleV3) CheckAndSetDefaults() error { for _, condition := range []RoleConditionType{Allow, Deny} { for _, login := range r.GetLogins(condition) { if strings.Contains(login, "{{") || strings.Contains(login, "}}") { - _, _, err := parse.IsRoleVariable(login) + _, err := parse.RoleVariable(login) if err != nil { return trace.BadParameter("invalid login found: %v", login) } @@ -1285,9 +1325,9 @@ type AccessChecker interface { // returns a combined list of allowed logins. CheckLoginDuration(ttl time.Duration) ([]string, error) - // CheckKubeGroups check if role can login into kubernetes - // and returns a combined list of allowed groups - CheckKubeGroups(ttl time.Duration) ([]string, error) + // CheckKubeGroupsAndUsers check if role can login into kubernetes + // and returns two lists of combined allowed groups and users + CheckKubeGroupsAndUsers(ttl time.Duration) (groups []string, users []string, err error) // AdjustSessionTTL will reduce the requested ttl to lowest max allowed TTL // for this role set, otherwise it returns ttl unchanged @@ -1626,31 +1666,39 @@ func (set RoleSet) AdjustDisconnectExpiredCert(disconnect bool) bool { return disconnect } -// CheckKubeGroups check if role can login into kubernetes -// and returns a combined list of allowed groups -func (set RoleSet) CheckKubeGroups(ttl time.Duration) ([]string, error) { - groups := make(map[string]bool) +// CheckKubeGroupsAndUsers check if role can login into kubernetes +// and returns two lists of allowed groups and users +func (set RoleSet) CheckKubeGroupsAndUsers(ttl time.Duration) ([]string, []string, error) { + groups := make(map[string]struct{}) + users := make(map[string]struct{}) var matchedTTL bool for _, role := range set { maxSessionTTL := role.GetOptions().MaxSessionTTL.Value() if ttl <= maxSessionTTL && maxSessionTTL != 0 { matchedTTL = true for _, group := range role.GetKubeGroups(Allow) { - groups[group] = true + groups[group] = struct{}{} + } + for _, user := range role.GetKubeUsers(Allow) { + users[user] = struct{}{} } } } - if !matchedTTL { - return nil, trace.AccessDenied("this user cannot request kubernetes access for %v", ttl) + for _, role := range set { + for _, group := range role.GetKubeGroups(Deny) { + delete(groups, group) + } + for _, user := range role.GetKubeUsers(Deny) { + delete(users, user) + } } - if len(groups) == 0 { - return nil, trace.AccessDenied("this user cannot request kubernetes access, has no assigned groups") + if !matchedTTL { + return nil, nil, trace.AccessDenied("this user cannot request kubernetes access for %v", ttl) } - out := make([]string, 0, len(groups)) - for group := range groups { - out = append(out, group) + if len(groups) == 0 && len(users) == 0 { + return nil, nil, trace.AccessDenied("this user cannot request kubernetes access, has no assigned groups or users") } - return out, nil + return utils.StringsSliceFromSet(groups), utils.StringsSliceFromSet(users), nil } // CheckLoginDuration checks if role set can login up to given duration and diff --git a/lib/services/role_test.go b/lib/services/role_test.go index e16c4d5b93e34..6130fd849d6e6 100644 --- a/lib/services/role_test.go +++ b/lib/services/role_test.go @@ -1128,6 +1128,8 @@ func (s *RoleSuite) TestApplyTraits(c *C) { outLabels Labels inKubeGroups []string outKubeGroups []string + inKubeUsers []string + outKubeUsers []string } var tests = []struct { comment string @@ -1146,6 +1148,16 @@ func (s *RoleSuite) TestApplyTraits(c *C) { outLogins: []string{"bar", "root"}, }, }, + { + comment: "logins substitute in allow rule with function", + inTraits: map[string][]string{ + "foo": []string{"Bar "}, + }, + allow: rule{ + inLogins: []string{`{{email.local(external.foo)}}`, "root"}, + outLogins: []string{"bar", "root"}, + }, + }, { comment: "logins substitute in deny rule", inTraits: map[string][]string{ @@ -1176,6 +1188,26 @@ func (s *RoleSuite) TestApplyTraits(c *C) { outKubeGroups: []string{"bar", "root"}, }, }, + { + comment: "kube user interpolation in allow rule", + inTraits: map[string][]string{ + "foo": []string{"bar"}, + }, + allow: rule{ + inKubeUsers: []string{`IAM#{{external.foo}};`}, + outKubeUsers: []string{"IAM#bar;"}, + }, + }, + { + comment: "kube users interpolation in deny rule", + inTraits: map[string][]string{ + "foo": []string{"bar"}, + }, + deny: rule{ + inKubeUsers: []string{`IAM#{{external.foo}};`}, + outKubeUsers: []string{"IAM#bar;"}, + }, + }, { comment: "no variable in logins", inTraits: map[string][]string{ @@ -1186,15 +1218,40 @@ func (s *RoleSuite) TestApplyTraits(c *C) { outLogins: []string{"root"}, }, }, - { - comment: "invalid variable in logins gets passed along", + comment: "invalid variable in logins does not get passed along", + inTraits: map[string][]string{ + "foo": []string{"bar"}, + }, + allow: rule{ + inLogins: []string{`external.foo}}`}, + }, + }, + { + comment: "invalid function call in logins does not get passed along", + inTraits: map[string][]string{ + "foo": []string{"bar"}, + }, + allow: rule{ + inLogins: []string{`{{email.local(external.foo, 1)}}`}, + }, + }, + { + comment: "invalid function call in logins does not get passed along", + inTraits: map[string][]string{ + "foo": []string{"bar"}, + }, + allow: rule{ + inLogins: []string{`{{email.local()}}`}, + }, + }, + { + comment: "invalid function call in logins does not get passed along", inTraits: map[string][]string{ "foo": []string{"bar"}, }, allow: rule{ - inLogins: []string{`external.foo}}`}, - outLogins: []string{`external.foo}}`}, + inLogins: []string{`{{email.local(email.local)}}`, `{{email.local(email.local())}}`}, }, }, { @@ -1310,11 +1367,13 @@ func (s *RoleSuite) TestApplyTraits(c *C) { Logins: tt.allow.inLogins, NodeLabels: tt.allow.inLabels, KubeGroups: tt.allow.inKubeGroups, + KubeUsers: tt.allow.inKubeUsers, }, Deny: RoleConditions{ Logins: tt.deny.inLogins, NodeLabels: tt.deny.inLabels, KubeGroups: tt.deny.inKubeGroups, + KubeUsers: tt.deny.inKubeUsers, }, }, } @@ -1323,10 +1382,12 @@ func (s *RoleSuite) TestApplyTraits(c *C) { c.Assert(outRole.GetLogins(Allow), DeepEquals, tt.allow.outLogins, comment) c.Assert(outRole.GetNodeLabels(Allow), DeepEquals, tt.allow.outLabels, comment) c.Assert(outRole.GetKubeGroups(Allow), DeepEquals, tt.allow.outKubeGroups, comment) + c.Assert(outRole.GetKubeUsers(Allow), DeepEquals, tt.allow.outKubeUsers, comment) c.Assert(outRole.GetLogins(Deny), DeepEquals, tt.deny.outLogins, comment) c.Assert(outRole.GetNodeLabels(Deny), DeepEquals, tt.deny.outLabels, comment) c.Assert(outRole.GetKubeGroups(Deny), DeepEquals, tt.deny.outKubeGroups, comment) + c.Assert(outRole.GetKubeUsers(Deny), DeepEquals, tt.deny.outKubeUsers, comment) } } diff --git a/lib/services/saml.go b/lib/services/saml.go index 567ee5b4e0cdb..4ace9592763f8 100644 --- a/lib/services/saml.go +++ b/lib/services/saml.go @@ -1,5 +1,5 @@ /* -Copyright 2015-2019 Gravitational, Inc. +Copyright 2015-2020 Gravitational, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ limitations under the License. package services import ( - "bytes" "crypto/x509" "crypto/x509/pkix" "encoding/base64" @@ -26,7 +25,6 @@ import ( "io/ioutil" "net/http" "strings" - "text/template" "time" "github.com/gravitational/teleport" @@ -192,7 +190,7 @@ func (*TeleportSAMLConnectorMarshaler) UnmarshalSAMLConnector(bytes []byte, opts return nil, trace.BadParameter("SAML connector resource version %v is not supported", h.Version) } -// MarshalUser marshals SAML connector into JSON +// MarshalSAMLConnector marshals SAML connector into JSON func (*TeleportSAMLConnectorMarshaler) MarshalSAMLConnector(c SAMLConnector, opts ...MarshalOption) ([]byte, error) { cfg, err := collectOptions(opts) if err != nil { @@ -507,50 +505,6 @@ func (o *SAMLConnectorV2) MapAttributes(assertionInfo saml2.AssertionInfo) []str return utils.Deduplicate(roles) } -// executeSAMLStringTemplate takes a raw template string and a map of -// assertions to execute a template and generate output. Because the data -// structure used to execute the template is a map, the format of the raw -// string is expected to be {{index . "key"}}. See -// https://golang.org/pkg/text/template/ for more details. -func executeSAMLStringTemplate(raw string, assertion map[string]string) (string, error) { - tmpl, err := template.New("dynamic-roles").Parse(raw) - if err != nil { - return "", trace.Wrap(err) - } - var buf bytes.Buffer - err = tmpl.Execute(&buf, assertion) - if err != nil { - return "", trace.Wrap(err) - } - - return buf.String(), nil -} - -// executeSAMLStringTemplate takes raw template strings and a map of -// assertions to execute templates and generate a slice of output. Because the -// data structure used to execute the template is a map, the format of each raw -// string is expected to be {{index . "key"}}. See -// https://golang.org/pkg/text/template/ for more details. -func executeSAMLSliceTemplate(raw []string, assertion map[string]string) ([]string, error) { - var sl []string - - for _, v := range raw { - tmpl, err := template.New("dynamic-roles").Parse(v) - if err != nil { - return nil, trace.Wrap(err) - } - var buf bytes.Buffer - err = tmpl.Execute(&buf, assertion) - if err != nil { - return nil, trace.Wrap(err) - } - - sl = append(sl, buf.String()) - } - - return sl, nil -} - // GetServiceProvider initialises service provider spec from settings func (o *SAMLConnectorV2) GetServiceProvider(clock clockwork.Clock) (*saml2.SAMLServiceProvider, error) { if o.Metadata.Name == "" { diff --git a/lib/services/types.pb.go b/lib/services/types.pb.go index 7cd5007d78b53..acfdcdecbec96 100644 --- a/lib/services/types.pb.go +++ b/lib/services/types.pb.go @@ -64,7 +64,7 @@ func (x RequestState) String() string { return proto.EnumName(RequestState_name, int32(x)) } func (RequestState) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{0} + return fileDescriptor_types_bfe64a187d8e57e3, []int{0} } type KeepAlive struct { @@ -85,7 +85,7 @@ func (m *KeepAlive) Reset() { *m = KeepAlive{} } func (m *KeepAlive) String() string { return proto.CompactTextString(m) } func (*KeepAlive) ProtoMessage() {} func (*KeepAlive) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{0} + return fileDescriptor_types_bfe64a187d8e57e3, []int{0} } func (m *KeepAlive) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -138,7 +138,7 @@ func (m *Metadata) Reset() { *m = Metadata{} } func (m *Metadata) String() string { return proto.CompactTextString(m) } func (*Metadata) ProtoMessage() {} func (*Metadata) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{1} + return fileDescriptor_types_bfe64a187d8e57e3, []int{1} } func (m *Metadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -197,7 +197,7 @@ type Rotation struct { func (m *Rotation) Reset() { *m = Rotation{} } func (*Rotation) ProtoMessage() {} func (*Rotation) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{2} + return fileDescriptor_types_bfe64a187d8e57e3, []int{2} } func (m *Rotation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -244,7 +244,7 @@ func (m *RotationSchedule) Reset() { *m = RotationSchedule{} } func (m *RotationSchedule) String() string { return proto.CompactTextString(m) } func (*RotationSchedule) ProtoMessage() {} func (*RotationSchedule) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{3} + return fileDescriptor_types_bfe64a187d8e57e3, []int{3} } func (m *RotationSchedule) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -293,7 +293,7 @@ func (m *ResourceHeader) Reset() { *m = ResourceHeader{} } func (m *ResourceHeader) String() string { return proto.CompactTextString(m) } func (*ResourceHeader) ProtoMessage() {} func (*ResourceHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{4} + return fileDescriptor_types_bfe64a187d8e57e3, []int{4} } func (m *ResourceHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -342,7 +342,7 @@ type ServerV2 struct { func (m *ServerV2) Reset() { *m = ServerV2{} } func (*ServerV2) ProtoMessage() {} func (*ServerV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{5} + return fileDescriptor_types_bfe64a187d8e57e3, []int{5} } func (m *ServerV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -395,7 +395,7 @@ func (m *ServerSpecV2) Reset() { *m = ServerSpecV2{} } func (m *ServerSpecV2) String() string { return proto.CompactTextString(m) } func (*ServerSpecV2) ProtoMessage() {} func (*ServerSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{6} + return fileDescriptor_types_bfe64a187d8e57e3, []int{6} } func (m *ServerSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -442,7 +442,7 @@ func (m *CommandLabelV2) Reset() { *m = CommandLabelV2{} } func (m *CommandLabelV2) String() string { return proto.CompactTextString(m) } func (*CommandLabelV2) ProtoMessage() {} func (*CommandLabelV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{7} + return fileDescriptor_types_bfe64a187d8e57e3, []int{7} } func (m *CommandLabelV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -486,7 +486,7 @@ func (m *TLSKeyPair) Reset() { *m = TLSKeyPair{} } func (m *TLSKeyPair) String() string { return proto.CompactTextString(m) } func (*TLSKeyPair) ProtoMessage() {} func (*TLSKeyPair) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{8} + return fileDescriptor_types_bfe64a187d8e57e3, []int{8} } func (m *TLSKeyPair) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -535,7 +535,7 @@ type CertAuthorityV2 struct { func (m *CertAuthorityV2) Reset() { *m = CertAuthorityV2{} } func (*CertAuthorityV2) ProtoMessage() {} func (*CertAuthorityV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{9} + return fileDescriptor_types_bfe64a187d8e57e3, []int{9} } func (m *CertAuthorityV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -598,7 +598,7 @@ func (m *CertAuthoritySpecV2) Reset() { *m = CertAuthoritySpecV2{} } func (m *CertAuthoritySpecV2) String() string { return proto.CompactTextString(m) } func (*CertAuthoritySpecV2) ProtoMessage() {} func (*CertAuthoritySpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{10} + return fileDescriptor_types_bfe64a187d8e57e3, []int{10} } func (m *CertAuthoritySpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -643,7 +643,7 @@ func (m *RoleMapping) Reset() { *m = RoleMapping{} } func (m *RoleMapping) String() string { return proto.CompactTextString(m) } func (*RoleMapping) ProtoMessage() {} func (*RoleMapping) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{11} + return fileDescriptor_types_bfe64a187d8e57e3, []int{11} } func (m *RoleMapping) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -690,7 +690,7 @@ type ProvisionTokenV1 struct { func (m *ProvisionTokenV1) Reset() { *m = ProvisionTokenV1{} } func (*ProvisionTokenV1) ProtoMessage() {} func (*ProvisionTokenV1) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{12} + return fileDescriptor_types_bfe64a187d8e57e3, []int{12} } func (m *ProvisionTokenV1) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -739,7 +739,7 @@ type ProvisionTokenV2 struct { func (m *ProvisionTokenV2) Reset() { *m = ProvisionTokenV2{} } func (*ProvisionTokenV2) ProtoMessage() {} func (*ProvisionTokenV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{13} + return fileDescriptor_types_bfe64a187d8e57e3, []int{13} } func (m *ProvisionTokenV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -783,7 +783,7 @@ func (m *ProvisionTokenSpecV2) Reset() { *m = ProvisionTokenSpecV2{} } func (m *ProvisionTokenSpecV2) String() string { return proto.CompactTextString(m) } func (*ProvisionTokenSpecV2) ProtoMessage() {} func (*ProvisionTokenSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{14} + return fileDescriptor_types_bfe64a187d8e57e3, []int{14} } func (m *ProvisionTokenSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -832,7 +832,7 @@ type StaticTokensV2 struct { func (m *StaticTokensV2) Reset() { *m = StaticTokensV2{} } func (*StaticTokensV2) ProtoMessage() {} func (*StaticTokensV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{15} + return fileDescriptor_types_bfe64a187d8e57e3, []int{15} } func (m *StaticTokensV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -875,7 +875,7 @@ func (m *StaticTokensSpecV2) Reset() { *m = StaticTokensSpecV2{} } func (m *StaticTokensSpecV2) String() string { return proto.CompactTextString(m) } func (*StaticTokensSpecV2) ProtoMessage() {} func (*StaticTokensSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{16} + return fileDescriptor_types_bfe64a187d8e57e3, []int{16} } func (m *StaticTokensSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -924,7 +924,7 @@ type ClusterNameV2 struct { func (m *ClusterNameV2) Reset() { *m = ClusterNameV2{} } func (*ClusterNameV2) ProtoMessage() {} func (*ClusterNameV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{17} + return fileDescriptor_types_bfe64a187d8e57e3, []int{17} } func (m *ClusterNameV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -967,7 +967,7 @@ func (m *ClusterNameSpecV2) Reset() { *m = ClusterNameSpecV2{} } func (m *ClusterNameSpecV2) String() string { return proto.CompactTextString(m) } func (*ClusterNameSpecV2) ProtoMessage() {} func (*ClusterNameSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{18} + return fileDescriptor_types_bfe64a187d8e57e3, []int{18} } func (m *ClusterNameSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1016,7 +1016,7 @@ type ClusterConfigV3 struct { func (m *ClusterConfigV3) Reset() { *m = ClusterConfigV3{} } func (*ClusterConfigV3) ProtoMessage() {} func (*ClusterConfigV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{19} + return fileDescriptor_types_bfe64a187d8e57e3, []int{19} } func (m *ClusterConfigV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1079,7 +1079,7 @@ func (m *ClusterConfigSpecV3) Reset() { *m = ClusterConfigSpecV3{} } func (m *ClusterConfigSpecV3) String() string { return proto.CompactTextString(m) } func (*ClusterConfigSpecV3) ProtoMessage() {} func (*ClusterConfigSpecV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{20} + return fileDescriptor_types_bfe64a187d8e57e3, []int{20} } func (m *ClusterConfigSpecV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1132,7 +1132,7 @@ func (m *AuditConfig) Reset() { *m = AuditConfig{} } func (m *AuditConfig) String() string { return proto.CompactTextString(m) } func (*AuditConfig) ProtoMessage() {} func (*AuditConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{21} + return fileDescriptor_types_bfe64a187d8e57e3, []int{21} } func (m *AuditConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1182,7 +1182,7 @@ func (m *Namespace) Reset() { *m = Namespace{} } func (m *Namespace) String() string { return proto.CompactTextString(m) } func (*Namespace) ProtoMessage() {} func (*Namespace) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{22} + return fileDescriptor_types_bfe64a187d8e57e3, []int{22} } func (m *Namespace) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1222,7 +1222,7 @@ func (m *NamespaceSpec) Reset() { *m = NamespaceSpec{} } func (m *NamespaceSpec) String() string { return proto.CompactTextString(m) } func (*NamespaceSpec) ProtoMessage() {} func (*NamespaceSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{23} + return fileDescriptor_types_bfe64a187d8e57e3, []int{23} } func (m *NamespaceSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1271,7 +1271,7 @@ type AccessRequestV3 struct { func (m *AccessRequestV3) Reset() { *m = AccessRequestV3{} } func (*AccessRequestV3) ProtoMessage() {} func (*AccessRequestV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{24} + return fileDescriptor_types_bfe64a187d8e57e3, []int{24} } func (m *AccessRequestV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1321,7 +1321,7 @@ func (m *AccessRequestSpecV3) Reset() { *m = AccessRequestSpecV3{} } func (m *AccessRequestSpecV3) String() string { return proto.CompactTextString(m) } func (*AccessRequestSpecV3) ProtoMessage() {} func (*AccessRequestSpecV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{25} + return fileDescriptor_types_bfe64a187d8e57e3, []int{25} } func (m *AccessRequestSpecV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1367,7 +1367,7 @@ func (m *AccessRequestFilter) Reset() { *m = AccessRequestFilter{} } func (m *AccessRequestFilter) String() string { return proto.CompactTextString(m) } func (*AccessRequestFilter) ProtoMessage() {} func (*AccessRequestFilter) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{26} + return fileDescriptor_types_bfe64a187d8e57e3, []int{26} } func (m *AccessRequestFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1416,7 +1416,7 @@ type PluginDataV3 struct { func (m *PluginDataV3) Reset() { *m = PluginDataV3{} } func (*PluginDataV3) ProtoMessage() {} func (*PluginDataV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{27} + return fileDescriptor_types_bfe64a187d8e57e3, []int{27} } func (m *PluginDataV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1459,7 +1459,7 @@ func (m *PluginDataEntry) Reset() { *m = PluginDataEntry{} } func (m *PluginDataEntry) String() string { return proto.CompactTextString(m) } func (*PluginDataEntry) ProtoMessage() {} func (*PluginDataEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{28} + return fileDescriptor_types_bfe64a187d8e57e3, []int{28} } func (m *PluginDataEntry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1501,7 +1501,7 @@ func (m *PluginDataSpecV3) Reset() { *m = PluginDataSpecV3{} } func (m *PluginDataSpecV3) String() string { return proto.CompactTextString(m) } func (*PluginDataSpecV3) ProtoMessage() {} func (*PluginDataSpecV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{29} + return fileDescriptor_types_bfe64a187d8e57e3, []int{29} } func (m *PluginDataSpecV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1548,7 +1548,7 @@ func (m *PluginDataFilter) Reset() { *m = PluginDataFilter{} } func (m *PluginDataFilter) String() string { return proto.CompactTextString(m) } func (*PluginDataFilter) ProtoMessage() {} func (*PluginDataFilter) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{30} + return fileDescriptor_types_bfe64a187d8e57e3, []int{30} } func (m *PluginDataFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1599,7 +1599,7 @@ func (m *PluginDataUpdateParams) Reset() { *m = PluginDataUpdateParams{} func (m *PluginDataUpdateParams) String() string { return proto.CompactTextString(m) } func (*PluginDataUpdateParams) ProtoMessage() {} func (*PluginDataUpdateParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{31} + return fileDescriptor_types_bfe64a187d8e57e3, []int{31} } func (m *PluginDataUpdateParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1648,7 @@ type RoleV3 struct { func (m *RoleV3) Reset() { *m = RoleV3{} } func (*RoleV3) ProtoMessage() {} func (*RoleV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{32} + return fileDescriptor_types_bfe64a187d8e57e3, []int{32} } func (m *RoleV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1694,7 +1694,7 @@ func (m *RoleSpecV3) Reset() { *m = RoleSpecV3{} } func (m *RoleSpecV3) String() string { return proto.CompactTextString(m) } func (*RoleSpecV3) ProtoMessage() {} func (*RoleSpecV3) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{33} + return fileDescriptor_types_bfe64a187d8e57e3, []int{33} } func (m *RoleSpecV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1753,7 +1753,7 @@ func (m *RoleOptions) Reset() { *m = RoleOptions{} } func (m *RoleOptions) String() string { return proto.CompactTextString(m) } func (*RoleOptions) ProtoMessage() {} func (*RoleOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{34} + return fileDescriptor_types_bfe64a187d8e57e3, []int{34} } func (m *RoleOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1796,18 +1796,20 @@ type RoleConditions struct { // construct used for access control. Rules []Rule `protobuf:"bytes,4,rep,name=Rules" json:"rules,omitempty"` // KubeGroups is a list of kubernetes groups - KubeGroups []string `protobuf:"bytes,5,rep,name=KubeGroups" json:"kubernetes_groups,omitempty"` - Request *AccessRequestConditions `protobuf:"bytes,6,opt,name=Request" json:"request,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + KubeGroups []string `protobuf:"bytes,5,rep,name=KubeGroups" json:"kubernetes_groups,omitempty"` + Request *AccessRequestConditions `protobuf:"bytes,6,opt,name=Request" json:"request,omitempty"` + // KubeUsers is an optional kuberentes users to impersonate + KubeUsers []string `protobuf:"bytes,7,rep,name=KubeUsers" json:"kubernetes_users,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *RoleConditions) Reset() { *m = RoleConditions{} } func (m *RoleConditions) String() string { return proto.CompactTextString(m) } func (*RoleConditions) ProtoMessage() {} func (*RoleConditions) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{35} + return fileDescriptor_types_bfe64a187d8e57e3, []int{35} } func (m *RoleConditions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1849,7 +1851,7 @@ func (m *AccessRequestConditions) Reset() { *m = AccessRequestConditions func (m *AccessRequestConditions) String() string { return proto.CompactTextString(m) } func (*AccessRequestConditions) ProtoMessage() {} func (*AccessRequestConditions) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{36} + return fileDescriptor_types_bfe64a187d8e57e3, []int{36} } func (m *AccessRequestConditions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1898,7 +1900,7 @@ func (m *Rule) Reset() { *m = Rule{} } func (m *Rule) String() string { return proto.CompactTextString(m) } func (*Rule) ProtoMessage() {} func (*Rule) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{37} + return fileDescriptor_types_bfe64a187d8e57e3, []int{37} } func (m *Rule) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1940,7 +1942,7 @@ func (m *BoolValue) Reset() { *m = BoolValue{} } func (m *BoolValue) String() string { return proto.CompactTextString(m) } func (*BoolValue) ProtoMessage() {} func (*BoolValue) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{38} + return fileDescriptor_types_bfe64a187d8e57e3, []int{38} } func (m *BoolValue) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1989,7 +1991,7 @@ type UserV2 struct { func (m *UserV2) Reset() { *m = UserV2{} } func (*UserV2) ProtoMessage() {} func (*UserV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{39} + return fileDescriptor_types_bfe64a187d8e57e3, []int{39} } func (m *UserV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2052,7 +2054,7 @@ func (m *UserSpecV2) Reset() { *m = UserSpecV2{} } func (m *UserSpecV2) String() string { return proto.CompactTextString(m) } func (*UserSpecV2) ProtoMessage() {} func (*UserSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{40} + return fileDescriptor_types_bfe64a187d8e57e3, []int{40} } func (m *UserSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2097,7 +2099,7 @@ type ExternalIdentity struct { func (m *ExternalIdentity) Reset() { *m = ExternalIdentity{} } func (*ExternalIdentity) ProtoMessage() {} func (*ExternalIdentity) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{41} + return fileDescriptor_types_bfe64a187d8e57e3, []int{41} } func (m *ExternalIdentity) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2145,7 +2147,7 @@ func (m *LoginStatus) Reset() { *m = LoginStatus{} } func (m *LoginStatus) String() string { return proto.CompactTextString(m) } func (*LoginStatus) ProtoMessage() {} func (*LoginStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{42} + return fileDescriptor_types_bfe64a187d8e57e3, []int{42} } func (m *LoginStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2190,7 +2192,7 @@ type CreatedBy struct { func (m *CreatedBy) Reset() { *m = CreatedBy{} } func (*CreatedBy) ProtoMessage() {} func (*CreatedBy) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{43} + return fileDescriptor_types_bfe64a187d8e57e3, []int{43} } func (m *CreatedBy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2236,7 +2238,7 @@ func (m *U2FRegistrationData) Reset() { *m = U2FRegistrationData{} } func (m *U2FRegistrationData) String() string { return proto.CompactTextString(m) } func (*U2FRegistrationData) ProtoMessage() {} func (*U2FRegistrationData) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{44} + return fileDescriptor_types_bfe64a187d8e57e3, []int{44} } func (m *U2FRegistrationData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2284,7 +2286,7 @@ func (m *LocalAuthSecrets) Reset() { *m = LocalAuthSecrets{} } func (m *LocalAuthSecrets) String() string { return proto.CompactTextString(m) } func (*LocalAuthSecrets) ProtoMessage() {} func (*LocalAuthSecrets) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{45} + return fileDescriptor_types_bfe64a187d8e57e3, []int{45} } func (m *LocalAuthSecrets) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2330,7 +2332,7 @@ func (m *ConnectorRef) Reset() { *m = ConnectorRef{} } func (m *ConnectorRef) String() string { return proto.CompactTextString(m) } func (*ConnectorRef) ProtoMessage() {} func (*ConnectorRef) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{46} + return fileDescriptor_types_bfe64a187d8e57e3, []int{46} } func (m *ConnectorRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2372,7 +2374,7 @@ func (m *UserRef) Reset() { *m = UserRef{} } func (m *UserRef) String() string { return proto.CompactTextString(m) } func (*UserRef) ProtoMessage() {} func (*UserRef) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{47} + return fileDescriptor_types_bfe64a187d8e57e3, []int{47} } func (m *UserRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2422,7 +2424,7 @@ func (m *ReverseTunnelV2) Reset() { *m = ReverseTunnelV2{} } func (m *ReverseTunnelV2) String() string { return proto.CompactTextString(m) } func (*ReverseTunnelV2) ProtoMessage() {} func (*ReverseTunnelV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{48} + return fileDescriptor_types_bfe64a187d8e57e3, []int{48} } func (m *ReverseTunnelV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2469,7 +2471,7 @@ func (m *ReverseTunnelSpecV2) Reset() { *m = ReverseTunnelSpecV2{} } func (m *ReverseTunnelSpecV2) String() string { return proto.CompactTextString(m) } func (*ReverseTunnelSpecV2) ProtoMessage() {} func (*ReverseTunnelSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{49} + return fileDescriptor_types_bfe64a187d8e57e3, []int{49} } func (m *ReverseTunnelSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2518,7 +2520,7 @@ type TunnelConnectionV2 struct { func (m *TunnelConnectionV2) Reset() { *m = TunnelConnectionV2{} } func (*TunnelConnectionV2) ProtoMessage() {} func (*TunnelConnectionV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{50} + return fileDescriptor_types_bfe64a187d8e57e3, []int{50} } func (m *TunnelConnectionV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2566,7 +2568,7 @@ func (m *TunnelConnectionSpecV2) Reset() { *m = TunnelConnectionSpecV2{} func (m *TunnelConnectionSpecV2) String() string { return proto.CompactTextString(m) } func (*TunnelConnectionSpecV2) ProtoMessage() {} func (*TunnelConnectionSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_types_acdf685071da14d4, []int{51} + return fileDescriptor_types_bfe64a187d8e57e3, []int{51} } func (m *TunnelConnectionSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4611,6 +4613,21 @@ func (m *RoleConditions) MarshalTo(dAtA []byte) (int, error) { } i += n44 } + if len(m.KubeUsers) > 0 { + for _, s := range m.KubeUsers { + dAtA[i] = 0x3a + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) } @@ -6313,6 +6330,12 @@ func (m *RoleConditions) Size() (n int) { l = m.Request.Size() n += 1 + l + sovTypes(uint64(l)) } + if len(m.KubeUsers) > 0 { + for _, s := range m.KubeUsers { + l = len(s) + n += 1 + l + sovTypes(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -13308,6 +13331,35 @@ func (m *RoleConditions) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KubeUsers", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KubeUsers = append(m.KubeUsers, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15917,257 +15969,259 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("types.proto", fileDescriptor_types_acdf685071da14d4) } - -var fileDescriptor_types_acdf685071da14d4 = []byte{ - // 3983 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x7b, 0xcd, 0x6f, 0x1c, 0xc9, - 0x75, 0xb8, 0xe6, 0x83, 0xe4, 0xcc, 0x1b, 0x92, 0xa2, 0x8a, 0xfa, 0x18, 0x51, 0x5a, 0x35, 0x77, - 0xf4, 0xdb, 0x5d, 0xad, 0x7f, 0x32, 0x19, 0x53, 0x59, 0xd9, 0xda, 0xac, 0xb2, 0xcb, 0xe1, 0x50, - 0x12, 0x4d, 0x8a, 0xe2, 0x36, 0x29, 0x1a, 0xce, 0x57, 0xa7, 0xd9, 0x5d, 0x1c, 0x36, 0xd8, 0xd3, - 0x3d, 0xe9, 0xae, 0xa6, 0x34, 0x37, 0x23, 0x09, 0x10, 0x24, 0x06, 0xbc, 0x09, 0x02, 0xc3, 0x0b, - 0xc4, 0x87, 0x00, 0x39, 0xe5, 0x90, 0x5c, 0x83, 0x1c, 0x72, 0x08, 0xe0, 0x83, 0x8e, 0x3e, 0x1b, - 0x4e, 0x3b, 0xd9, 0x1c, 0x0c, 0xcc, 0x9f, 0xb0, 0x97, 0x04, 0xf5, 0xaa, 0xba, 0xbb, 0x7a, 0x66, - 0x28, 0x8e, 0x36, 0xbe, 0x70, 0x4f, 0xec, 0x79, 0xf5, 0xde, 0xab, 0x7a, 0xaf, 0x5e, 0xbd, 0xaf, - 0x2a, 0x42, 0x8d, 0xf5, 0xba, 0x34, 0x5c, 0xea, 0x06, 0x3e, 0xf3, 0x49, 0x25, 0xa4, 0xc1, 0x89, - 0x63, 0xd1, 0x70, 0xe1, 0x72, 0xdb, 0x6f, 0xfb, 0x08, 0x5c, 0xe6, 0x5f, 0x62, 0x7c, 0x41, 0x6b, - 0xfb, 0x7e, 0xdb, 0xa5, 0xcb, 0xf8, 0xeb, 0x20, 0x3a, 0x5c, 0x66, 0x4e, 0x87, 0x86, 0xcc, 0xec, - 0x74, 0x25, 0xc2, 0xc3, 0xb6, 0xc3, 0x8e, 0xa2, 0x83, 0x25, 0xcb, 0xef, 0x2c, 0xb7, 0x03, 0xf3, - 0xc4, 0x61, 0x26, 0x73, 0x7c, 0xcf, 0x74, 0x97, 0x19, 0x75, 0x69, 0xd7, 0x0f, 0xd8, 0xb2, 0xeb, - 0x1c, 0x2c, 0xbf, 0x08, 0xcc, 0x6e, 0x97, 0x06, 0x61, 0xfa, 0x21, 0xc8, 0x1b, 0xbf, 0x2c, 0x40, - 0x75, 0x93, 0xd2, 0xee, 0xaa, 0xeb, 0x9c, 0x50, 0xb2, 0x0c, 0xb0, 0x4b, 0x83, 0x13, 0x1a, 0x6c, - 0x9b, 0x1d, 0x5a, 0x2f, 0x2c, 0x16, 0xee, 0x54, 0x9b, 0x17, 0xfb, 0xb1, 0x56, 0x0b, 0x11, 0x6a, - 0x78, 0x66, 0x87, 0xea, 0x0a, 0x0a, 0xf9, 0xff, 0x50, 0xe5, 0x7f, 0xc3, 0xae, 0x69, 0xd1, 0x7a, - 0x11, 0xf1, 0x67, 0xfa, 0xb1, 0x56, 0xf5, 0x12, 0xa0, 0x9e, 0x8d, 0x93, 0x77, 0x61, 0x6a, 0x8b, - 0x9a, 0x21, 0xdd, 0x68, 0xd5, 0x4b, 0x8b, 0x85, 0x3b, 0xa5, 0xe6, 0x74, 0x3f, 0xd6, 0x2a, 0x2e, - 0x07, 0x19, 0x8e, 0xad, 0x27, 0x83, 0x64, 0x03, 0xa6, 0xd6, 0x5f, 0x76, 0x9d, 0x80, 0x86, 0xf5, - 0xf2, 0x62, 0xe1, 0x4e, 0x6d, 0x65, 0x61, 0x49, 0x68, 0x61, 0x29, 0xd1, 0xc2, 0xd2, 0x5e, 0xa2, - 0x85, 0xe6, 0xfc, 0xab, 0x58, 0xbb, 0xd0, 0x8f, 0xb5, 0x29, 0x2a, 0x48, 0xfe, 0xfa, 0x57, 0x5a, - 0x41, 0x4f, 0xe8, 0x1b, 0x7f, 0x55, 0x82, 0xca, 0x53, 0xca, 0x4c, 0xdb, 0x64, 0x26, 0xb9, 0x09, - 0x65, 0x45, 0xae, 0x4a, 0x3f, 0xd6, 0xca, 0x28, 0x10, 0x42, 0xc9, 0xed, 0x61, 0x51, 0x26, 0xfa, - 0xb1, 0x56, 0xf8, 0xa6, 0x2a, 0xc2, 0xef, 0x40, 0xad, 0x45, 0x43, 0x2b, 0x70, 0xba, 0x5c, 0xc9, - 0x28, 0x46, 0xb5, 0x79, 0xbd, 0x1f, 0x6b, 0x57, 0xec, 0x0c, 0x7c, 0xd7, 0xef, 0x38, 0x8c, 0x76, - 0xba, 0xac, 0xa7, 0xab, 0xd8, 0x64, 0x0b, 0x26, 0xb7, 0xcc, 0x03, 0xea, 0x86, 0xf5, 0x89, 0xc5, - 0xd2, 0x9d, 0xda, 0xca, 0xad, 0xa5, 0x64, 0xf3, 0x97, 0x92, 0x35, 0x2e, 0x09, 0x84, 0x75, 0x8f, - 0x05, 0xbd, 0xe6, 0xe5, 0x7e, 0xac, 0xcd, 0xb9, 0x08, 0x50, 0x58, 0x4a, 0x1e, 0x64, 0x37, 0xd3, - 0xd2, 0xe4, 0x99, 0x5a, 0x7a, 0xeb, 0x55, 0xac, 0x15, 0xfa, 0xb1, 0x76, 0x49, 0x6a, 0x29, 0xe3, - 0x97, 0xd3, 0x17, 0x59, 0x84, 0xe2, 0x46, 0xab, 0x3e, 0x85, 0xbb, 0x33, 0xd7, 0x8f, 0xb5, 0x69, - 0xc7, 0x56, 0xa6, 0x2e, 0x6e, 0xb4, 0x16, 0x1e, 0x40, 0x4d, 0x59, 0x23, 0x99, 0x83, 0xd2, 0x31, - 0xed, 0x09, 0x95, 0xea, 0xfc, 0x93, 0x5c, 0x86, 0x89, 0x13, 0xd3, 0x8d, 0xa4, 0x0e, 0x75, 0xf1, - 0xe3, 0xc3, 0xe2, 0x77, 0x0a, 0x8d, 0x1f, 0x97, 0xa1, 0xa2, 0xfb, 0xc2, 0x3e, 0xc9, 0xfb, 0x30, - 0xb1, 0xcb, 0x4c, 0x96, 0xec, 0xc6, 0x7c, 0x3f, 0xd6, 0x2e, 0x86, 0x1c, 0xa0, 0xcc, 0x27, 0x30, - 0x38, 0xea, 0xce, 0x91, 0x19, 0x26, 0xbb, 0x82, 0xa8, 0x5d, 0x0e, 0x50, 0x51, 0x11, 0x83, 0xbc, - 0x0b, 0xe5, 0xa7, 0xbe, 0x4d, 0xe5, 0xc6, 0x90, 0x7e, 0xac, 0xcd, 0x76, 0x7c, 0x5b, 0x45, 0xc4, - 0x71, 0x72, 0x17, 0xaa, 0x6b, 0x51, 0x10, 0x50, 0x8f, 0x6d, 0xb4, 0xd0, 0xc8, 0xaa, 0xcd, 0xd9, - 0x7e, 0xac, 0x81, 0x25, 0x80, 0xdc, 0x1c, 0x33, 0x04, 0xae, 0xea, 0x5d, 0x66, 0x06, 0x8c, 0xda, - 0xf5, 0x89, 0xb1, 0x54, 0xcd, 0x0d, 0xf2, 0x52, 0x28, 0x48, 0x06, 0x55, 0x2d, 0x39, 0x91, 0x27, - 0x50, 0x7b, 0x1c, 0x98, 0x16, 0xdd, 0xa1, 0x81, 0xe3, 0xdb, 0xb8, 0x87, 0xa5, 0xe6, 0xbb, 0xfd, - 0x58, 0xbb, 0xda, 0xe6, 0x60, 0xa3, 0x8b, 0xf0, 0x8c, 0xfa, 0xcb, 0x58, 0xab, 0xb4, 0xa2, 0x00, - 0xb5, 0xa7, 0xab, 0xa4, 0xe4, 0x8f, 0xf9, 0x96, 0x84, 0x0c, 0x55, 0x4b, 0x6d, 0xdc, 0xbd, 0xd7, - 0x2f, 0xb1, 0x21, 0x97, 0x78, 0xd5, 0x35, 0x43, 0x66, 0x04, 0x82, 0x6e, 0x60, 0x9d, 0x2a, 0x4b, - 0xa2, 0x43, 0x65, 0xd7, 0x3a, 0xa2, 0x76, 0xe4, 0xd2, 0x7a, 0x45, 0xb2, 0x4f, 0x6d, 0x37, 0xd9, - 0xd2, 0x04, 0xa3, 0xb9, 0x20, 0xd9, 0x93, 0x50, 0x42, 0x14, 0xf5, 0xa7, 0x7c, 0x3e, 0xac, 0x7c, - 0xfe, 0xf7, 0xda, 0x85, 0x1f, 0xfc, 0x72, 0xf1, 0x42, 0xe3, 0x5f, 0x8a, 0x30, 0x37, 0xc8, 0x84, - 0x1c, 0xc2, 0xcc, 0xf3, 0xae, 0x6d, 0x32, 0xba, 0xe6, 0x3a, 0xd4, 0x63, 0x21, 0xda, 0xc9, 0xeb, - 0xc5, 0xfa, 0x7f, 0x72, 0xde, 0x7a, 0x84, 0x84, 0x86, 0x25, 0x28, 0x07, 0x04, 0xcb, 0xb3, 0xcd, - 0xe6, 0x11, 0x5e, 0x2d, 0x44, 0x23, 0x7b, 0xb3, 0x79, 0x84, 0x73, 0x3c, 0x65, 0x1e, 0xc9, 0x56, - 0xda, 0x90, 0x67, 0x1f, 0xf4, 0xd0, 0x38, 0xc7, 0xb7, 0x21, 0x4e, 0x32, 0xc2, 0x86, 0x38, 0xb8, - 0xf1, 0xeb, 0x02, 0xcc, 0xea, 0x34, 0xf4, 0xa3, 0xc0, 0xa2, 0x4f, 0xa8, 0x69, 0xd3, 0x80, 0x9f, - 0x80, 0x4d, 0xc7, 0xb3, 0xe5, 0xb1, 0xc2, 0x13, 0x70, 0xec, 0x78, 0xea, 0x29, 0xc6, 0x71, 0xf2, - 0x5b, 0x30, 0xb5, 0x1b, 0x1d, 0x20, 0xaa, 0x38, 0x56, 0x57, 0x71, 0xc7, 0xa2, 0x03, 0x63, 0x00, - 0x3d, 0x41, 0x23, 0xcb, 0x30, 0xb5, 0x4f, 0x83, 0x30, 0xf3, 0x7b, 0x57, 0xf8, 0x0a, 0x4f, 0x04, - 0x48, 0x25, 0x90, 0x58, 0xe4, 0xbb, 0x99, 0xef, 0x95, 0x8e, 0x9c, 0x0c, 0x7b, 0xbc, 0xcc, 0x5a, - 0x3a, 0x12, 0xa2, 0x5a, 0x4b, 0x82, 0xd5, 0xf8, 0x8b, 0x22, 0x54, 0x84, 0x2a, 0xf7, 0x57, 0xb8, - 0x23, 0x57, 0x64, 0x44, 0x47, 0xce, 0x17, 0xfd, 0x95, 0x25, 0x7b, 0x67, 0x50, 0xb2, 0x1a, 0x0f, - 0x28, 0x52, 0xb2, 0x4c, 0x9e, 0x4f, 0xc6, 0x92, 0x67, 0x4e, 0xca, 0x53, 0x49, 0xe4, 0xc9, 0xa4, - 0x20, 0xdf, 0x81, 0xf2, 0x6e, 0x97, 0x5a, 0xd2, 0x8b, 0x5c, 0xcd, 0xa8, 0x85, 0x68, 0x7c, 0x6c, - 0x7f, 0xa5, 0x39, 0x2d, 0x39, 0x94, 0xc3, 0x2e, 0xb5, 0x74, 0xa4, 0x50, 0x4e, 0xcb, 0xcf, 0x4b, - 0x30, 0xad, 0xa2, 0x73, 0x6d, 0xac, 0xda, 0x76, 0xa0, 0x6a, 0xc3, 0xb4, 0xed, 0x40, 0x47, 0x28, - 0x79, 0x00, 0xb0, 0x13, 0x1d, 0xb8, 0x8e, 0x85, 0x38, 0xc5, 0x2c, 0x60, 0x75, 0x11, 0x6a, 0x70, - 0x54, 0x45, 0x27, 0x0a, 0x32, 0xb9, 0x03, 0x95, 0x27, 0x7e, 0xc8, 0x78, 0x8c, 0x94, 0x7a, 0xc1, - 0x80, 0x7d, 0x24, 0x61, 0x7a, 0x3a, 0x4a, 0x4c, 0xa8, 0xae, 0x75, 0x6c, 0x19, 0xdc, 0xca, 0x18, - 0xdc, 0xde, 0x19, 0x2d, 0xdc, 0x52, 0x8a, 0x27, 0x62, 0xdc, 0x4d, 0x29, 0xeb, 0x65, 0xab, 0x63, - 0x1b, 0x43, 0xb1, 0x2e, 0xe3, 0xca, 0x8d, 0x29, 0xf1, 0x11, 0x52, 0x7d, 0x64, 0xd8, 0x05, 0x65, - 0xc6, 0x14, 0x48, 0x88, 0x6a, 0x4c, 0x69, 0xec, 0xb9, 0x0f, 0xd5, 0xe7, 0x21, 0xdd, 0x8b, 0x3c, - 0x8f, 0xba, 0xe8, 0x78, 0x2b, 0xcd, 0x3a, 0x5f, 0x43, 0x14, 0x52, 0x83, 0x21, 0x54, 0x5d, 0x43, - 0x8a, 0xba, 0xb0, 0x0f, 0xb3, 0xf9, 0xe5, 0x8f, 0x08, 0x7f, 0x4b, 0x6a, 0xf8, 0xab, 0xad, 0xd4, - 0xb3, 0x45, 0xae, 0xf9, 0x9d, 0x8e, 0xe9, 0x09, 0xf2, 0xfd, 0x15, 0x35, 0x30, 0xfe, 0xb0, 0x00, - 0xb3, 0xf9, 0x51, 0xb2, 0x04, 0x93, 0x32, 0x30, 0x14, 0x30, 0x30, 0x70, 0x1b, 0x9e, 0x14, 0x21, - 0x21, 0x17, 0x08, 0x24, 0x16, 0x37, 0x61, 0xc9, 0xa1, 0x5e, 0x5c, 0x2c, 0x25, 0x26, 0x6c, 0x09, - 0x90, 0x9e, 0x8c, 0x91, 0x06, 0x4c, 0xea, 0x34, 0x8c, 0x5c, 0x26, 0x37, 0x14, 0x38, 0xdb, 0x00, - 0x21, 0xba, 0x1c, 0x69, 0x7c, 0x1f, 0x60, 0x6f, 0x6b, 0x77, 0x93, 0xf6, 0x76, 0x4c, 0x07, 0xfd, - 0xc9, 0x1a, 0x0d, 0x18, 0x2e, 0x63, 0x5a, 0xf8, 0x13, 0x8b, 0x06, 0x4c, 0xf5, 0x27, 0x7c, 0x9c, - 0xdc, 0x86, 0xd2, 0x26, 0xed, 0xa1, 0xd4, 0xd3, 0xcd, 0x4b, 0xfd, 0x58, 0x9b, 0x39, 0xa6, 0x8a, - 0xdf, 0xd2, 0xf9, 0x68, 0xe3, 0xc7, 0x45, 0xb8, 0xc8, 0xb1, 0x57, 0x23, 0x76, 0xe4, 0x07, 0x0e, - 0xeb, 0x9d, 0xe7, 0xc3, 0xfc, 0x71, 0xee, 0x30, 0xbf, 0xa5, 0x6c, 0xb4, 0x2a, 0xe1, 0x58, 0x67, - 0xfa, 0x2f, 0xcb, 0x30, 0x3f, 0x82, 0x8a, 0xdc, 0x85, 0xf2, 0x5e, 0xaf, 0x9b, 0xe4, 0x48, 0xdc, - 0x46, 0xcb, 0xbc, 0x78, 0xf8, 0x32, 0xd6, 0xa6, 0x13, 0x74, 0x3e, 0xae, 0x23, 0x16, 0x59, 0x81, - 0xda, 0x9a, 0x1b, 0x85, 0x4c, 0xa6, 0xef, 0x42, 0x5f, 0x98, 0xc5, 0x59, 0x02, 0x2c, 0xf2, 0x77, - 0x15, 0x89, 0x7c, 0x00, 0xd3, 0x6b, 0x47, 0xd4, 0x3a, 0x76, 0xbc, 0xf6, 0x26, 0xed, 0x85, 0xf5, - 0xd2, 0x62, 0x29, 0xd9, 0x3f, 0x4b, 0xc2, 0x8d, 0x63, 0xda, 0x0b, 0xf5, 0x1c, 0x1a, 0xf9, 0x08, - 0x6a, 0xbb, 0x4e, 0xdb, 0x4b, 0xa8, 0xca, 0x48, 0xb5, 0xc0, 0x53, 0x8a, 0x50, 0x80, 0x91, 0x48, - 0x4d, 0x84, 0x15, 0x74, 0x9e, 0xd0, 0xe9, 0xbe, 0x4b, 0x45, 0x1e, 0x2c, 0x13, 0xba, 0x80, 0x03, - 0xd4, 0x84, 0x0e, 0x31, 0xc8, 0x26, 0x4c, 0xf1, 0x8f, 0xa7, 0x66, 0xb7, 0x3e, 0x89, 0x7e, 0xe5, - 0x8a, 0x7a, 0xea, 0x71, 0xa0, 0xeb, 0x78, 0x6d, 0xf5, 0xe0, 0xbb, 0xd4, 0xe8, 0x98, 0x5d, 0xd5, - 0x34, 0x24, 0x22, 0xf9, 0x1e, 0xd4, 0x32, 0xcb, 0x0e, 0xeb, 0x53, 0xc8, 0xf0, 0x72, 0xc6, 0x30, - 0x1b, 0x6c, 0x6a, 0x92, 0xdf, 0x35, 0xe6, 0x86, 0x5c, 0x16, 0xa3, 0xcb, 0x49, 0x54, 0x81, 0x14, - 0x4e, 0x39, 0xe7, 0x54, 0x79, 0xad, 0x73, 0x2a, 0x9c, 0xe5, 0x9c, 0x1a, 0x3a, 0xd4, 0x14, 0xc1, - 0xc4, 0x89, 0xed, 0xf8, 0x69, 0xa2, 0x2c, 0x4f, 0x2c, 0x87, 0xe8, 0x72, 0x84, 0x68, 0x30, 0xb1, - 0xe5, 0x5b, 0xa6, 0x2b, 0x8f, 0x7e, 0xb5, 0x1f, 0x6b, 0x13, 0x2e, 0x07, 0xe8, 0x02, 0xde, 0xf8, - 0x8f, 0x02, 0xcc, 0xed, 0x04, 0xfe, 0x89, 0xc3, 0x4d, 0x7f, 0xcf, 0x3f, 0xa6, 0xde, 0xfe, 0xb7, - 0xc8, 0x46, 0xb2, 0x0b, 0x05, 0xa4, 0xba, 0xc7, 0xa9, 0x70, 0x17, 0xbe, 0x8c, 0xb5, 0x77, 0xcf, - 0xac, 0x2a, 0x51, 0xfb, 0xc9, 0x2e, 0x29, 0xb5, 0x48, 0x71, 0xfc, 0xe4, 0xe6, 0x8c, 0x5a, 0x44, - 0x83, 0x09, 0x5c, 0xaa, 0x3c, 0xc6, 0x28, 0x15, 0xe3, 0x00, 0x5d, 0xc0, 0x95, 0xf3, 0xf3, 0x93, - 0xe2, 0x90, 0x7c, 0xe7, 0xd8, 0xb1, 0x7c, 0x92, 0x73, 0x2c, 0x4a, 0x95, 0x98, 0x17, 0x71, 0x2c, - 0xcf, 0x62, 0xc2, 0xe5, 0x51, 0x54, 0xbf, 0xc1, 0xcd, 0x6f, 0xfc, 0x6d, 0x11, 0x66, 0x79, 0xa1, - 0xe6, 0x58, 0x38, 0x41, 0x78, 0x9e, 0x55, 0xff, 0xbb, 0x39, 0xd5, 0xdf, 0x54, 0x72, 0x18, 0x45, - 0xc0, 0xb1, 0x14, 0x7f, 0x0c, 0x64, 0x98, 0x86, 0x3c, 0x87, 0x69, 0x15, 0x8a, 0xda, 0xcf, 0x15, - 0x53, 0x83, 0xa7, 0xb4, 0x79, 0x45, 0xce, 0x32, 0x13, 0x22, 0x9d, 0x81, 0x27, 0x20, 0xd4, 0x73, - 0x6c, 0x1a, 0x7f, 0x53, 0x84, 0x19, 0xc5, 0xab, 0x9f, 0xe7, 0x1d, 0x78, 0x98, 0xdb, 0x81, 0x1b, - 0x4a, 0x54, 0xcd, 0xe4, 0x1b, 0x6b, 0x03, 0x1e, 0xc3, 0xa5, 0x21, 0x92, 0xc1, 0x10, 0x59, 0x18, - 0x23, 0x44, 0x8a, 0xa4, 0x45, 0xfc, 0x5e, 0xf3, 0xbd, 0x43, 0xa7, 0xbd, 0x7f, 0xef, 0xeb, 0x98, - 0xb4, 0xa8, 0x12, 0xa2, 0xb6, 0xee, 0x9d, 0xa1, 0xe0, 0xcf, 0x26, 0x60, 0x7e, 0x04, 0x15, 0x59, - 0x85, 0xb9, 0x5d, 0x1a, 0xe2, 0xc2, 0xa9, 0xe5, 0x07, 0xb6, 0xe3, 0xb5, 0xa5, 0x9e, 0xb0, 0x60, - 0x0c, 0xc5, 0x98, 0x11, 0x24, 0x83, 0xfa, 0x10, 0x3a, 0xb6, 0x67, 0x04, 0xe7, 0x8d, 0x96, 0x54, - 0xa1, 0x68, 0xcf, 0xc8, 0x4d, 0xc2, 0xf6, 0x4c, 0x82, 0x40, 0xb6, 0x60, 0x7e, 0x27, 0xf0, 0x5f, - 0xf6, 0x30, 0x43, 0x09, 0x79, 0x51, 0x22, 0x53, 0x19, 0x4e, 0x87, 0x49, 0x49, 0x97, 0x0f, 0x1b, - 0x98, 0xd0, 0x84, 0x06, 0xaf, 0x5f, 0x44, 0x4e, 0x33, 0x8a, 0x8c, 0x7c, 0x08, 0x13, 0xab, 0x91, - 0xed, 0x30, 0xa9, 0x60, 0x25, 0xdf, 0x40, 0xb0, 0x10, 0xb5, 0x39, 0x23, 0x55, 0x33, 0x61, 0x72, - 0xa0, 0x2e, 0x48, 0xc8, 0xa7, 0xdc, 0xe6, 0x1c, 0xea, 0xb1, 0x0d, 0xdb, 0xa5, 0x3c, 0xe2, 0xf9, - 0x11, 0x43, 0x55, 0x97, 0x9a, 0xb7, 0xfb, 0xb1, 0x36, 0x2f, 0x3a, 0x12, 0x86, 0x63, 0xbb, 0xd4, - 0x60, 0x62, 0x38, 0x97, 0xcd, 0x0f, 0x53, 0x93, 0xef, 0xc3, 0x95, 0x96, 0x13, 0x5a, 0xbe, 0xe7, - 0x51, 0x8b, 0x89, 0xd0, 0x68, 0x63, 0x42, 0x2e, 0xea, 0x16, 0xce, 0xf6, 0x9a, 0x9d, 0x22, 0x18, - 0x22, 0xa6, 0xda, 0x06, 0xcf, 0xd1, 0xbf, 0x8c, 0xb5, 0x72, 0xd3, 0xf7, 0x5d, 0x7d, 0x34, 0x07, - 0xbe, 0xda, 0xb4, 0xf5, 0xbb, 0xe1, 0x31, 0x1a, 0x9c, 0x98, 0xae, 0xec, 0xfd, 0xe1, 0x6a, 0x8f, - 0x29, 0xed, 0x1a, 0x26, 0x1f, 0x35, 0x1c, 0x39, 0x9c, 0x5f, 0xed, 0x10, 0x35, 0x79, 0xa4, 0xb0, - 0x5c, 0xf3, 0x23, 0x8f, 0x3d, 0x35, 0x5f, 0x62, 0x46, 0x54, 0x12, 0x15, 0x96, 0xc2, 0xd2, 0xe2, - 0xc3, 0x46, 0xc7, 0x7c, 0xa9, 0x0f, 0x93, 0x90, 0xdf, 0x86, 0x2a, 0x66, 0x2e, 0x3c, 0xc3, 0xad, - 0x57, 0x51, 0x52, 0x7e, 0x86, 0x00, 0xb3, 0x1a, 0xc3, 0x8c, 0xd8, 0x51, 0x2a, 0x5c, 0x86, 0xd8, - 0xf8, 0xbc, 0x04, 0x35, 0x65, 0x93, 0x78, 0xed, 0xa2, 0xa4, 0xcf, 0x58, 0xbb, 0xf0, 0xf4, 0x59, - 0xad, 0x5d, 0x30, 0x71, 0xbe, 0xcb, 0x73, 0xac, 0x36, 0x3f, 0x7c, 0xc2, 0xd6, 0xb0, 0xf1, 0x1a, - 0x20, 0x44, 0x6d, 0xbc, 0x0a, 0x1c, 0xb2, 0x05, 0x73, 0x38, 0x89, 0xb4, 0xda, 0xf0, 0xb9, 0xbe, - 0x21, 0x6d, 0x6d, 0xb1, 0x1f, 0x6b, 0x37, 0xd1, 0x20, 0x0c, 0x69, 0xe5, 0xa1, 0x11, 0x05, 0x8e, - 0xc2, 0x63, 0x88, 0x92, 0xfc, 0xb4, 0x00, 0xb3, 0x08, 0x5c, 0x3f, 0xa1, 0x1e, 0x43, 0x66, 0x65, - 0xd9, 0x1d, 0x48, 0x5b, 0xf5, 0xbb, 0x2c, 0x70, 0xbc, 0xf6, 0x3e, 0xaf, 0x17, 0xc3, 0xe6, 0x1f, - 0x70, 0xcb, 0xfb, 0x45, 0xac, 0xdd, 0x7f, 0xb3, 0xc6, 0xbf, 0x64, 0x12, 0xf6, 0x63, 0x6d, 0x41, - 0x2c, 0x91, 0xe2, 0x84, 0x03, 0x0b, 0x1c, 0x58, 0x0b, 0x79, 0x24, 0x57, 0xb7, 0x67, 0x1e, 0xb8, - 0x14, 0x7d, 0xe6, 0x04, 0x8a, 0x7a, 0x2b, 0xe3, 0xc3, 0xf8, 0x10, 0xfa, 0xcd, 0x21, 0x3e, 0x29, - 0x55, 0xe3, 0x7f, 0x0a, 0x4a, 0x7b, 0xfd, 0xfc, 0xba, 0xcf, 0x07, 0x39, 0xf7, 0x79, 0x2d, 0xa3, - 0x4e, 0x65, 0xe3, 0xc3, 0xa3, 0x1c, 0x67, 0xe3, 0x22, 0xcc, 0xe4, 0x90, 0x30, 0xae, 0xac, 0x5a, - 0x16, 0x0d, 0x43, 0x9d, 0xfe, 0x49, 0x44, 0x43, 0xf6, 0xb5, 0x8c, 0x2b, 0x39, 0x09, 0xc7, 0x8a, - 0x2b, 0xff, 0x5e, 0x84, 0xf9, 0x11, 0x54, 0x5c, 0x37, 0xcf, 0x43, 0x9a, 0xeb, 0x73, 0x45, 0x21, - 0x0d, 0x74, 0x84, 0xf2, 0x6a, 0x41, 0x24, 0xb4, 0x4a, 0x0d, 0x84, 0x09, 0x6d, 0x52, 0xa3, 0xac, - 0x26, 0x17, 0x0e, 0x5c, 0x11, 0xb3, 0x6a, 0xf3, 0x2d, 0x99, 0x86, 0x8f, 0xbe, 0xf6, 0x22, 0x62, - 0x17, 0xa6, 0xd6, 0x02, 0x8a, 0x4d, 0xf6, 0xf2, 0xf8, 0x65, 0x8e, 0x25, 0x48, 0x06, 0xcb, 0x1c, - 0xc9, 0x49, 0xad, 0x9d, 0x26, 0x7e, 0x53, 0xb5, 0x53, 0xe3, 0xcf, 0x0b, 0x03, 0x3a, 0x7c, 0xe4, - 0xb8, 0x8c, 0x06, 0xe4, 0x2a, 0xde, 0xef, 0x08, 0x0d, 0x4e, 0xf6, 0x63, 0xad, 0xe8, 0xd8, 0x7a, - 0x71, 0xa3, 0x95, 0xea, 0xb6, 0x38, 0x52, 0xb7, 0xdf, 0x1e, 0x4f, 0x75, 0xa8, 0x73, 0x54, 0x9d, - 0x54, 0x58, 0xe3, 0xb3, 0x22, 0x4c, 0xef, 0xb8, 0x51, 0xdb, 0xf1, 0x5a, 0x26, 0x33, 0xcf, 0xb3, - 0x7d, 0x7f, 0x94, 0xb3, 0x6f, 0x35, 0x61, 0x4f, 0xc5, 0x1b, 0xcb, 0xb8, 0x7f, 0x52, 0x80, 0x8b, - 0x19, 0x89, 0x68, 0x22, 0x6e, 0x42, 0x99, 0xff, 0x90, 0xc5, 0xc0, 0xed, 0x51, 0xbc, 0x11, 0x71, - 0x29, 0xfd, 0x12, 0xb1, 0x6c, 0xa0, 0x59, 0x8e, 0x4c, 0x16, 0xbe, 0x0d, 0xd5, 0x8c, 0xf3, 0x9b, - 0xdc, 0xce, 0xfd, 0x6b, 0x01, 0xe6, 0x06, 0x85, 0x21, 0xdb, 0x30, 0xc5, 0x39, 0x39, 0x34, 0x29, - 0x55, 0xde, 0x3b, 0x5d, 0xf2, 0x25, 0x89, 0x29, 0x56, 0x88, 0x1b, 0x41, 0x05, 0x44, 0x4f, 0x98, - 0x2c, 0x3c, 0x87, 0x69, 0x15, 0x6b, 0xc4, 0x02, 0x97, 0xf3, 0xfd, 0xd3, 0xeb, 0xa7, 0x6a, 0x43, - 0x5d, 0xfb, 0x4f, 0x73, 0x6b, 0x97, 0xb6, 0x3e, 0xee, 0x4d, 0xc8, 0x0a, 0x54, 0x92, 0x3b, 0x14, - 0xd5, 0xec, 0x02, 0x09, 0xcb, 0x35, 0x69, 0x24, 0x8c, 0x67, 0x0c, 0x62, 0x3e, 0x69, 0x76, 0x98, - 0x31, 0x74, 0x11, 0xa2, 0x66, 0x0c, 0x02, 0xa7, 0xf1, 0x0f, 0x25, 0xb8, 0x9a, 0x2d, 0x4f, 0xdc, - 0x0b, 0xed, 0x98, 0x81, 0xd9, 0x09, 0xcf, 0x38, 0x10, 0x77, 0x86, 0x96, 0x86, 0x1d, 0xf8, 0x64, - 0x69, 0xca, 0x82, 0x1a, 0x03, 0x0b, 0xc2, 0x36, 0x91, 0x58, 0x50, 0xb2, 0x0c, 0xb2, 0x0d, 0xa5, - 0x5d, 0xca, 0x64, 0x7f, 0xfe, 0xfd, 0x51, 0x8a, 0x55, 0x97, 0xb6, 0xb4, 0x4b, 0x99, 0xd8, 0x4a, - 0xec, 0x06, 0x86, 0x54, 0xed, 0xf9, 0x72, 0x46, 0xe4, 0xf7, 0x60, 0x72, 0xfd, 0x65, 0x97, 0x5a, - 0x4c, 0xde, 0x67, 0xdf, 0x3d, 0x93, 0xa5, 0x40, 0x57, 0x6e, 0xb7, 0x29, 0x02, 0x54, 0x95, 0x09, - 0x94, 0x85, 0xfb, 0x50, 0x49, 0xe6, 0x7f, 0x13, 0x2b, 0x5e, 0x78, 0x00, 0x35, 0x65, 0x92, 0x37, - 0x3a, 0x00, 0x7f, 0x56, 0x84, 0x49, 0x1e, 0x2a, 0xce, 0xb3, 0x9b, 0xba, 0x9f, 0x73, 0x53, 0x97, - 0xf3, 0xbd, 0xd2, 0xb1, 0x1c, 0xd4, 0xaf, 0x0b, 0x00, 0x19, 0x32, 0xf9, 0x2e, 0x4c, 0x3d, 0xc3, - 0xd7, 0x0b, 0xc9, 0x05, 0xec, 0x40, 0xff, 0x55, 0x0e, 0x36, 0xaf, 0x27, 0x81, 0xc9, 0x17, 0x00, - 0x55, 0x0b, 0x12, 0x87, 0x3c, 0x86, 0x89, 0x55, 0xd7, 0xf5, 0x5f, 0x0c, 0x5f, 0x8d, 0x70, 0x4e, - 0x6b, 0xbe, 0x67, 0x3b, 0x82, 0xd9, 0x35, 0xc9, 0xec, 0xa2, 0xc9, 0xd1, 0xd5, 0x38, 0x8c, 0xf4, - 0xa4, 0x05, 0xe5, 0x16, 0xf5, 0x92, 0x8b, 0xd4, 0xd3, 0xf9, 0x5c, 0x95, 0x7c, 0x66, 0x6d, 0xea, - 0xf5, 0x72, 0x9e, 0x92, 0x7a, 0xbd, 0xc6, 0xcf, 0xca, 0xa2, 0xd3, 0x9a, 0x2c, 0xef, 0x21, 0x4c, +func init() { proto.RegisterFile("types.proto", fileDescriptor_types_bfe64a187d8e57e3) } + +var fileDescriptor_types_bfe64a187d8e57e3 = []byte{ + // 4005 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x3b, 0x4d, 0x6f, 0x1c, 0xc9, + 0x75, 0x9a, 0x0f, 0x92, 0x33, 0x6f, 0x48, 0x8a, 0x2a, 0xea, 0x63, 0x44, 0x69, 0xd5, 0xdc, 0x51, + 0x76, 0x57, 0xeb, 0xc8, 0x64, 0x4c, 0x65, 0x65, 0x6b, 0xb3, 0xca, 0x2e, 0x87, 0x43, 0x49, 0x34, + 0x29, 0x8a, 0xdb, 0xa4, 0x68, 0x38, 0x5f, 0x9d, 0x66, 0x77, 0x71, 0xd8, 0x60, 0x4f, 0xf7, 0xa4, + 0xbb, 0x9a, 0xd2, 0xdc, 0x8c, 0x24, 0x40, 0x90, 0x18, 0xb0, 0x13, 0x04, 0x86, 0x17, 0x88, 0x0f, + 0x01, 0x72, 0xca, 0x21, 0xb9, 0x06, 0x39, 0xe4, 0x10, 0xc0, 0x07, 0x5d, 0x02, 0xf8, 0x6c, 0x38, + 0xed, 0x64, 0x73, 0x30, 0x30, 0x3f, 0x61, 0x2f, 0x09, 0xea, 0x55, 0x75, 0x77, 0xf5, 0xcc, 0x50, + 0x1c, 0x6d, 0x7c, 0xe1, 0x9e, 0xd8, 0xf3, 0xea, 0xbd, 0x57, 0xef, 0xbd, 0x7a, 0xf5, 0xea, 0xbd, + 0x57, 0x45, 0xa8, 0xb1, 0x5e, 0x97, 0x86, 0x4b, 0xdd, 0xc0, 0x67, 0x3e, 0xa9, 0x84, 0x34, 0x38, + 0x71, 0x2c, 0x1a, 0x2e, 0x5c, 0x6e, 0xfb, 0x6d, 0x1f, 0x81, 0xcb, 0xfc, 0x4b, 0x8c, 0x2f, 0x68, + 0x6d, 0xdf, 0x6f, 0xbb, 0x74, 0x19, 0x7f, 0x1d, 0x44, 0x87, 0xcb, 0xcc, 0xe9, 0xd0, 0x90, 0x99, + 0x9d, 0xae, 0x44, 0x78, 0xd8, 0x76, 0xd8, 0x51, 0x74, 0xb0, 0x64, 0xf9, 0x9d, 0xe5, 0x76, 0x60, + 0x9e, 0x38, 0xcc, 0x64, 0x8e, 0xef, 0x99, 0xee, 0x32, 0xa3, 0x2e, 0xed, 0xfa, 0x01, 0x5b, 0x76, + 0x9d, 0x83, 0xe5, 0x17, 0x81, 0xd9, 0xed, 0xd2, 0x20, 0x4c, 0x3f, 0x04, 0x79, 0xe3, 0x17, 0x05, + 0xa8, 0x6e, 0x52, 0xda, 0x5d, 0x75, 0x9d, 0x13, 0x4a, 0x96, 0x01, 0x76, 0x69, 0x70, 0x42, 0x83, + 0x6d, 0xb3, 0x43, 0xeb, 0x85, 0xc5, 0xc2, 0x9d, 0x6a, 0xf3, 0x62, 0x3f, 0xd6, 0x6a, 0x21, 0x42, + 0x0d, 0xcf, 0xec, 0x50, 0x5d, 0x41, 0x21, 0xbf, 0x09, 0x55, 0xfe, 0x37, 0xec, 0x9a, 0x16, 0xad, + 0x17, 0x11, 0x7f, 0xa6, 0x1f, 0x6b, 0x55, 0x2f, 0x01, 0xea, 0xd9, 0x38, 0x79, 0x17, 0xa6, 0xb6, + 0xa8, 0x19, 0xd2, 0x8d, 0x56, 0xbd, 0xb4, 0x58, 0xb8, 0x53, 0x6a, 0x4e, 0xf7, 0x63, 0xad, 0xe2, + 0x72, 0x90, 0xe1, 0xd8, 0x7a, 0x32, 0x48, 0x36, 0x60, 0x6a, 0xfd, 0x65, 0xd7, 0x09, 0x68, 0x58, + 0x2f, 0x2f, 0x16, 0xee, 0xd4, 0x56, 0x16, 0x96, 0x84, 0x15, 0x96, 0x12, 0x2b, 0x2c, 0xed, 0x25, + 0x56, 0x68, 0xce, 0xbf, 0x8a, 0xb5, 0x0b, 0xfd, 0x58, 0x9b, 0xa2, 0x82, 0xe4, 0xaf, 0x7f, 0xa9, + 0x15, 0xf4, 0x84, 0xbe, 0xf1, 0x57, 0x25, 0xa8, 0x3c, 0xa5, 0xcc, 0xb4, 0x4d, 0x66, 0x92, 0x9b, + 0x50, 0x56, 0xf4, 0xaa, 0xf4, 0x63, 0xad, 0x8c, 0x0a, 0x21, 0x94, 0xdc, 0x1e, 0x56, 0x65, 0xa2, + 0x1f, 0x6b, 0x85, 0xaf, 0xab, 0x2a, 0xfc, 0x0e, 0xd4, 0x5a, 0x34, 0xb4, 0x02, 0xa7, 0xcb, 0x8d, + 0x8c, 0x6a, 0x54, 0x9b, 0xd7, 0xfb, 0xb1, 0x76, 0xc5, 0xce, 0xc0, 0x77, 0xfd, 0x8e, 0xc3, 0x68, + 0xa7, 0xcb, 0x7a, 0xba, 0x8a, 0x4d, 0xb6, 0x60, 0x72, 0xcb, 0x3c, 0xa0, 0x6e, 0x58, 0x9f, 0x58, + 0x2c, 0xdd, 0xa9, 0xad, 0xdc, 0x5a, 0x4a, 0x16, 0x7f, 0x29, 0x91, 0x71, 0x49, 0x20, 0xac, 0x7b, + 0x2c, 0xe8, 0x35, 0x2f, 0xf7, 0x63, 0x6d, 0xce, 0x45, 0x80, 0xc2, 0x52, 0xf2, 0x20, 0xbb, 0x99, + 0x95, 0x26, 0xcf, 0xb4, 0xd2, 0x5b, 0xaf, 0x62, 0xad, 0xd0, 0x8f, 0xb5, 0x4b, 0xd2, 0x4a, 0x19, + 0xbf, 0x9c, 0xbd, 0xc8, 0x22, 0x14, 0x37, 0x5a, 0xf5, 0x29, 0x5c, 0x9d, 0xb9, 0x7e, 0xac, 0x4d, + 0x3b, 0xb6, 0x32, 0x75, 0x71, 0xa3, 0xb5, 0xf0, 0x00, 0x6a, 0x8a, 0x8c, 0x64, 0x0e, 0x4a, 0xc7, + 0xb4, 0x27, 0x4c, 0xaa, 0xf3, 0x4f, 0x72, 0x19, 0x26, 0x4e, 0x4c, 0x37, 0x92, 0x36, 0xd4, 0xc5, + 0x8f, 0x0f, 0x8b, 0xdf, 0x2a, 0x34, 0x7e, 0x54, 0x86, 0x8a, 0xee, 0x0b, 0xff, 0x24, 0xef, 0xc3, + 0xc4, 0x2e, 0x33, 0x59, 0xb2, 0x1a, 0xf3, 0xfd, 0x58, 0xbb, 0x18, 0x72, 0x80, 0x32, 0x9f, 0xc0, + 0xe0, 0xa8, 0x3b, 0x47, 0x66, 0x98, 0xac, 0x0a, 0xa2, 0x76, 0x39, 0x40, 0x45, 0x45, 0x0c, 0xf2, + 0x2e, 0x94, 0x9f, 0xfa, 0x36, 0x95, 0x0b, 0x43, 0xfa, 0xb1, 0x36, 0xdb, 0xf1, 0x6d, 0x15, 0x11, + 0xc7, 0xc9, 0x5d, 0xa8, 0xae, 0x45, 0x41, 0x40, 0x3d, 0xb6, 0xd1, 0x42, 0x27, 0xab, 0x36, 0x67, + 0xfb, 0xb1, 0x06, 0x96, 0x00, 0x72, 0x77, 0xcc, 0x10, 0xb8, 0xa9, 0x77, 0x99, 0x19, 0x30, 0x6a, + 0xd7, 0x27, 0xc6, 0x32, 0x35, 0x77, 0xc8, 0x4b, 0xa1, 0x20, 0x19, 0x34, 0xb5, 0xe4, 0x44, 0x9e, + 0x40, 0xed, 0x71, 0x60, 0x5a, 0x74, 0x87, 0x06, 0x8e, 0x6f, 0xe3, 0x1a, 0x96, 0x9a, 0xef, 0xf6, + 0x63, 0xed, 0x6a, 0x9b, 0x83, 0x8d, 0x2e, 0xc2, 0x33, 0xea, 0x2f, 0x62, 0xad, 0xd2, 0x8a, 0x02, + 0xb4, 0x9e, 0xae, 0x92, 0x92, 0x3f, 0xe6, 0x4b, 0x12, 0x32, 0x34, 0x2d, 0xb5, 0x71, 0xf5, 0x5e, + 0x2f, 0x62, 0x43, 0x8a, 0x78, 0xd5, 0x35, 0x43, 0x66, 0x04, 0x82, 0x6e, 0x40, 0x4e, 0x95, 0x25, + 0xd1, 0xa1, 0xb2, 0x6b, 0x1d, 0x51, 0x3b, 0x72, 0x69, 0xbd, 0x22, 0xd9, 0xa7, 0xbe, 0x9b, 0x2c, + 0x69, 0x82, 0xd1, 0x5c, 0x90, 0xec, 0x49, 0x28, 0x21, 0x8a, 0xf9, 0x53, 0x3e, 0x1f, 0x56, 0x3e, + 0xfb, 0x7b, 0xed, 0xc2, 0xf7, 0x7e, 0xb1, 0x78, 0xa1, 0xf1, 0x2f, 0x45, 0x98, 0x1b, 0x64, 0x42, + 0x0e, 0x61, 0xe6, 0x79, 0xd7, 0x36, 0x19, 0x5d, 0x73, 0x1d, 0xea, 0xb1, 0x10, 0xfd, 0xe4, 0xf5, + 0x6a, 0xfd, 0x86, 0x9c, 0xb7, 0x1e, 0x21, 0xa1, 0x61, 0x09, 0xca, 0x01, 0xc5, 0xf2, 0x6c, 0xb3, + 0x79, 0x44, 0x54, 0x0b, 0xd1, 0xc9, 0xde, 0x6c, 0x1e, 0x11, 0x1c, 0x4f, 0x99, 0x47, 0xb2, 0x95, + 0x3e, 0xe4, 0xd9, 0x07, 0x3d, 0x74, 0xce, 0xf1, 0x7d, 0x88, 0x93, 0x8c, 0xf0, 0x21, 0x0e, 0x6e, + 0xfc, 0xaa, 0x00, 0xb3, 0x3a, 0x0d, 0xfd, 0x28, 0xb0, 0xe8, 0x13, 0x6a, 0xda, 0x34, 0xe0, 0x3b, + 0x60, 0xd3, 0xf1, 0x6c, 0xb9, 0xad, 0x70, 0x07, 0x1c, 0x3b, 0x9e, 0xba, 0x8b, 0x71, 0x9c, 0xfc, + 0x16, 0x4c, 0xed, 0x46, 0x07, 0x88, 0x2a, 0xb6, 0xd5, 0x55, 0x5c, 0xb1, 0xe8, 0xc0, 0x18, 0x40, + 0x4f, 0xd0, 0xc8, 0x32, 0x4c, 0xed, 0xd3, 0x20, 0xcc, 0xe2, 0xde, 0x15, 0x2e, 0xe1, 0x89, 0x00, + 0xa9, 0x04, 0x12, 0x8b, 0x7c, 0x3b, 0x8b, 0xbd, 0x32, 0x90, 0x93, 0xe1, 0x88, 0x97, 0x79, 0x4b, + 0x47, 0x42, 0x54, 0x6f, 0x49, 0xb0, 0x1a, 0x7f, 0x51, 0x84, 0x8a, 0x30, 0xe5, 0xfe, 0x0a, 0x0f, + 0xe4, 0x8a, 0x8e, 0x18, 0xc8, 0xb9, 0xd0, 0x5f, 0x5a, 0xb3, 0x77, 0x06, 0x35, 0xab, 0xf1, 0x03, + 0x45, 0x6a, 0x96, 0xe9, 0xf3, 0xc9, 0x58, 0xfa, 0xcc, 0x49, 0x7d, 0x2a, 0x89, 0x3e, 0x99, 0x16, + 0xe4, 0x5b, 0x50, 0xde, 0xed, 0x52, 0x4b, 0x46, 0x91, 0xab, 0x19, 0xb5, 0x50, 0x8d, 0x8f, 0xed, + 0xaf, 0x34, 0xa7, 0x25, 0x87, 0x72, 0xd8, 0xa5, 0x96, 0x8e, 0x14, 0xca, 0x6e, 0xf9, 0x59, 0x09, + 0xa6, 0x55, 0x74, 0x6e, 0x8d, 0x55, 0xdb, 0x0e, 0x54, 0x6b, 0x98, 0xb6, 0x1d, 0xe8, 0x08, 0x25, + 0x0f, 0x00, 0x76, 0xa2, 0x03, 0xd7, 0xb1, 0x10, 0xa7, 0x98, 0x1d, 0x58, 0x5d, 0x84, 0x1a, 0x1c, + 0x55, 0xb1, 0x89, 0x82, 0x4c, 0xee, 0x40, 0xe5, 0x89, 0x1f, 0x32, 0x7e, 0x46, 0x4a, 0xbb, 0xe0, + 0x81, 0x7d, 0x24, 0x61, 0x7a, 0x3a, 0x4a, 0x4c, 0xa8, 0xae, 0x75, 0x6c, 0x79, 0xb8, 0x95, 0xf1, + 0x70, 0x7b, 0x67, 0xb4, 0x72, 0x4b, 0x29, 0x9e, 0x38, 0xe3, 0x6e, 0x4a, 0x5d, 0x2f, 0x5b, 0x1d, + 0xdb, 0x18, 0x3a, 0xeb, 0x32, 0xae, 0xdc, 0x99, 0x92, 0x18, 0x21, 0xcd, 0x47, 0x86, 0x43, 0x50, + 0xe6, 0x4c, 0x81, 0x84, 0xa8, 0xce, 0x94, 0x9e, 0x3d, 0xf7, 0xa1, 0xfa, 0x3c, 0xa4, 0x7b, 0x91, + 0xe7, 0x51, 0x17, 0x03, 0x6f, 0xa5, 0x59, 0xe7, 0x32, 0x44, 0x21, 0x35, 0x18, 0x42, 0x55, 0x19, + 0x52, 0xd4, 0x85, 0x7d, 0x98, 0xcd, 0x8b, 0x3f, 0xe2, 0xf8, 0x5b, 0x52, 0x8f, 0xbf, 0xda, 0x4a, + 0x3d, 0x13, 0x72, 0xcd, 0xef, 0x74, 0x4c, 0x4f, 0x90, 0xef, 0xaf, 0xa8, 0x07, 0xe3, 0xf7, 0x0b, + 0x30, 0x9b, 0x1f, 0x25, 0x4b, 0x30, 0x29, 0x0f, 0x86, 0x02, 0x1e, 0x0c, 0xdc, 0x87, 0x27, 0xc5, + 0x91, 0x90, 0x3b, 0x08, 0x24, 0x16, 0x77, 0x61, 0xc9, 0xa1, 0x5e, 0x5c, 0x2c, 0x25, 0x2e, 0x6c, + 0x09, 0x90, 0x9e, 0x8c, 0x91, 0x06, 0x4c, 0xea, 0x34, 0x8c, 0x5c, 0x26, 0x17, 0x14, 0x38, 0xdb, + 0x00, 0x21, 0xba, 0x1c, 0x69, 0x7c, 0x17, 0x60, 0x6f, 0x6b, 0x77, 0x93, 0xf6, 0x76, 0x4c, 0x07, + 0xe3, 0xc9, 0x1a, 0x0d, 0x18, 0x8a, 0x31, 0x2d, 0xe2, 0x89, 0x45, 0x03, 0xa6, 0xc6, 0x13, 0x3e, + 0x4e, 0x6e, 0x43, 0x69, 0x93, 0xf6, 0x50, 0xeb, 0xe9, 0xe6, 0xa5, 0x7e, 0xac, 0xcd, 0x1c, 0x53, + 0x25, 0x6e, 0xe9, 0x7c, 0xb4, 0xf1, 0xa3, 0x22, 0x5c, 0xe4, 0xd8, 0xab, 0x11, 0x3b, 0xf2, 0x03, + 0x87, 0xf5, 0xce, 0xf3, 0x66, 0xfe, 0x38, 0xb7, 0x99, 0xdf, 0x52, 0x16, 0x5a, 0xd5, 0x70, 0xac, + 0x3d, 0xfd, 0x97, 0x65, 0x98, 0x1f, 0x41, 0x45, 0xee, 0x42, 0x79, 0xaf, 0xd7, 0x4d, 0x72, 0x24, + 0xee, 0xa3, 0x65, 0x5e, 0x3c, 0x7c, 0x11, 0x6b, 0xd3, 0x09, 0x3a, 0x1f, 0xd7, 0x11, 0x8b, 0xac, + 0x40, 0x6d, 0xcd, 0x8d, 0x42, 0x26, 0xd3, 0x77, 0x61, 0x2f, 0xcc, 0xe2, 0x2c, 0x01, 0x16, 0xf9, + 0xbb, 0x8a, 0x44, 0x3e, 0x80, 0xe9, 0xb5, 0x23, 0x6a, 0x1d, 0x3b, 0x5e, 0x7b, 0x93, 0xf6, 0xc2, + 0x7a, 0x69, 0xb1, 0x94, 0xac, 0x9f, 0x25, 0xe1, 0xc6, 0x31, 0xed, 0x85, 0x7a, 0x0e, 0x8d, 0x7c, + 0x04, 0xb5, 0x5d, 0xa7, 0xed, 0x25, 0x54, 0x65, 0xa4, 0x5a, 0xe0, 0x29, 0x45, 0x28, 0xc0, 0x48, + 0xa4, 0x26, 0xc2, 0x0a, 0x3a, 0x4f, 0xe8, 0x74, 0xdf, 0xa5, 0x22, 0x0f, 0x96, 0x09, 0x5d, 0xc0, + 0x01, 0x6a, 0x42, 0x87, 0x18, 0x64, 0x13, 0xa6, 0xf8, 0xc7, 0x53, 0xb3, 0x5b, 0x9f, 0xc4, 0xb8, + 0x72, 0x45, 0xdd, 0xf5, 0x38, 0xd0, 0x75, 0xbc, 0xb6, 0xba, 0xf1, 0x5d, 0x6a, 0x74, 0xcc, 0xae, + 0xea, 0x1a, 0x12, 0x91, 0x7c, 0x07, 0x6a, 0x99, 0x67, 0x87, 0xf5, 0x29, 0x64, 0x78, 0x39, 0x63, + 0x98, 0x0d, 0x36, 0x35, 0xc9, 0xef, 0x1a, 0x73, 0x43, 0xae, 0x8b, 0xd1, 0xe5, 0x24, 0xaa, 0x42, + 0x0a, 0xa7, 0x5c, 0x70, 0xaa, 0xbc, 0x36, 0x38, 0x15, 0xce, 0x0a, 0x4e, 0x0d, 0x1d, 0x6a, 0x8a, + 0x62, 0x62, 0xc7, 0x76, 0xfc, 0x34, 0x51, 0x96, 0x3b, 0x96, 0x43, 0x74, 0x39, 0x42, 0x34, 0x98, + 0xd8, 0xf2, 0x2d, 0xd3, 0x95, 0x5b, 0xbf, 0xda, 0x8f, 0xb5, 0x09, 0x97, 0x03, 0x74, 0x01, 0x6f, + 0xfc, 0x67, 0x01, 0xe6, 0x76, 0x02, 0xff, 0xc4, 0xe1, 0xae, 0xbf, 0xe7, 0x1f, 0x53, 0x6f, 0xff, + 0x1b, 0x64, 0x23, 0x59, 0x85, 0x02, 0x52, 0xdd, 0xe3, 0x54, 0xb8, 0x0a, 0x5f, 0xc4, 0xda, 0xbb, + 0x67, 0x56, 0x95, 0x68, 0xfd, 0x64, 0x95, 0x94, 0x5a, 0xa4, 0x38, 0x7e, 0x72, 0x73, 0x46, 0x2d, + 0xa2, 0xc1, 0x04, 0x8a, 0x2a, 0xb7, 0x31, 0x6a, 0xc5, 0x38, 0x40, 0x17, 0x70, 0x65, 0xff, 0xfc, + 0xb8, 0x38, 0xa4, 0xdf, 0x39, 0x0e, 0x2c, 0x9f, 0xe4, 0x02, 0x8b, 0x52, 0x25, 0xe6, 0x55, 0x1c, + 0x2b, 0xb2, 0x98, 0x70, 0x79, 0x14, 0xd5, 0xaf, 0x71, 0xf1, 0x1b, 0x7f, 0x5b, 0x84, 0x59, 0x5e, + 0xa8, 0x39, 0x16, 0x4e, 0x10, 0x9e, 0x67, 0xd3, 0xff, 0x6e, 0xce, 0xf4, 0x37, 0x95, 0x1c, 0x46, + 0x51, 0x70, 0x2c, 0xc3, 0x1f, 0x03, 0x19, 0xa6, 0x21, 0xcf, 0x61, 0x5a, 0x85, 0xa2, 0xf5, 0x73, + 0xc5, 0xd4, 0xe0, 0x2e, 0x6d, 0x5e, 0x91, 0xb3, 0xcc, 0x84, 0x48, 0x67, 0xe0, 0x0e, 0x08, 0xf5, + 0x1c, 0x9b, 0xc6, 0xdf, 0x14, 0x61, 0x46, 0x89, 0xea, 0xe7, 0x79, 0x05, 0x1e, 0xe6, 0x56, 0xe0, + 0x86, 0x72, 0xaa, 0x66, 0xfa, 0x8d, 0xb5, 0x00, 0x8f, 0xe1, 0xd2, 0x10, 0xc9, 0xe0, 0x11, 0x59, + 0x18, 0xe3, 0x88, 0x14, 0x49, 0x8b, 0xf8, 0xbd, 0xe6, 0x7b, 0x87, 0x4e, 0x7b, 0xff, 0xde, 0x57, + 0x31, 0x69, 0x51, 0x35, 0x44, 0x6b, 0xdd, 0x3b, 0xc3, 0xc0, 0x3f, 0x9c, 0x80, 0xf9, 0x11, 0x54, + 0x64, 0x15, 0xe6, 0x76, 0x69, 0x88, 0x82, 0x53, 0xcb, 0x0f, 0x6c, 0xc7, 0x6b, 0x4b, 0x3b, 0x61, + 0xc1, 0x18, 0x8a, 0x31, 0x23, 0x48, 0x06, 0xf5, 0x21, 0x74, 0x6c, 0xcf, 0x08, 0xce, 0x1b, 0x2d, + 0x69, 0x42, 0xd1, 0x9e, 0x91, 0x8b, 0x84, 0xed, 0x99, 0x04, 0x81, 0x6c, 0xc1, 0xfc, 0x4e, 0xe0, + 0xbf, 0xec, 0x61, 0x86, 0x12, 0xf2, 0xa2, 0x44, 0xa6, 0x32, 0x9c, 0x0e, 0x93, 0x92, 0x2e, 0x1f, + 0x36, 0x30, 0xa1, 0x09, 0x0d, 0x5e, 0xbf, 0x88, 0x9c, 0x66, 0x14, 0x19, 0xf9, 0x10, 0x26, 0x56, + 0x23, 0xdb, 0x61, 0xd2, 0xc0, 0x4a, 0xbe, 0x81, 0x60, 0xa1, 0x6a, 0x73, 0x46, 0x9a, 0x66, 0xc2, + 0xe4, 0x40, 0x5d, 0x90, 0x90, 0x4f, 0xb9, 0xcf, 0x39, 0xd4, 0x63, 0x1b, 0xb6, 0x4b, 0xf9, 0x89, + 0xe7, 0x47, 0x0c, 0x4d, 0x5d, 0x6a, 0xde, 0xee, 0xc7, 0xda, 0xbc, 0xe8, 0x48, 0x18, 0x8e, 0xed, + 0x52, 0x83, 0x89, 0xe1, 0x5c, 0x36, 0x3f, 0x4c, 0x4d, 0xbe, 0x0b, 0x57, 0x5a, 0x4e, 0x68, 0xf9, + 0x9e, 0x47, 0x2d, 0x26, 0x8e, 0x46, 0x1b, 0x13, 0x72, 0x51, 0xb7, 0x70, 0xb6, 0xd7, 0xec, 0x14, + 0xc1, 0x10, 0x67, 0xaa, 0x6d, 0xf0, 0x1c, 0xfd, 0x8b, 0x58, 0x2b, 0x37, 0x7d, 0xdf, 0xd5, 0x47, + 0x73, 0xe0, 0xd2, 0xa6, 0xad, 0xdf, 0x0d, 0x8f, 0xd1, 0xe0, 0xc4, 0x74, 0x65, 0xef, 0x0f, 0xa5, + 0x3d, 0xa6, 0xb4, 0x6b, 0x98, 0x7c, 0xd4, 0x70, 0xe4, 0x70, 0x5e, 0xda, 0x21, 0x6a, 0xf2, 0x48, + 0x61, 0xb9, 0xe6, 0x47, 0x1e, 0x7b, 0x6a, 0xbe, 0xc4, 0x8c, 0xa8, 0x24, 0x2a, 0x2c, 0x85, 0xa5, + 0xc5, 0x87, 0x8d, 0x8e, 0xf9, 0x52, 0x1f, 0x26, 0x21, 0xbf, 0x0d, 0x55, 0xcc, 0x5c, 0x78, 0x86, + 0x5b, 0xaf, 0xa2, 0xa6, 0x7c, 0x0f, 0x01, 0x66, 0x35, 0x86, 0x19, 0xb1, 0xa3, 0x54, 0xb9, 0x0c, + 0xb1, 0xf1, 0x59, 0x09, 0x6a, 0xca, 0x22, 0xf1, 0xda, 0x45, 0x49, 0x9f, 0xb1, 0x76, 0xe1, 0xe9, + 0xb3, 0x5a, 0xbb, 0x60, 0xe2, 0x7c, 0x97, 0xe7, 0x58, 0x6d, 0xbe, 0xf9, 0x84, 0xaf, 0x61, 0xe3, + 0x35, 0x40, 0x88, 0xda, 0x78, 0x15, 0x38, 0x64, 0x0b, 0xe6, 0x70, 0x12, 0xe9, 0xb5, 0xe1, 0x73, + 0x7d, 0x43, 0xfa, 0xda, 0x62, 0x3f, 0xd6, 0x6e, 0xa2, 0x43, 0x18, 0xd2, 0xcb, 0x43, 0x23, 0x0a, + 0x1c, 0x85, 0xc7, 0x10, 0x25, 0xf9, 0x49, 0x01, 0x66, 0x11, 0xb8, 0x7e, 0x42, 0x3d, 0x86, 0xcc, + 0xca, 0xb2, 0x3b, 0x90, 0xb6, 0xea, 0x77, 0x59, 0xe0, 0x78, 0xed, 0x7d, 0x5e, 0x2f, 0x86, 0xcd, + 0x3f, 0xe0, 0x9e, 0xf7, 0xf3, 0x58, 0xbb, 0xff, 0x66, 0x8d, 0x7f, 0xc9, 0x24, 0xec, 0xc7, 0xda, + 0x82, 0x10, 0x91, 0xe2, 0x84, 0x03, 0x02, 0x0e, 0xc8, 0x42, 0x1e, 0x49, 0xe9, 0xf6, 0xcc, 0x03, + 0x97, 0x62, 0xcc, 0x9c, 0x40, 0x55, 0x6f, 0x65, 0x7c, 0x18, 0x1f, 0xc2, 0xb8, 0x39, 0xc4, 0x27, + 0xa5, 0x6a, 0xfc, 0x6f, 0x41, 0x69, 0xaf, 0x9f, 0xdf, 0xf0, 0xf9, 0x20, 0x17, 0x3e, 0xaf, 0x65, + 0xd4, 0xa9, 0x6e, 0x7c, 0x78, 0x54, 0xe0, 0x6c, 0x5c, 0x84, 0x99, 0x1c, 0x12, 0x9e, 0x2b, 0xab, + 0x96, 0x45, 0xc3, 0x50, 0xa7, 0x7f, 0x12, 0xd1, 0x90, 0x7d, 0x25, 0xcf, 0x95, 0x9c, 0x86, 0x63, + 0x9d, 0x2b, 0xff, 0x5e, 0x84, 0xf9, 0x11, 0x54, 0xdc, 0x36, 0xcf, 0x43, 0x9a, 0xeb, 0x73, 0x45, + 0x21, 0x0d, 0x74, 0x84, 0xf2, 0x6a, 0x41, 0x24, 0xb4, 0x4a, 0x0d, 0x84, 0x09, 0x6d, 0x52, 0xa3, + 0xac, 0x26, 0x17, 0x0e, 0xdc, 0x10, 0xb3, 0x6a, 0xf3, 0x2d, 0x99, 0x86, 0x8f, 0xbe, 0xf6, 0x22, + 0x62, 0x17, 0xa6, 0xd6, 0x02, 0x8a, 0x4d, 0xf6, 0xf2, 0xf8, 0x65, 0x8e, 0x25, 0x48, 0x06, 0xcb, + 0x1c, 0xc9, 0x49, 0xad, 0x9d, 0x26, 0x7e, 0x5d, 0xb5, 0x53, 0xe3, 0xcf, 0x0b, 0x03, 0x36, 0x7c, + 0xe4, 0xb8, 0x8c, 0x06, 0xe4, 0x2a, 0xde, 0xef, 0x08, 0x0b, 0x4e, 0xf6, 0x63, 0xad, 0xe8, 0xd8, + 0x7a, 0x71, 0xa3, 0x95, 0xda, 0xb6, 0x38, 0xd2, 0xb6, 0xdf, 0x1c, 0xcf, 0x74, 0x68, 0x73, 0x34, + 0x9d, 0x34, 0x58, 0xe3, 0x87, 0x45, 0x98, 0xde, 0x71, 0xa3, 0xb6, 0xe3, 0xb5, 0x4c, 0x66, 0x9e, + 0x67, 0xff, 0xfe, 0x28, 0xe7, 0xdf, 0x6a, 0xc2, 0x9e, 0xaa, 0x37, 0x96, 0x73, 0xff, 0xb8, 0x00, + 0x17, 0x33, 0x12, 0xd1, 0x44, 0xdc, 0x84, 0x32, 0xff, 0x21, 0x8b, 0x81, 0xdb, 0xa3, 0x78, 0x23, + 0xe2, 0x52, 0xfa, 0x25, 0xce, 0xb2, 0x81, 0x66, 0x39, 0x32, 0x59, 0xf8, 0x26, 0x54, 0x33, 0xce, + 0x6f, 0x72, 0x3b, 0xf7, 0xaf, 0x05, 0x98, 0x1b, 0x54, 0x86, 0x6c, 0xc3, 0x14, 0xe7, 0xe4, 0xd0, + 0xa4, 0x54, 0x79, 0xef, 0x74, 0xcd, 0x97, 0x24, 0xa6, 0x90, 0x10, 0x17, 0x82, 0x0a, 0x88, 0x9e, + 0x30, 0x59, 0x78, 0x0e, 0xd3, 0x2a, 0xd6, 0x08, 0x01, 0x97, 0xf3, 0xfd, 0xd3, 0xeb, 0xa7, 0x5a, + 0x43, 0x95, 0xfd, 0x27, 0x39, 0xd9, 0xa5, 0xaf, 0x8f, 0x7b, 0x13, 0xb2, 0x02, 0x95, 0xe4, 0x0e, + 0x45, 0x75, 0xbb, 0x40, 0xc2, 0x72, 0x4d, 0x1a, 0x09, 0xe3, 0x19, 0x83, 0x98, 0x4f, 0xba, 0x1d, + 0x66, 0x0c, 0x5d, 0x84, 0xa8, 0x19, 0x83, 0xc0, 0x69, 0xfc, 0x43, 0x09, 0xae, 0x66, 0xe2, 0x89, + 0x7b, 0xa1, 0x1d, 0x33, 0x30, 0x3b, 0xe1, 0x19, 0x1b, 0xe2, 0xce, 0x90, 0x68, 0xd8, 0x81, 0x4f, + 0x44, 0x53, 0x04, 0x6a, 0x0c, 0x08, 0x84, 0x6d, 0x22, 0x21, 0x50, 0x22, 0x06, 0xd9, 0x86, 0xd2, + 0x2e, 0x65, 0xb2, 0x3f, 0xff, 0xfe, 0x28, 0xc3, 0xaa, 0xa2, 0x2d, 0xed, 0x52, 0x26, 0x96, 0x12, + 0xbb, 0x81, 0x21, 0x55, 0x7b, 0xbe, 0x9c, 0x11, 0xf9, 0x3d, 0x98, 0x5c, 0x7f, 0xd9, 0xa5, 0x16, + 0x93, 0xf7, 0xd9, 0x77, 0xcf, 0x64, 0x29, 0xd0, 0x95, 0xdb, 0x6d, 0x8a, 0x00, 0xd5, 0x64, 0x02, + 0x65, 0xe1, 0x3e, 0x54, 0x92, 0xf9, 0xdf, 0xc4, 0x8b, 0x17, 0x1e, 0x40, 0x4d, 0x99, 0xe4, 0x8d, + 0x36, 0xc0, 0x9f, 0x15, 0x61, 0x92, 0x1f, 0x15, 0xe7, 0x39, 0x4c, 0xdd, 0xcf, 0x85, 0xa9, 0xcb, + 0xf9, 0x5e, 0xe9, 0x58, 0x01, 0xea, 0x57, 0x05, 0x80, 0x0c, 0x99, 0x7c, 0x1b, 0xa6, 0x9e, 0xe1, + 0xeb, 0x85, 0xe4, 0x02, 0x76, 0xa0, 0xff, 0x2a, 0x07, 0x9b, 0xd7, 0x93, 0x83, 0xc9, 0x17, 0x00, + 0xd5, 0x0a, 0x12, 0x87, 0x3c, 0x86, 0x89, 0x55, 0xd7, 0xf5, 0x5f, 0x0c, 0x5f, 0x8d, 0x70, 0x4e, + 0x6b, 0xbe, 0x67, 0x3b, 0x82, 0xd9, 0x35, 0xc9, 0xec, 0xa2, 0xc9, 0xd1, 0xd5, 0x73, 0x18, 0xe9, + 0x49, 0x0b, 0xca, 0x2d, 0xea, 0x25, 0x17, 0xa9, 0xa7, 0xf3, 0xb9, 0x2a, 0xf9, 0xcc, 0xda, 0xd4, + 0xeb, 0xe5, 0x22, 0x25, 0xf5, 0x7a, 0x8d, 0x9f, 0x96, 0x45, 0xa7, 0x35, 0x11, 0xef, 0x21, 0x4c, 0x3f, 0xf2, 0x83, 0x17, 0x66, 0x60, 0xaf, 0xb6, 0xa9, 0x27, 0x6e, 0x3c, 0x2a, 0x78, 0x57, 0x36, 0x73, 0x28, 0xe0, 0x86, 0xc9, 0x07, 0xd2, 0xca, 0x23, 0x87, 0x4e, 0x9e, 0xc1, 0xcc, 0x53, 0xf3, - 0xa5, 0x4c, 0xed, 0xf7, 0xf6, 0xb6, 0x50, 0xca, 0x52, 0xf3, 0xfd, 0x7e, 0xac, 0x5d, 0xef, 0x98, + 0xa5, 0x4c, 0xed, 0xf7, 0xf6, 0xb6, 0x50, 0xcb, 0x52, 0xf3, 0xfd, 0x7e, 0xac, 0x5d, 0xef, 0x98, 0x2f, 0x93, 0x8a, 0xc0, 0x60, 0xcc, 0x3d, 0xe5, 0x52, 0x3f, 0x4f, 0x4f, 0x5c, 0x98, 0xdd, 0xf1, - 0x03, 0x26, 0x27, 0xe1, 0x55, 0xb4, 0x90, 0x77, 0x3e, 0x93, 0x97, 0x2f, 0x03, 0xcb, 0x82, 0xe6, - 0xf2, 0xab, 0x58, 0x2b, 0xfc, 0x22, 0xd6, 0x80, 0x83, 0x84, 0x44, 0x7c, 0x62, 0x5e, 0x06, 0x18, - 0x87, 0x29, 0x07, 0x35, 0x41, 0xcf, 0xf3, 0x26, 0x0f, 0xe1, 0x12, 0x2f, 0x0a, 0x9d, 0x43, 0xc7, - 0x32, 0x19, 0x7d, 0xe4, 0x07, 0x1d, 0x93, 0xc9, 0x97, 0x11, 0xf8, 0x02, 0x88, 0x17, 0x94, 0x9c, - 0x53, 0xc7, 0x64, 0xfa, 0x30, 0x26, 0xf9, 0xfd, 0xd3, 0x2b, 0xdf, 0x6f, 0xf6, 0x63, 0xed, 0xad, - 0x11, 0x95, 0xef, 0x29, 0x5a, 0x18, 0x51, 0x03, 0xb7, 0x5f, 0x5f, 0x03, 0x7f, 0x4b, 0xf6, 0xd5, - 0xdf, 0x3e, 0xa5, 0x0e, 0xce, 0x4d, 0xf4, 0xda, 0x8a, 0x78, 0x05, 0x4a, 0xcd, 0x9d, 0x47, 0x78, - 0x31, 0x20, 0xab, 0x39, 0xea, 0x1d, 0x99, 0x9e, 0x45, 0xed, 0xac, 0x5d, 0xa1, 0x7a, 0xc1, 0xe6, - 0xce, 0xa3, 0xc6, 0x67, 0x25, 0x98, 0xcd, 0xdb, 0x1d, 0x77, 0xc6, 0x5b, 0x7e, 0xdb, 0xf1, 0x92, - 0xee, 0x2a, 0x3a, 0x63, 0x17, 0x21, 0xba, 0x1c, 0x21, 0xef, 0x00, 0xa4, 0xe5, 0x40, 0x92, 0xb4, - 0xca, 0xf7, 0x46, 0xca, 0x00, 0xf9, 0x23, 0x80, 0x6d, 0xdf, 0xa6, 0xf2, 0x6a, 0xb5, 0x24, 0x8f, - 0x60, 0x5a, 0xd4, 0x89, 0xeb, 0x42, 0x51, 0x18, 0xbe, 0x27, 0x0b, 0x43, 0xf9, 0x40, 0xa8, 0x1f, - 0x6b, 0x57, 0x3c, 0xdf, 0xa6, 0xc3, 0xb7, 0xaa, 0x0a, 0x47, 0xf2, 0x10, 0x26, 0xf4, 0xc8, 0xa5, - 0xc9, 0xad, 0xed, 0xac, 0x72, 0x96, 0x22, 0x97, 0x66, 0x27, 0x31, 0x88, 0x06, 0xaf, 0x67, 0x38, - 0x80, 0x7c, 0x0c, 0xb0, 0x19, 0x1d, 0xd0, 0xc7, 0x81, 0x1f, 0x75, 0x93, 0xeb, 0x1c, 0xad, 0x1f, - 0x6b, 0x37, 0x8e, 0xa3, 0x03, 0x1a, 0x78, 0x94, 0xd1, 0xd0, 0x68, 0xe3, 0xa0, 0x3a, 0x7f, 0x46, - 0x42, 0x74, 0x98, 0x92, 0x39, 0xa4, 0x7c, 0xc5, 0xf4, 0xf6, 0x29, 0xa5, 0x83, 0x72, 0xac, 0xb1, - 0x8d, 0x14, 0x08, 0x70, 0xee, 0x9a, 0x47, 0x80, 0x1a, 0x2d, 0xb8, 0x76, 0x0a, 0x69, 0x76, 0xf3, - 0x54, 0x38, 0xeb, 0xe6, 0xa9, 0xf1, 0xb3, 0x02, 0x94, 0xb9, 0x90, 0xe4, 0x03, 0xa8, 0x26, 0x61, - 0x36, 0xa1, 0xbb, 0xd6, 0x8f, 0xb5, 0xf9, 0x24, 0x0a, 0xe7, 0x2e, 0xac, 0x53, 0x4c, 0x3e, 0xd5, - 0x3e, 0x0d, 0x0e, 0x92, 0xbd, 0xc5, 0xa9, 0x4e, 0x38, 0x40, 0x9d, 0x0a, 0x31, 0x38, 0xea, 0xf7, - 0x8e, 0x68, 0x90, 0xdc, 0xb2, 0x23, 0xea, 0x0b, 0x0e, 0x50, 0x51, 0x11, 0x83, 0x2c, 0xc3, 0xd4, - 0xaa, 0x25, 0xfc, 0x71, 0x19, 0xf9, 0xa2, 0x32, 0x4c, 0x6b, 0xc8, 0xe9, 0x4a, 0xac, 0xc6, 0xdb, - 0x50, 0x4d, 0xbd, 0x04, 0x0f, 0x7e, 0xf8, 0x21, 0x7c, 0x9b, 0x2e, 0x7e, 0x60, 0xe0, 0xe3, 0x79, - 0xfe, 0x79, 0x6e, 0x1b, 0x9f, 0x1a, 0xf8, 0xb8, 0x60, 0x63, 0xf5, 0x8b, 0xff, 0x79, 0x12, 0x20, - 0x43, 0x26, 0x14, 0x66, 0x9f, 0x6d, 0xb4, 0xd6, 0x36, 0x6c, 0xea, 0x31, 0x87, 0x65, 0x09, 0xb0, - 0x92, 0xfa, 0xaf, 0xbf, 0x64, 0x34, 0xf0, 0x4c, 0x57, 0xe2, 0xf4, 0x9a, 0x6f, 0xcb, 0x09, 0xae, - 0xfb, 0x8e, 0x6d, 0x19, 0x4e, 0x4a, 0xaa, 0xba, 0xdd, 0x3c, 0x53, 0x3e, 0xcd, 0xee, 0xea, 0xd3, - 0x2d, 0x65, 0x9a, 0xe2, 0xf8, 0xd3, 0x84, 0x66, 0xc7, 0x3d, 0x65, 0x9a, 0x3c, 0x53, 0x72, 0x0c, - 0x73, 0x8f, 0xb1, 0x5d, 0xa4, 0x4c, 0x54, 0x3a, 0x73, 0xa2, 0xdb, 0x72, 0xa2, 0x1b, 0xa2, 0xd5, - 0x34, 0x7a, 0xaa, 0x21, 0xc6, 0xd9, 0x21, 0x2b, 0x9f, 0x79, 0xbd, 0xfb, 0x83, 0x02, 0x4c, 0xee, - 0x05, 0xa6, 0xc3, 0x92, 0xe2, 0xf7, 0x14, 0xdf, 0xf6, 0xa9, 0xf4, 0x6d, 0x1f, 0xbc, 0x61, 0xd3, - 0x4b, 0xf0, 0xe6, 0x99, 0x26, 0xc3, 0x2f, 0x35, 0xd3, 0x14, 0x63, 0xe4, 0x31, 0x4c, 0xf2, 0x62, - 0x35, 0x4a, 0x9e, 0x51, 0x2a, 0x09, 0x0e, 0xba, 0x6a, 0x31, 0xd8, 0xac, 0x4b, 0x5d, 0xcc, 0x85, - 0xf8, 0x5b, 0x65, 0x24, 0x30, 0xd4, 0x67, 0xab, 0x53, 0xff, 0xb7, 0x67, 0xab, 0xe4, 0x19, 0x54, - 0x65, 0x7b, 0xa0, 0xd9, 0x93, 0x17, 0xca, 0x4a, 0xd4, 0x4f, 0x87, 0x94, 0xd7, 0x33, 0x02, 0x64, - 0xa8, 0x4f, 0xc5, 0xf4, 0x8c, 0x07, 0xd1, 0x07, 0xfb, 0xa9, 0xb9, 0x8d, 0x4f, 0x87, 0x76, 0xa9, - 0x15, 0x50, 0x16, 0x8a, 0x5e, 0x6d, 0xd6, 0x6b, 0x55, 0x79, 0x66, 0xdd, 0xd6, 0x1f, 0x15, 0x60, - 0x6e, 0xd0, 0x64, 0xc8, 0x47, 0x50, 0x5b, 0x13, 0x71, 0xd5, 0x0f, 0xd2, 0x4e, 0x03, 0xf6, 0xe0, - 0xad, 0x04, 0x6c, 0xe4, 0xde, 0x94, 0xaa, 0xe8, 0xbc, 0x14, 0xe3, 0x47, 0xd0, 0xcb, 0x9e, 0x2f, - 0xa0, 0x87, 0x89, 0x24, 0x4c, 0x2d, 0xc5, 0x12, 0x3c, 0xe5, 0x04, 0xff, 0x5b, 0x11, 0x6a, 0xca, - 0x96, 0x91, 0xf7, 0xa1, 0xb2, 0x11, 0x6e, 0xf9, 0xd6, 0x31, 0xb5, 0x65, 0x32, 0x87, 0x6f, 0x93, - 0x9d, 0xd0, 0x70, 0x11, 0xa8, 0xa7, 0xc3, 0xa4, 0x09, 0x33, 0xe2, 0xeb, 0x29, 0x0d, 0x43, 0xb3, - 0x9d, 0xcc, 0x7e, 0xb3, 0x1f, 0x6b, 0x75, 0x81, 0x6c, 0x74, 0xc4, 0x88, 0xb2, 0x86, 0x3c, 0x09, - 0xf9, 0x43, 0x00, 0x01, 0xe0, 0xbb, 0x3c, 0xc6, 0x23, 0xbf, 0xe4, 0x18, 0x5f, 0x91, 0x13, 0xf0, - 0xb4, 0x68, 0xa0, 0x9f, 0xa3, 0x30, 0xc4, 0x57, 0x9e, 0xbe, 0x75, 0x3c, 0xfe, 0xcb, 0xe8, 0xec, - 0x95, 0xa7, 0x6f, 0x1d, 0x1b, 0xa3, 0x1b, 0x46, 0x2a, 0xcb, 0xc6, 0xaf, 0x0a, 0x8a, 0xd9, 0x91, - 0x4f, 0xa1, 0x9a, 0x6e, 0x8d, 0xcc, 0xfd, 0xaf, 0xaa, 0x8f, 0x99, 0xe4, 0x90, 0x4e, 0x0f, 0x9b, - 0x37, 0x64, 0x02, 0x36, 0x9f, 0xee, 0x71, 0xce, 0x0a, 0x13, 0x20, 0xf9, 0x04, 0xca, 0xa8, 0x9b, - 0xb3, 0xdf, 0x08, 0x24, 0xae, 0xbe, 0xcc, 0x95, 0x82, 0x2b, 0x45, 0x4a, 0x72, 0x4f, 0xf6, 0xa9, - 0x84, 0x76, 0x2f, 0xe5, 0xdd, 0x3c, 0x5f, 0x4a, 0xea, 0xe3, 0xb3, 0xf6, 0x95, 0x62, 0x21, 0x7f, - 0x5a, 0x80, 0xf9, 0xe7, 0x2b, 0x8f, 0x74, 0xda, 0x76, 0x42, 0x26, 0xf2, 0x4d, 0x5e, 0xa3, 0x92, - 0xeb, 0x50, 0xd2, 0xcd, 0x17, 0xf2, 0x8d, 0xd3, 0x54, 0x3f, 0xd6, 0x4a, 0x81, 0xf9, 0x42, 0xe7, - 0x30, 0x72, 0x17, 0xaa, 0x9b, 0xb4, 0xf7, 0xc4, 0xf4, 0x6c, 0x97, 0xca, 0xd7, 0x4d, 0x78, 0x15, - 0x75, 0x4c, 0x7b, 0xc6, 0x11, 0x42, 0xf5, 0x0c, 0x01, 0xcb, 0xf0, 0xe8, 0x60, 0x93, 0x8a, 0xda, - 0x64, 0x5a, 0x96, 0xe1, 0xd1, 0xc1, 0x31, 0xed, 0xe9, 0x72, 0xa4, 0xf1, 0x8f, 0x45, 0x98, 0x1b, - 0x3c, 0x71, 0xe4, 0x63, 0x98, 0xde, 0x31, 0xc3, 0xf0, 0x85, 0x1f, 0xd8, 0x4f, 0xcc, 0xf0, 0x48, - 0x2e, 0xe5, 0x46, 0x3f, 0xd6, 0xae, 0x75, 0x25, 0xdc, 0x38, 0x32, 0x43, 0xf5, 0x28, 0xe6, 0x08, - 0x78, 0x6c, 0xde, 0x7b, 0xb6, 0xb7, 0x93, 0xbc, 0xc1, 0x92, 0x27, 0x87, 0xf9, 0xac, 0x6b, 0xe4, - 0x1f, 0x62, 0x25, 0x68, 0xa4, 0x0d, 0x17, 0x07, 0x74, 0x21, 0xd5, 0xaa, 0x74, 0x6f, 0x47, 0x28, - 0x4b, 0xb4, 0xfe, 0xa3, 0x95, 0x43, 0x23, 0x50, 0x46, 0x94, 0x09, 0x06, 0xb9, 0x92, 0x07, 0x00, - 0xcf, 0x57, 0x1e, 0xe1, 0xdd, 0x0e, 0x0d, 0xd0, 0x70, 0x67, 0xc4, 0x13, 0x44, 0xce, 0xc4, 0x12, - 0x60, 0x35, 0x3d, 0xcc, 0x90, 0x1b, 0x1e, 0x4c, 0xab, 0x96, 0xc6, 0xf3, 0x13, 0xe5, 0x46, 0xa7, - 0x92, 0x3c, 0x88, 0x92, 0xf7, 0x38, 0xa2, 0xbb, 0x59, 0x1c, 0xea, 0x6e, 0xde, 0x81, 0x4a, 0xe2, - 0xa0, 0xd4, 0x87, 0x8c, 0x32, 0x9c, 0xf5, 0xf4, 0x74, 0xb4, 0xf1, 0x1e, 0x4c, 0x49, 0x4b, 0x7a, - 0xfd, 0x7f, 0x0b, 0x34, 0x7e, 0x58, 0x84, 0x8b, 0x3a, 0xe5, 0x89, 0x8c, 0x7c, 0x1c, 0xf8, 0xb5, - 0x7c, 0xc9, 0x96, 0x93, 0xf0, 0xf4, 0x2c, 0xaa, 0xf1, 0x4f, 0x05, 0x98, 0x1f, 0x81, 0xfb, 0x55, - 0xae, 0xdb, 0xc9, 0x7d, 0xa8, 0xb6, 0x1c, 0xd3, 0x5d, 0xb5, 0xed, 0x20, 0xc9, 0x9d, 0x31, 0x1c, - 0xd9, 0x0e, 0x8f, 0x46, 0x1c, 0xaa, 0x3a, 0x97, 0x14, 0x95, 0x7c, 0x43, 0x9a, 0x46, 0x29, 0x55, - 0x6e, 0xf2, 0x56, 0x0e, 0xc4, 0x9a, 0xb2, 0x97, 0x72, 0x8d, 0xbf, 0x2b, 0x02, 0x11, 0x40, 0x69, - 0x5d, 0x8e, 0x7f, 0xae, 0x5f, 0x0c, 0x35, 0x73, 0x1b, 0xb8, 0xa8, 0xbc, 0x68, 0x1b, 0x10, 0x72, - 0xac, 0x4c, 0xf8, 0x47, 0x45, 0xb8, 0x3a, 0x9a, 0xf0, 0x2b, 0x6d, 0xe8, 0x5d, 0xa8, 0xe2, 0x3d, - 0xbb, 0xf2, 0x28, 0x11, 0x3d, 0xa8, 0xb8, 0x94, 0x47, 0xfc, 0x0c, 0x81, 0x1c, 0xc2, 0xcc, 0x96, - 0x19, 0xb2, 0x27, 0xd4, 0x0c, 0xd8, 0x01, 0x35, 0xd9, 0x18, 0x81, 0x34, 0x7d, 0x8f, 0x8f, 0xff, - 0xce, 0x70, 0x94, 0x50, 0x0e, 0xbe, 0xc7, 0xcf, 0xb1, 0x4d, 0xcd, 0xa5, 0x7c, 0xb6, 0xb9, 0x7c, - 0xe3, 0x63, 0x98, 0x56, 0x2f, 0x3a, 0x48, 0x05, 0xca, 0xdb, 0xcf, 0xb6, 0xd7, 0xe7, 0x2e, 0x90, - 0x1a, 0x4c, 0xed, 0xac, 0x6f, 0xb7, 0x36, 0xb6, 0x1f, 0xcf, 0x15, 0xc8, 0x34, 0x54, 0x56, 0x77, - 0x76, 0xf4, 0x67, 0xfb, 0xeb, 0xad, 0xb9, 0x22, 0x01, 0x98, 0x6c, 0xad, 0x6f, 0x6f, 0xac, 0xb7, - 0xe6, 0x4a, 0xcd, 0xcb, 0xaf, 0xfe, 0xeb, 0xd6, 0x85, 0x57, 0x5f, 0xdc, 0x2a, 0xfc, 0xfc, 0x8b, - 0x5b, 0x85, 0xff, 0xfc, 0xe2, 0x56, 0xe1, 0xf3, 0xff, 0xbe, 0x75, 0xe1, 0x60, 0x12, 0x65, 0xb9, - 0xf7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xc4, 0x9f, 0xc5, 0x11, 0x36, 0x00, 0x00, + 0x03, 0x26, 0x27, 0xe1, 0x55, 0xb4, 0xd0, 0x77, 0x3e, 0xd3, 0x97, 0x8b, 0x81, 0x65, 0x41, 0x73, + 0xf9, 0x55, 0xac, 0x15, 0x7e, 0x1e, 0x6b, 0xc0, 0x41, 0x42, 0x23, 0x3e, 0x31, 0x2f, 0x03, 0x8c, + 0xc3, 0x94, 0x83, 0x9a, 0xa0, 0xe7, 0x79, 0x93, 0x87, 0x70, 0x89, 0x17, 0x85, 0xce, 0xa1, 0x63, + 0x99, 0x8c, 0x3e, 0xf2, 0x83, 0x8e, 0xc9, 0xe4, 0xcb, 0x08, 0x7c, 0x01, 0xc4, 0x0b, 0x4a, 0xce, + 0xa9, 0x63, 0x32, 0x7d, 0x18, 0x93, 0xfc, 0xfe, 0xe9, 0x95, 0xef, 0xd7, 0xfb, 0xb1, 0xf6, 0xd6, + 0x88, 0xca, 0xf7, 0x14, 0x2b, 0x8c, 0xa8, 0x81, 0xdb, 0xaf, 0xaf, 0x81, 0xbf, 0x21, 0xfb, 0xea, + 0x6f, 0x9f, 0x52, 0x07, 0xe7, 0x26, 0x7a, 0x6d, 0x45, 0xbc, 0x02, 0xa5, 0xe6, 0xce, 0x23, 0xbc, + 0x18, 0x90, 0xd5, 0x1c, 0xf5, 0x8e, 0x4c, 0xcf, 0xa2, 0x76, 0xd6, 0xae, 0x50, 0xa3, 0x60, 0x73, + 0xe7, 0x51, 0xe3, 0x3f, 0x4a, 0x30, 0x9b, 0xf7, 0x3b, 0x1e, 0x8c, 0xb7, 0xfc, 0xb6, 0xe3, 0x25, + 0xdd, 0x55, 0x0c, 0xc6, 0x2e, 0x42, 0x74, 0x39, 0x42, 0xde, 0x01, 0x48, 0xcb, 0x81, 0x24, 0x69, + 0x95, 0xef, 0x8d, 0x94, 0x01, 0xf2, 0x47, 0x00, 0xdb, 0xbe, 0x4d, 0xe5, 0xd5, 0x6a, 0x49, 0x6e, + 0xc1, 0xb4, 0xa8, 0x13, 0xd7, 0x85, 0xa2, 0x30, 0x7c, 0x4f, 0x16, 0x86, 0xf2, 0x81, 0x50, 0x3f, + 0xd6, 0xae, 0x78, 0xbe, 0x4d, 0x87, 0x6f, 0x55, 0x15, 0x8e, 0xe4, 0x21, 0x4c, 0xe8, 0x91, 0x4b, + 0x93, 0x5b, 0xdb, 0x59, 0x65, 0x2f, 0x45, 0x2e, 0xcd, 0x76, 0x62, 0x10, 0x0d, 0x5e, 0xcf, 0x70, + 0x00, 0xf9, 0x18, 0x60, 0x33, 0x3a, 0xa0, 0x8f, 0x03, 0x3f, 0xea, 0x26, 0xd7, 0x39, 0x5a, 0x3f, + 0xd6, 0x6e, 0x1c, 0x47, 0x07, 0x34, 0xf0, 0x28, 0xa3, 0xa1, 0xd1, 0xc6, 0x41, 0x75, 0xfe, 0x8c, + 0x84, 0xe8, 0x30, 0x25, 0x73, 0x48, 0xf9, 0x8a, 0xe9, 0xed, 0x53, 0x4a, 0x07, 0x65, 0x5b, 0x63, + 0x1b, 0x29, 0x10, 0xe0, 0xdc, 0x35, 0x8f, 0x00, 0x91, 0x8f, 0xa0, 0xca, 0x67, 0xe0, 0xa9, 0x6b, + 0x28, 0xd7, 0x12, 0xcb, 0x55, 0x45, 0x26, 0x9e, 0xdc, 0xe6, 0x2e, 0x9a, 0x53, 0x82, 0x46, 0x0b, + 0xae, 0x9d, 0x32, 0x71, 0x76, 0x6f, 0x55, 0x38, 0xeb, 0xde, 0xaa, 0xf1, 0xd3, 0x02, 0x94, 0xb9, + 0x89, 0xc8, 0x07, 0x50, 0x4d, 0x0e, 0xe9, 0x84, 0xee, 0x5a, 0x3f, 0xd6, 0xe6, 0x93, 0x33, 0x3c, + 0x27, 0x45, 0x8a, 0xc9, 0xa7, 0xda, 0xa7, 0xc1, 0x41, 0xe2, 0x19, 0x38, 0xd5, 0x09, 0x07, 0xa8, + 0x53, 0x21, 0x06, 0x47, 0xfd, 0xce, 0x11, 0x0d, 0x92, 0x3b, 0x7a, 0x44, 0x7d, 0xc1, 0x01, 0x2a, + 0x2a, 0x62, 0x90, 0x65, 0x98, 0x5a, 0xb5, 0x44, 0x34, 0x2f, 0x23, 0x5f, 0x34, 0xa5, 0x69, 0x0d, + 0x85, 0x6c, 0x89, 0xd5, 0x78, 0x1b, 0xaa, 0x69, 0x8c, 0xe1, 0x47, 0x27, 0x7e, 0x88, 0xc8, 0xa8, + 0x8b, 0x1f, 0x78, 0x6c, 0x72, 0xcb, 0x9d, 0xe7, 0xa6, 0xf3, 0xa9, 0xc7, 0x26, 0x57, 0x6c, 0xac, + 0x6e, 0xf3, 0x3f, 0x4f, 0x02, 0x64, 0xc8, 0x84, 0xc2, 0xec, 0xb3, 0x8d, 0xd6, 0xda, 0x86, 0x4d, + 0x3d, 0xe6, 0xb0, 0x2c, 0x7d, 0x56, 0x0a, 0x87, 0xf5, 0x97, 0x8c, 0x06, 0x9e, 0xe9, 0x4a, 0x9c, + 0x5e, 0xf3, 0x6d, 0x39, 0xc1, 0x75, 0xdf, 0xb1, 0x2d, 0xc3, 0x49, 0x49, 0xd5, 0xa0, 0x9d, 0x67, + 0xca, 0xa7, 0xd9, 0x5d, 0x7d, 0xba, 0xa5, 0x4c, 0x53, 0x1c, 0x7f, 0x9a, 0xd0, 0xec, 0xb8, 0xa7, + 0x4c, 0x93, 0x67, 0x4a, 0x8e, 0x61, 0xee, 0x31, 0x36, 0x9b, 0x94, 0x89, 0x4a, 0x67, 0x4e, 0x74, + 0x5b, 0x4e, 0x74, 0x43, 0x34, 0xaa, 0x46, 0x4f, 0x35, 0xc4, 0x38, 0xdb, 0x64, 0xe5, 0x33, 0x2f, + 0x87, 0xbf, 0x57, 0x80, 0xc9, 0xbd, 0xc0, 0x74, 0x58, 0x52, 0x3a, 0x9f, 0x12, 0x19, 0x3f, 0x95, + 0x91, 0xf1, 0x83, 0x37, 0x6c, 0x99, 0x09, 0xde, 0x3c, 0x4f, 0x65, 0xf8, 0xa5, 0xe6, 0xa9, 0x62, + 0x8c, 0x3c, 0x86, 0x49, 0x5e, 0xea, 0x46, 0xc9, 0x23, 0x4c, 0x25, 0x3d, 0xc2, 0x40, 0x2f, 0x06, + 0x9b, 0x75, 0x69, 0x8b, 0xb9, 0x10, 0x7f, 0xab, 0x8c, 0x04, 0x86, 0xfa, 0xe8, 0x75, 0xea, 0xff, + 0xf7, 0xe8, 0x95, 0x3c, 0x83, 0xaa, 0x6c, 0x2e, 0x34, 0x7b, 0xf2, 0x3a, 0x5a, 0xc9, 0x19, 0xd2, + 0x21, 0xe5, 0xed, 0x8d, 0x00, 0x19, 0xea, 0x43, 0x33, 0x3d, 0xe3, 0x41, 0xf4, 0xc1, 0x6e, 0x6c, + 0x6e, 0xe1, 0xd3, 0xa1, 0x5d, 0x6a, 0x05, 0x94, 0x85, 0xa2, 0xd3, 0x9b, 0x75, 0x6a, 0x55, 0x9e, + 0x59, 0xaf, 0xf6, 0x07, 0x05, 0x98, 0x1b, 0x74, 0x19, 0xf2, 0x11, 0xd4, 0xd6, 0xc4, 0xa9, 0xec, + 0x07, 0x69, 0x9f, 0x02, 0x3b, 0xf8, 0x56, 0x02, 0x36, 0x72, 0x2f, 0x52, 0x55, 0x74, 0x5e, 0xc8, + 0xf1, 0x2d, 0xe8, 0x65, 0x8f, 0x1f, 0x30, 0xc2, 0x44, 0x12, 0xa6, 0x16, 0x72, 0x09, 0x9e, 0xb2, + 0x83, 0xff, 0xad, 0x08, 0x35, 0x65, 0xc9, 0xc8, 0xfb, 0x50, 0xd9, 0x08, 0xb7, 0x7c, 0xeb, 0x98, + 0xda, 0x32, 0x15, 0xc4, 0x97, 0xcd, 0x4e, 0x68, 0xb8, 0x08, 0xd4, 0xd3, 0x61, 0xd2, 0x84, 0x19, + 0xf1, 0xf5, 0x94, 0x86, 0xa1, 0xd9, 0x4e, 0x66, 0xbf, 0xd9, 0x8f, 0xb5, 0xba, 0x40, 0x36, 0x3a, + 0x62, 0x44, 0x91, 0x21, 0x4f, 0x42, 0xfe, 0x10, 0x40, 0x00, 0xf8, 0x2a, 0x8f, 0xf1, 0x44, 0x30, + 0xd9, 0xc6, 0x57, 0xe4, 0x04, 0x3c, 0xa9, 0x1a, 0xe8, 0x06, 0x29, 0x0c, 0xf1, 0x8d, 0xa8, 0x6f, + 0x1d, 0x8f, 0xff, 0xae, 0x3a, 0x7b, 0x23, 0xea, 0x5b, 0xc7, 0xc6, 0xe8, 0x76, 0x93, 0xca, 0xb2, + 0xf1, 0xcb, 0x82, 0xe2, 0x76, 0xe4, 0x53, 0xa8, 0xa6, 0x4b, 0x23, 0x2b, 0x87, 0xab, 0xea, 0x53, + 0x28, 0x39, 0xa4, 0xd3, 0xc3, 0xe6, 0x0d, 0x99, 0xbe, 0xcd, 0xa7, 0x6b, 0x9c, 0xf3, 0xc2, 0x04, + 0x48, 0x3e, 0x81, 0x32, 0xda, 0xe6, 0xec, 0x17, 0x06, 0x49, 0xa8, 0x2f, 0x73, 0xa3, 0xa0, 0xa4, + 0x48, 0x49, 0xee, 0xc9, 0x2e, 0x97, 0xb0, 0xee, 0xa5, 0x7c, 0x98, 0xe7, 0xa2, 0xa4, 0x31, 0x3e, + 0x6b, 0x7e, 0x29, 0x1e, 0xf2, 0xa7, 0x05, 0x98, 0x7f, 0xbe, 0xf2, 0x48, 0xa7, 0x6d, 0x27, 0x64, + 0x22, 0x5b, 0xe5, 0x15, 0x2e, 0xb9, 0x0e, 0x25, 0xdd, 0x7c, 0x21, 0x5f, 0x48, 0x4d, 0xf5, 0x63, + 0xad, 0x14, 0x98, 0x2f, 0x74, 0x0e, 0x23, 0x77, 0xa1, 0xba, 0x49, 0x7b, 0x4f, 0x4c, 0xcf, 0x76, + 0xa9, 0x7c, 0x1b, 0x85, 0x17, 0x59, 0xc7, 0xb4, 0x67, 0x1c, 0x21, 0x54, 0xcf, 0x10, 0xb0, 0x88, + 0x8f, 0x0e, 0x36, 0xa9, 0xa8, 0x6c, 0xa6, 0x65, 0x11, 0x1f, 0x1d, 0x1c, 0xd3, 0x9e, 0x2e, 0x47, + 0x1a, 0xff, 0x58, 0x84, 0xb9, 0xc1, 0x1d, 0x47, 0x3e, 0x86, 0xe9, 0x1d, 0x33, 0x0c, 0x5f, 0xf8, + 0x81, 0xfd, 0xc4, 0x0c, 0x8f, 0xa4, 0x28, 0x37, 0xfa, 0xb1, 0x76, 0xad, 0x2b, 0xe1, 0xc6, 0x91, + 0x19, 0xaa, 0x5b, 0x31, 0x47, 0xc0, 0xcf, 0xe6, 0xbd, 0x67, 0x7b, 0x3b, 0xc9, 0x0b, 0x2e, 0xb9, + 0x73, 0x98, 0xcf, 0xba, 0x46, 0xfe, 0x19, 0x57, 0x82, 0x46, 0xda, 0x70, 0x71, 0xc0, 0x16, 0xd2, + 0xac, 0x4a, 0xef, 0x77, 0x84, 0xb1, 0x44, 0x26, 0x16, 0xad, 0x1c, 0x1a, 0x81, 0x32, 0xa2, 0x4c, + 0x30, 0xc8, 0x95, 0x3c, 0x00, 0x78, 0xbe, 0xf2, 0x08, 0x6f, 0x86, 0x68, 0x80, 0x8e, 0x3b, 0x23, + 0x1e, 0x30, 0x72, 0x26, 0x96, 0x00, 0xab, 0xc9, 0x65, 0x86, 0xdc, 0xf0, 0x60, 0x5a, 0xf5, 0x34, + 0x9e, 0x9f, 0x28, 0xf7, 0x41, 0x95, 0xe4, 0x39, 0x95, 0xbc, 0x05, 0x12, 0xbd, 0xd1, 0xe2, 0x50, + 0x6f, 0xf4, 0x0e, 0x54, 0x92, 0x00, 0xa5, 0x3e, 0x83, 0x94, 0xc7, 0x59, 0x4f, 0x4f, 0x47, 0x1b, + 0xef, 0xc1, 0x94, 0xf4, 0xa4, 0xd7, 0xff, 0xaf, 0x41, 0xe3, 0xfb, 0x45, 0xb8, 0xa8, 0x53, 0x9e, + 0xc8, 0xc8, 0xa7, 0x85, 0x5f, 0xc9, 0x77, 0x70, 0x39, 0x0d, 0x4f, 0xcf, 0xa2, 0x1a, 0xff, 0x54, + 0x80, 0xf9, 0x11, 0xb8, 0x5f, 0xe6, 0xb2, 0x9e, 0xdc, 0x87, 0x6a, 0xcb, 0x31, 0xdd, 0x55, 0xdb, + 0x0e, 0x92, 0xdc, 0x19, 0x8f, 0x23, 0xdb, 0xe1, 0xa7, 0x11, 0x87, 0xaa, 0xc1, 0x25, 0x45, 0x25, + 0x5f, 0x93, 0xae, 0x51, 0x4a, 0x8d, 0x9b, 0xbc, 0xb4, 0x03, 0x21, 0x53, 0xf6, 0xce, 0xae, 0xf1, + 0x77, 0x45, 0x20, 0x02, 0x28, 0xbd, 0xcb, 0xf1, 0xcf, 0xf5, 0x7b, 0xa3, 0x66, 0x6e, 0x01, 0x17, + 0x95, 0xf7, 0x70, 0x03, 0x4a, 0x8e, 0x95, 0x09, 0xff, 0xa0, 0x08, 0x57, 0x47, 0x13, 0x7e, 0xa9, + 0x05, 0xbd, 0x0b, 0x55, 0xbc, 0xa5, 0x57, 0x9e, 0x34, 0x62, 0x04, 0x15, 0x57, 0xfa, 0x88, 0x9f, + 0x21, 0x90, 0x43, 0x98, 0xd9, 0x32, 0x43, 0xf6, 0x84, 0x9a, 0x01, 0x3b, 0xa0, 0x26, 0x1b, 0xe3, + 0x20, 0x4d, 0x5f, 0xf3, 0xe3, 0x3f, 0x43, 0x1c, 0x25, 0x94, 0x83, 0xaf, 0xf9, 0x73, 0x6c, 0x53, + 0x77, 0x29, 0x9f, 0xed, 0x2e, 0x5f, 0xfb, 0x18, 0xa6, 0xd5, 0x6b, 0x12, 0x52, 0x81, 0xf2, 0xf6, + 0xb3, 0xed, 0xf5, 0xb9, 0x0b, 0xa4, 0x06, 0x53, 0x3b, 0xeb, 0xdb, 0xad, 0x8d, 0xed, 0xc7, 0x73, + 0x05, 0x32, 0x0d, 0x95, 0xd5, 0x9d, 0x1d, 0xfd, 0xd9, 0xfe, 0x7a, 0x6b, 0xae, 0x48, 0x00, 0x26, + 0x5b, 0xeb, 0xdb, 0x1b, 0xeb, 0xad, 0xb9, 0x52, 0xf3, 0xf2, 0xab, 0xff, 0xbe, 0x75, 0xe1, 0xd5, + 0xe7, 0xb7, 0x0a, 0x3f, 0xfb, 0xfc, 0x56, 0xe1, 0xbf, 0x3e, 0xbf, 0x55, 0xf8, 0xec, 0x7f, 0x6e, + 0x5d, 0x38, 0x98, 0x44, 0x5d, 0xee, 0xfd, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfa, 0x04, 0x2f, + 0x6b, 0x4f, 0x36, 0x00, 0x00, } diff --git a/lib/services/types.proto b/lib/services/types.proto index 9aace18b917e6..a7216fc13670a 100644 --- a/lib/services/types.proto +++ b/lib/services/types.proto @@ -566,6 +566,9 @@ message RoleConditions { repeated string KubeGroups = 5 [(gogoproto.jsontag) = "kubernetes_groups,omitempty"]; AccessRequestConditions Request = 6 [(gogoproto.jsontag) = "request,omitempty"]; + + // KubeUsers is an optional kubernetes users to impersonate + repeated string KubeUsers = 7 [(gogoproto.jsontag) = "kubernetes_users,omitempty"]; } // AccessRequestConditions is a matcher for allow/deny restrictions on access-requests. diff --git a/lib/services/user.go b/lib/services/user.go index 2922b83a82360..9623407c90915 100644 --- a/lib/services/user.go +++ b/lib/services/user.go @@ -484,7 +484,7 @@ func (u *UserV1) Check() error { return trace.BadParameter("user name cannot be empty") } for _, login := range u.AllowedLogins { - _, _, err := parse.IsRoleVariable(login) + _, err := parse.RoleVariable(login) if err == nil { return trace.BadParameter("role variables not allowed in allowed logins") } diff --git a/lib/tlsca/ca.go b/lib/tlsca/ca.go index 26cca14a6a30f..74680c4b6ebf9 100644 --- a/lib/tlsca/ca.go +++ b/lib/tlsca/ca.go @@ -21,6 +21,7 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "encoding/pem" "math/big" "net" @@ -77,6 +78,8 @@ type Identity struct { Principals []string // KubernetesGroups is a list of Kubernetes groups allowed KubernetesGroups []string + // KubernetesUsers is a list of Kubernetes users allowed + KubernetesUsers []string // Expires specifies whenever the session will expire Expires time.Time // RouteToCluster specifies the target cluster @@ -97,6 +100,21 @@ func (i *Identity) CheckAndSetDefaults() error { return nil } +// Custom ranges are taken from this article +// +// https://serverfault.com/questions/551477/is-there-reserved-oid-space-for-internal-enterprise-cas +// +// http://oid-info.com/get/1.3.9999 +// + +// KubeUsersASN1ExtensionOID is an extension ID used when encoding/decoding +// license payload into certificates +var KubeUsersASN1ExtensionOID = asn1.ObjectIdentifier{1, 3, 9999, 1, 1} + +// KubeGroupsASN1ExtensionOID is an extension ID used when encoding/decoding +// license payload into certificates +var KubeGroupsASN1ExtensionOID = asn1.ObjectIdentifier{1, 3, 9999, 1, 2} + // Subject converts identity to X.509 subject name func (id *Identity) Subject() (pkix.Name, error) { rawTraits, err := wrappers.MarshalTraits(&id.Traits) @@ -110,37 +128,82 @@ func (id *Identity) Subject() (pkix.Name, error) { subject.Organization = append([]string{}, id.Groups...) subject.OrganizationalUnit = append([]string{}, id.Usage...) subject.Locality = append([]string{}, id.Principals...) + + // DELETE IN (5.0.0) + // Groups are marshaled to both ASN1 extension + // and old Province section, for backwards-compatibility, + // however begin migration to ASN1 extensions in the future + // for this and other properties subject.Province = append([]string{}, id.KubernetesGroups...) subject.StreetAddress = []string{id.RouteToCluster} subject.PostalCode = []string{string(rawTraits)} + for i := range id.KubernetesUsers { + kubeUser := id.KubernetesUsers[i] + subject.ExtraNames = append(subject.ExtraNames, + pkix.AttributeTypeAndValue{ + Type: KubeUsersASN1ExtensionOID, + Value: kubeUser, + }) + } + + for i := range id.KubernetesGroups { + kubeGroup := id.KubernetesGroups[i] + subject.ExtraNames = append(subject.ExtraNames, + pkix.AttributeTypeAndValue{ + Type: KubeGroupsASN1ExtensionOID, + Value: kubeGroup, + }) + } + return subject, nil } // FromSubject returns identity from subject name func FromSubject(subject pkix.Name, expires time.Time) (*Identity, error) { - i := &Identity{ - Username: subject.CommonName, - Groups: subject.Organization, - Usage: subject.OrganizationalUnit, - Principals: subject.Locality, - KubernetesGroups: subject.Province, - Expires: expires, + id := &Identity{ + Username: subject.CommonName, + Groups: subject.Organization, + Usage: subject.OrganizationalUnit, + Principals: subject.Locality, + Expires: expires, } if len(subject.StreetAddress) > 0 { - i.RouteToCluster = subject.StreetAddress[0] + id.RouteToCluster = subject.StreetAddress[0] } if len(subject.PostalCode) > 0 { - err := wrappers.UnmarshalTraits([]byte(subject.PostalCode[0]), &i.Traits) + err := wrappers.UnmarshalTraits([]byte(subject.PostalCode[0]), &id.Traits) if err != nil { return nil, trace.Wrap(err) } } - if err := i.CheckAndSetDefaults(); err != nil { + for _, attr := range subject.Names { + switch { + case attr.Type.Equal(KubeUsersASN1ExtensionOID): + val, ok := attr.Value.(string) + if ok { + id.KubernetesUsers = append(id.KubernetesUsers, val) + } + case attr.Type.Equal(KubeGroupsASN1ExtensionOID): + val, ok := attr.Value.(string) + if ok { + id.KubernetesGroups = append(id.KubernetesGroups, val) + } + } + } + + // DELETE IN(5.0.0): This logic is using Province field + // from subject in case if Kubernetes groups were not populated + // from ASN1 extension, after 5.0 Province field will be ignored + if len(id.KubernetesGroups) == 0 { + id.KubernetesGroups = subject.Province + } + + if err := id.CheckAndSetDefaults(); err != nil { return nil, trace.Wrap(err) } - return i, nil + return id, nil } // CertificateRequest is a X.509 signing certificate request diff --git a/lib/tlsca/ca_test.go b/lib/tlsca/ca_test.go index f15a5da7f23e6..a6a5d79481d64 100644 --- a/lib/tlsca/ca_test.go +++ b/lib/tlsca/ca_test.go @@ -71,3 +71,42 @@ func (s *TLSCASuite) TestPrincipals(c *check.C) { } c.Assert(certIPs, check.DeepEquals, ips) } + +// TestKubeExtensions test ASN1 subject kubernetes extensions +func (s *TLSCASuite) TestKubeExtensions(c *check.C) { + ca, err := New([]byte(fixtures.SigningCertPEM), []byte(fixtures.SigningKeyPEM)) + c.Assert(err, check.IsNil) + + privateKey, err := rsa.GenerateKey(rand.Reader, teleport.RSAKeySize) + c.Assert(err, check.IsNil) + + expires := s.clock.Now().Add(time.Hour) + identity := Identity{ + Username: "alice@example.com", + Groups: []string{"admin"}, + // Generate a certificate restricted for + // use against a kubernetes endpoint, and not the API server endpoint + // otherwise proxies can generate certs for any user. + Usage: []string{teleport.UsageKubeOnly}, + KubernetesGroups: []string{"system:masters", "admin"}, + KubernetesUsers: []string{"IAM#alice@example.com"}, + Expires: expires, + } + + subj, err := identity.Subject() + c.Assert(err, check.IsNil) + + certBytes, err := ca.GenerateCertificate(CertificateRequest{ + Clock: s.clock, + PublicKey: privateKey.Public(), + Subject: subj, + NotAfter: expires, + }) + c.Assert(err, check.IsNil) + + cert, err := ParseCertificatePEM(certBytes) + c.Assert(err, check.IsNil) + out, err := FromSubject(cert.Subject, cert.NotAfter) + c.Assert(err, check.IsNil) + fixtures.DeepCompare(c, out, &identity) +} diff --git a/lib/utils/parse/parse.go b/lib/utils/parse/parse.go index 51e25fc7394d5..e07e2885ed893 100644 --- a/lib/utils/parse/parse.go +++ b/lib/utils/parse/parse.go @@ -1,5 +1,5 @@ /* -Copyright 2017 Gravitational, Inc. +Copyright 2017-2020 Gravitational, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,87 +19,224 @@ package parse import ( "go/ast" "go/parser" + "net/mail" + "regexp" "strconv" "strings" + "unicode" "github.com/gravitational/trace" ) -// IsRoleVariable checks if the passed in string matches the variable pattern +// Expression is an expression template +// that can interpolate to some variables +type Expression struct { + // namespace is expression namespace, + // e.g. internal.traits has a variable traits + // in internal namespace + namespace string + // variable is a variable name, e.g. trait name, + // e.g. internal.traits has variable name traits + variable string + // prefix is a prefix of the string + prefix string + // suffix is a suffix + suffix string + // transform is an optional transform function to call, + // currently email.local is the only supported function + transform func(in string) (string, error) +} + +// EmailLocal returns local part of the email +func EmailLocal(in string) (string, error) { + if in == "" { + return "", trace.BadParameter("address is empty") + } + addr, err := mail.ParseAddress(in) + if err != nil { + return "", trace.BadParameter("failed to parse address %q: %q", in, err) + } + parts := strings.SplitN(addr.Address, "@", 2) + if len(parts) != 2 { + return "", trace.BadParameter("could not find local part in %q", addr.Address) + } + return parts[0], nil +} + +// Namespace returns a variable namespace, e.g. external or internal +func (p *Expression) Namespace() string { + return p.namespace +} + +// Name returns variable name +func (p *Expression) Name() string { + return p.variable +} + +// Interpolate interpolates the variable adding prefix and suffix if present, +// returns trace.NotFound in case if the trait is not found, nil in case of +// success and BadParameter error otherwise +func (p *Expression) Interpolate(traits map[string][]string) ([]string, error) { + values, ok := traits[p.variable] + if !ok { + return nil, trace.NotFound("variable is not found") + } + out := make([]string, len(values)) + for i := range values { + val := values[i] + var err error + if p.transform != nil { + val, err = p.transform(val) + if err != nil { + return nil, trace.Wrap(err) + } + } + out[i] = p.prefix + val + p.suffix + } + return out, nil +} + +var reVariable = regexp.MustCompile( + // prefix is anyting that is not { or } + `^(?P[^}{]*)` + + // variable is antything in brackets {{}} that is not { or } + `{{(?P\s*[^}{]*\s*)}}` + + // prefix is anyting that is not { or } + `(?P[^}{]*)$`, +) + +// RoleVariable checks if the passed in string matches the variable pattern // {{external.foo}} or {{internal.bar}}. If it does, it returns the variable // prefix and the variable name. In the previous example this would be // "external" or "internal" for the variable prefix and "foo" or "bar" for the // variable name. If no variable pattern is found, trace.NotFound is returned. -func IsRoleVariable(variable string) (string, string, error) { - // time whitespace around string if it exists - variable = strings.TrimSpace(variable) - - // strip {{ and }} from the start and end of the variable - if !strings.HasPrefix(variable, "{{") || !strings.HasSuffix(variable, "}}") { - return "", "", trace.NotFound("no variable found: %v", variable) +func RoleVariable(variable string) (*Expression, error) { + match := reVariable.FindStringSubmatch(variable) + if len(match) == 0 { + if strings.Index(variable, "{{") != -1 || strings.Index(variable, "}}") != -1 { + return nil, trace.BadParameter( + "%q is using template brackets '{{' or '}}', however expression does not parse, make sure the format is {{variable}}", + variable) + } + return nil, trace.NotFound("no variable found in %q", variable) } - variable = variable[2 : len(variable)-2] + + prefix, variable, suffix := match[1], match[2], match[3] // parse and get the ast of the expression expr, err := parser.ParseExpr(variable) if err != nil { - return "", "", trace.NotFound("no variable found: %v", variable) + return nil, trace.NotFound("no variable found in %q: %v", variable, err) } // walk the ast tree and gather the variable parts - variableParts, err := walk(expr) + result, err := walk(expr) if err != nil { - return "", "", trace.NotFound("no variable found: %v", variable) + return nil, trace.Wrap(err) } // the variable must have two parts the prefix and the variable name itself - if len(variableParts) != 2 { - return "", "", trace.NotFound("no variable found: %v", variable) + if len(result.parts) != 2 { + return nil, trace.NotFound("no variable found: %v", variable) } - return variableParts[0], variableParts[1], nil + return &Expression{ + prefix: strings.TrimLeftFunc(prefix, unicode.IsSpace), + namespace: result.parts[0], + variable: result.parts[1], + suffix: strings.TrimRightFunc(suffix, unicode.IsSpace), + transform: result.transform, + }, nil +} + +const ( + // EmailNamespace is a function namespace for email functions + EmailNamespace = "email" + // EmailLocalFnName is a name for email.local function + EmailLocalFnName = "local" +) + +// TransformFn is an optional transform function +// that can take in string and replace it with another value +type TransformFn func(in string) (string, error) + +type walkResult struct { + parts []string + transform TransformFn } // walk will walk the ast tree and gather all the variable parts into a slice and return it. -func walk(node ast.Node) ([]string, error) { - var l []string +func walk(node ast.Node) (*walkResult, error) { + var result walkResult switch n := node.(type) { + case *ast.CallExpr: + switch call := n.Fun.(type) { + case *ast.Ident: + return nil, trace.BadParameter("function %v is not supported", call.Name) + case *ast.SelectorExpr: + // Selector expression looks like email.local(parameter) + namespace, ok := call.X.(*ast.Ident) + if !ok { + return nil, trace.BadParameter("expected namespace, e.g. email.local, got %v", call.X) + } + // This is the part before the dot + if namespace.Name != EmailNamespace { + return nil, trace.BadParameter("unsupported namespace, e.g. email.local, got %v", call.X) + } + // This is a function name + if call.Sel.Name != EmailLocalFnName { + return nil, trace.BadParameter("unsupported function %v, supported functions are: email.local", call.Sel.Name) + } + // Because only one function is supported for now, + // this makes sure that the function call has exactly one argument + if len(n.Args) != 1 { + return nil, trace.BadParameter("expected 1 argument for email.local got %v", len(n.Args)) + } + result.transform = EmailLocal + ret, err := walk(n.Args[0]) + if err != nil { + return nil, trace.Wrap(err) + } + result.parts = ret.parts + return &result, nil + default: + return nil, trace.BadParameter("unsupported function %T", n.Fun) + } case *ast.IndexExpr: ret, err := walk(n.X) if err != nil { return nil, err } - l = append(l, ret...) - + result.parts = append(result.parts, ret.parts...) ret, err = walk(n.Index) if err != nil { return nil, err } - l = append(l, ret...) + result.parts = append(result.parts, ret.parts...) + return &result, nil case *ast.SelectorExpr: ret, err := walk(n.X) if err != nil { return nil, err } - l = append(l, ret...) + result.parts = append(result.parts, ret.parts...) ret, err = walk(n.Sel) if err != nil { return nil, err } - l = append(l, ret...) + result.parts = append(result.parts, ret.parts...) + return &result, nil case *ast.Ident: - return []string{n.Name}, nil + return &walkResult{parts: []string{n.Name}}, nil case *ast.BasicLit: value, err := strconv.Unquote(n.Value) if err != nil { return nil, err } - return []string{value}, nil + return &walkResult{parts: []string{value}}, nil default: return nil, trace.BadParameter("unknown node type: %T", n) } - - return l, nil } diff --git a/lib/utils/parse/parse_test.go b/lib/utils/parse/parse_test.go index 59954b21cade8..2e572a3cffea2 100644 --- a/lib/utils/parse/parse_test.go +++ b/lib/utils/parse/parse_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 Gravitational, Inc. +Copyright 2017-2020 Gravitational, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -40,94 +40,158 @@ func (s *ParseSuite) TearDownSuite(c *check.C) {} func (s *ParseSuite) SetUpTest(c *check.C) {} func (s *ParseSuite) TearDownTest(c *check.C) {} -func (s *ParseSuite) TestIsRoleVariable(c *check.C) { +// TestRoleVariable tests variable parsing +func (s *ParseSuite) TestRoleVariable(c *check.C) { var tests = []struct { - inVariable string - outIsNotFound bool - outVariablePrefix string - outVariableName string + title string + in string + err error + out Expression }{ - // 0 - no curly bracket prefix { - "external.foo}}", - true, - "", - "", + title: "no curly bracket prefix", + in: "external.foo}}", + err: trace.BadParameter(""), }, - // 1 - invalid syntax { - `{{external.foo("bar")`, - true, - "", - "", + title: "invalid syntax", + in: `{{external.foo("bar")`, + err: trace.BadParameter(""), }, - // 2 - invalid sytnax { - "{{internal.}}", - true, - "", - "", + title: "invalid variable syntax", + in: "{{internal.}}", + err: trace.BadParameter(""), }, - // 3 - invalid syntax { - "{{external..foo}}", - true, - "", - "", + title: "invalid dot syntax", + in: "{{external..foo}}", + err: trace.BadParameter(""), }, - // 4 - invalid syntax { - "{{}}", - true, - "", - "", + title: "empty variable", + in: "{{}}", + err: trace.BadParameter(""), }, - // 5 - invalid syntax { - "{{internal.foo", - true, - "", - "", + title: "no curly bracket suffix", + in: "{{internal.foo", + err: trace.BadParameter(""), }, - // 6 - invalid syntax { - "{{internal.foo.bar}}", - true, - "", - "", + title: "too many levels of nesting in the variable", + in: "{{internal.foo.bar}}", + err: trace.BadParameter(""), }, - // 7 - valid brackets { - `{{internal["foo"]}}`, - false, - "internal", - "foo", + title: "valid with brackets", + in: `{{internal["foo"]}}`, + out: Expression{namespace: "internal", variable: "foo"}, }, - // 8 - valid { - "{{external.foo}}", - false, - "external", - "foo", + title: "external with no brackets", + in: "{{external.foo}}", + out: Expression{namespace: "external", variable: "foo"}, }, - // 9 - valid { - "{{internal.bar}}", - false, - "internal", - "bar", + title: "internal with no brackets", + in: "{{internal.bar}}", + out: Expression{namespace: "internal", variable: "bar"}, + }, + { + title: "internal with spaces removed", + in: " {{ internal.bar }} ", + out: Expression{namespace: "internal", variable: "bar"}, + }, + { + title: "variable with prefix and suffix", + in: " hello, {{ internal.bar }} there! ", + out: Expression{prefix: "hello, ", namespace: "internal", variable: "bar", suffix: " there!"}, + }, + { + title: "variable with local function", + in: "{{email.local(internal.bar)}}", + out: Expression{namespace: "internal", variable: "bar", transform: EmailLocal}, + }, + } + + for i, tt := range tests { + comment := check.Commentf("Test(%v) %q", i, tt.title) + + variable, err := RoleVariable(tt.in) + if tt.err != nil { + c.Assert(err, check.FitsTypeOf, tt.err, comment) + continue + } + c.Assert(err, check.IsNil, comment) + // functionns are not directly comparable, compare fields + // directly, except functions as a workaround + c.Assert(variable.prefix, check.Equals, tt.out.prefix, comment) + c.Assert(variable.variable, check.Equals, tt.out.variable, comment) + c.Assert(variable.suffix, check.Equals, tt.out.suffix, comment) + // functions are not comparable + if tt.out.transform == nil { + c.Assert(variable.transform, check.IsNil, comment) + } else { + c.Assert(variable.transform, check.NotNil, comment) + } + } +} + +// TestInterpolate tests variable interpolation +func (s *ParseSuite) TestInterpolate(c *check.C) { + type result struct { + values []string + err error + } + var tests = []struct { + title string + in Expression + traits map[string][]string + res result + }{ + { + title: "mapped traits", + in: Expression{variable: "foo"}, + traits: map[string][]string{"foo": []string{"a", "b"}, "bar": []string{"c"}}, + res: result{values: []string{"a", "b"}}, + }, + { + title: "mapped traits with email.local", + in: Expression{variable: "foo", transform: EmailLocal}, + traits: map[string][]string{"foo": []string{"Alice ", "bob@example.com"}, "bar": []string{"c"}}, + res: result{values: []string{"alice", "bob"}}, + }, + { + title: "missed traits", + in: Expression{variable: "baz"}, + traits: map[string][]string{"foo": []string{"a", "b"}, "bar": []string{"c"}}, + res: result{err: trace.NotFound("not found"), values: []string{}}, + }, + { + title: "traits with prefix and suffix", + in: Expression{prefix: "IAM#", variable: "foo", suffix: ";"}, + traits: map[string][]string{"foo": []string{"a", "b"}, "bar": []string{"c"}}, + res: result{values: []string{"IAM#a;", "IAM#b;"}}, + }, + { + title: "error in mapping traits", + in: Expression{variable: "foo", transform: EmailLocal}, + traits: map[string][]string{"foo": []string{"Alice