Support scalar and list values for node_labels

This PR references #2136
This commit is contained in:
Sasha Klizhentas
2018-08-10 18:54:26 -07:00
committed by Alexander Klizhentas
parent f3eef0dc90
commit 34cd3da918
10 changed files with 298 additions and 188 deletions
+1 -1
View File
@@ -392,7 +392,7 @@ func GetCheckerForBuiltinRole(clusterName string, clusterConfig services.Cluster
Allow: services.RoleConditions{
Namespaces: []string{services.Wildcard},
Logins: []string{},
NodeLabels: map[string]string{services.Wildcard: services.Wildcard},
NodeLabels: services.Labels{services.Wildcard: []string{services.Wildcard}},
Rules: []services.Rule{
services.NewRule(services.Wildcard, services.RW()),
},
-47
View File
@@ -64,8 +64,6 @@ type OIDCConnector interface {
GetClaims() []string
// MapClaims maps claims to roles
MapClaims(claims jose.Claims) []string
// RoleFromTemplate creates a role from a template and claims.
RoleFromTemplate(claims jose.Claims) (Role, error)
// Check checks OIDC connector for errors
Check() error
// CheckAndSetDefaults checks and set default values for any missing fields.
@@ -436,51 +434,6 @@ func executeSliceTemplate(raw []string, claims jose.Claims) ([]string, error) {
return sl, nil
}
// RoleFromTemplate creates a role from a template and claims.
func (o *OIDCConnectorV2) RoleFromTemplate(claims jose.Claims) (Role, error) {
for _, mapping := range o.Spec.ClaimsToRoles {
for claimName := range claims {
// claim name doesn't match
if claimName != mapping.Claim {
continue
}
// claim value doesn't match
claimValue, ok, _ := claims.StringClaim(claimName)
if ok && claimValue != mapping.Value {
continue
}
// claim name and value match, if a role template exists, execute template
roleTemplate := mapping.RoleTemplate
if roleTemplate != nil {
// at the moment, only allow templating for role name and logins
executedName, err := executeStringTemplate(roleTemplate.GetName(), claims)
if err != nil {
return nil, trace.Wrap(err)
}
executedLogins, err := executeSliceTemplate(roleTemplate.GetLogins(), claims)
if err != nil {
return nil, trace.Wrap(err)
}
roleTemplate.SetName(executedName)
roleTemplate.SetLogins(executedLogins)
// check all fields and make sure we have have a valid role
err = roleTemplate.CheckAndSetDefaults()
if err != nil {
return nil, trace.Wrap(err)
}
return roleTemplate.V3(), nil
}
}
}
return nil, trace.BadParameter("no matching claim name/value, claims: %v", claims)
}
// Check returns nil if all parameters are great, err otherwise
func (o *OIDCConnectorV2) Check() error {
if o.Metadata.Name == "" {
+85 -32
View File
@@ -98,7 +98,7 @@ func NewAdminRole() Role {
},
Allow: RoleConditions{
Namespaces: []string{defaults.Namespace},
NodeLabels: map[string]string{Wildcard: Wildcard},
NodeLabels: Labels{Wildcard: []string{Wildcard}},
Rules: CopyRulesSlice(AdminUserRules),
},
},
@@ -148,7 +148,7 @@ func RoleForUser(u User) Role {
},
Allow: RoleConditions{
Namespaces: []string{defaults.Namespace},
NodeLabels: map[string]string{Wildcard: Wildcard},
NodeLabels: Labels{Wildcard: []string{Wildcard}},
Rules: CopyRulesSlice(AdminUserRules),
},
},
@@ -170,7 +170,7 @@ func RoleForCertAuthority(ca CertAuthority) Role {
},
Allow: RoleConditions{
Namespaces: []string{defaults.Namespace},
NodeLabels: map[string]string{Wildcard: Wildcard},
NodeLabels: Labels{Wildcard: []string{Wildcard}},
Rules: CopyRulesSlice(DefaultCertAuthorityRules),
},
},
@@ -249,9 +249,9 @@ type Role interface {
SetNamespaces(RoleConditionType, []string)
// GetNodeLabels gets the map of node labels this role is allowed or denied access to.
GetNodeLabels(RoleConditionType) map[string]string
GetNodeLabels(RoleConditionType) Labels
// SetNodeLabels sets the map of node labels this role is allowed or denied access to.
SetNodeLabels(RoleConditionType, map[string]string)
SetNodeLabels(RoleConditionType, Labels)
// GetRules gets all allow or deny rules.
GetRules(rct RoleConditionType) []Rule
@@ -312,25 +312,28 @@ func ApplyTraits(r Role, traits map[string][]string) Role {
inLabels := r.GetNodeLabels(condition)
// to avoid unnecessary allocations
if inLabels != nil {
outLabels := make(map[string]string, len(inLabels))
outLabels := make(Labels, len(inLabels))
// every key will be mapped to the first value
for key, val := range inLabels {
for key, vals := range inLabels {
keyVars, err := applyValueTraits(key, traits)
if err != nil {
// empty key will not match anything
log.Debugf("Setting empty node label pair %q -> %q: %v", key, val, err)
log.Debugf("Setting empty node label pair %q -> %q: %v", key, vals, err)
keyVars = []string{""}
}
valVars, err := applyValueTraits(val, traits)
if err != nil {
log.Debugf("Setting empty node label value %q -> %q: %v", key, val, err)
// empty value will not match anything
valVars = []string{""}
var values []string
for _, val := range vals {
valVars, err := applyValueTraits(val, traits)
if err != nil {
log.Debugf("Setting empty node label value %q -> %q: %v", key, val, err)
// empty value will not match anything
valVars = []string{""}
}
values = append(values, valVars...)
}
outLabels[keyVars[0]] = valVars[0]
outLabels[keyVars[0]] = utils.Deduplicate(values)
}
r.SetNodeLabels(condition, outLabels)
}
@@ -400,7 +403,7 @@ func (r *RoleV3) Equals(other Role) bool {
if !utils.StringSlicesEqual(r.GetNamespaces(condition), other.GetNamespaces(condition)) {
return false
}
if !utils.StringMapsEqual(r.GetNodeLabels(condition), other.GetNodeLabels(condition)) {
if !r.GetNodeLabels(condition).Equals(other.GetNodeLabels(condition)) {
return false
}
if !RuleSlicesEqual(r.GetRules(condition), other.GetRules(condition)) {
@@ -527,7 +530,7 @@ func (r *RoleV3) SetNamespaces(rct RoleConditionType, namespaces []string) {
}
// GetNodeLabels gets the map of node labels this role is allowed or denied access to.
func (r *RoleV3) GetNodeLabels(rct RoleConditionType) map[string]string {
func (r *RoleV3) GetNodeLabels(rct RoleConditionType) Labels {
if rct == Allow {
return r.Spec.Allow.NodeLabels
}
@@ -535,13 +538,11 @@ func (r *RoleV3) GetNodeLabels(rct RoleConditionType) map[string]string {
}
// SetNodeLabels sets the map of node labels this role is allowed or denied access to.
func (r *RoleV3) SetNodeLabels(rct RoleConditionType, labels map[string]string) {
lcopy := utils.CopyStringMap(labels)
func (r *RoleV3) SetNodeLabels(rct RoleConditionType, labels Labels) {
if rct == Allow {
r.Spec.Allow.NodeLabels = lcopy
r.Spec.Allow.NodeLabels = labels.Clone()
} else {
r.Spec.Deny.NodeLabels = lcopy
r.Spec.Deny.NodeLabels = labels.Clone()
}
}
@@ -585,7 +586,7 @@ func (r *RoleV3) CheckAndSetDefaults() error {
r.Spec.Allow.Namespaces = []string{defaults.Namespace}
}
if r.Spec.Allow.NodeLabels == nil {
r.Spec.Allow.NodeLabels = map[string]string{Wildcard: Wildcard}
r.Spec.Allow.NodeLabels = Labels{Wildcard: []string{Wildcard}}
}
if r.Spec.Deny.Namespaces == nil {
r.Spec.Deny.Namespaces = []string{defaults.Namespace}
@@ -615,7 +616,7 @@ func (r *RoleV3) CheckAndSetDefaults() error {
}
}
for key, val := range r.Spec.Allow.NodeLabels {
if key == Wildcard && val != Wildcard {
if key == Wildcard && (len(val) != 1 || val[0] != Wildcard) {
return trace.BadParameter("selector *:<val> is not supported")
}
}
@@ -694,8 +695,9 @@ type RoleConditions struct {
// Namespaces is a list of namespaces (used to partition a cluster). The
// field should be called "namespaces" when it returns in Teleport 2.4.
Namespaces []string `json:"-"`
// NodeLabels is a map of node labels (used to dynamically grant access to nodes).
NodeLabels map[string]string `json:"node_labels,omitempty"`
NodeLabels Labels `json:"node_labels,omitempty"`
// Rules is a list of rules and their access levels. Rules are a high level
// construct used for access control.
@@ -714,7 +716,7 @@ func (r *RoleConditions) Equals(o RoleConditions) bool {
if !utils.StringSlicesEqual(r.Namespaces, o.Namespaces) {
return false
}
if !utils.StringMapsEqual(r.NodeLabels, o.NodeLabels) {
if !r.NodeLabels.Equals(o.NodeLabels) {
return false
}
if len(r.Rules) != len(o.Rules) {
@@ -1177,7 +1179,7 @@ func (r *RoleV2) V3() *RoleV3 {
Allow: RoleConditions{
Logins: r.GetLogins(),
Namespaces: r.GetNamespaces(),
NodeLabels: r.GetNodeLabels(),
NodeLabels: scalarLabels(r.GetNodeLabels()).labels(),
},
},
rawObject: *r,
@@ -1392,17 +1394,23 @@ func MatchNamespace(selector []string, namespace string) bool {
}
// MatchLabels matches selector against target
func MatchLabels(selector map[string]string, target map[string]string) bool {
func MatchLabels(selector Labels, target map[string]string) bool {
// empty selector matches nothing
if len(selector) == 0 {
return false
}
// *: * matches everything even empty target set
if selector[Wildcard] == Wildcard {
selectorValues := selector[Wildcard]
if len(selectorValues) == 1 && selectorValues[0] == Wildcard {
return true
}
for key, val := range selector {
if targetVal, ok := target[key]; !ok || (val != targetVal && val != Wildcard) {
// otherwise perform full match
for key, selectorValues := range selector {
targetVal, hasKey := target[key]
if !hasKey {
return false
}
if !utils.SliceContainsStr(selectorValues, Wildcard) && !utils.SliceContainsStr(selectorValues, targetVal) {
return false
}
}
@@ -1740,6 +1748,50 @@ func BoolOption(v *Bool) Bool {
return *v
}
// Labels is a wrapper around map
// that can marshal and unmarshal itself
// from scalar and list values
type Labels map[string]utils.Strings
// Clone returns non-shallow copy of the labels set
func (l Labels) Clone() Labels {
if l == nil {
return nil
}
out := make(Labels, len(l))
for key, vals := range l {
cvals := make([]string, len(vals))
copy(cvals, vals)
out[key] = cvals
}
return out
}
// Equals returns true if two label sets are equal
func (l Labels) Equals(o Labels) bool {
if len(l) != len(o) {
return false
}
for key := range l {
if !utils.StringSlicesEqual(l[key], o[key]) {
return false
}
}
return true
}
// scalarLabels is a key value map
// with scalar values
type scalarLabels map[string]string
func (l scalarLabels) labels() Labels {
out := make(Labels, len(l))
for key, val := range l {
out[key] = []string{val}
}
return out
}
// Bool is a wrapper around boolean values
type Bool struct {
bool
@@ -1788,6 +1840,7 @@ func (b *Bool) UnmarshalYAML(unmarshal func(interface{}) error) error {
var boolVar bool
if err := unmarshal(&boolVar); err == nil {
b.bool = boolVar
return nil
}
var stringVar string
if err := unmarshal(&stringVar); err != nil {
@@ -1895,7 +1948,7 @@ const RoleSpecV3SchemaDefinitions = `
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-zA-Z/.0-9_*]+$": { "type": "string" }
"^[a-zA-Z/.0-9_*]+$": { "anyOf": [{"type": "string"}, { "type": "array", "items": {"type": "string"}}]}
}
},
"logins": {
+179 -48
View File
@@ -198,7 +198,7 @@ func (s *RoleSuite) TestRoleParse(c *C) {
PortForwarding: NewBoolOption(true),
},
Allow: RoleConditions{
NodeLabels: map[string]string{Wildcard: Wildcard},
NodeLabels: Labels{Wildcard: []string{Wildcard}},
Namespaces: []string{defaults.Namespace},
},
Deny: RoleConditions{
@@ -257,7 +257,7 @@ func (s *RoleSuite) TestRoleParse(c *C) {
DisconnectExpiredCert: NewBool(true),
},
Allow: RoleConditions{
NodeLabels: map[string]string{"a": "b"},
NodeLabels: Labels{"a": []string{"b"}},
Namespaces: []string{"default"},
Rules: []Rule{
Rule{
@@ -279,7 +279,7 @@ func (s *RoleSuite) TestRoleParse(c *C) {
error: nil,
},
{
name: "alternative options forma",
name: "alternative options form",
in: `{
"kind": "role",
"version": "v3",
@@ -329,7 +329,7 @@ func (s *RoleSuite) TestRoleParse(c *C) {
DisconnectExpiredCert: NewBool(false),
},
Allow: RoleConditions{
NodeLabels: map[string]string{"a": "b"},
NodeLabels: Labels{"a": []string{"b"}},
Namespaces: []string{"default"},
Rules: []Rule{
Rule{
@@ -350,6 +350,61 @@ func (s *RoleSuite) TestRoleParse(c *C) {
},
error: nil,
},
{
name: "non-scalar and scalar values of labels",
in: `{
"kind": "role",
"version": "v3",
"metadata": {"name": "name1"},
"spec": {
"options": {
"cert_format": "standard",
"max_session_ttl": "20h",
"port_forwarding": "yes",
"forward_agent": "yes",
"client_idle_timeout": "never",
"disconnect_expired_cert": "no"
},
"allow": {
"node_labels": {"a": "b", "key": ["val"], "key2": ["val2", "val3"]}
},
"deny": {
"logins": ["c"]
}
}
}`,
role: RoleV3{
Kind: KindRole,
Version: V3,
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV3{
Options: RoleOptions{
CertificateFormat: teleport.CertificateFormatStandard,
ForwardAgent: NewBool(true),
MaxSessionTTL: NewDuration(20 * time.Hour),
PortForwarding: NewBoolOption(true),
ClientIdleTimeout: NewDuration(0),
DisconnectExpiredCert: NewBool(false),
},
Allow: RoleConditions{
NodeLabels: Labels{
"a": []string{"b"},
"key": []string{"val"},
"key2": []string{"val2", "val3"},
},
Namespaces: []string{"default"},
},
Deny: RoleConditions{
Namespaces: []string{defaults.Namespace},
Logins: []string{"c"},
},
},
},
error: nil,
},
}
for i, tc := range testCases {
comment := Commentf("test case %v %q", i, tc.name)
@@ -374,6 +429,22 @@ func (s *RoleSuite) TestRoleParse(c *C) {
}
}
// TestLabelCompatibility makes sure that labels
// are serialized in format understood by older servers with
// scalar labels
func (s *RoleSuite) TestLabelCompatibility(c *C) {
labels := Labels{
"key": []string{"val"},
}
data, err := json.Marshal(labels)
c.Assert(err, IsNil)
var out map[string]string
err = json.Unmarshal(data, &out)
c.Assert(err, IsNil)
c.Assert(out, DeepEquals, map[string]string{"key": "val"})
}
func (s *RoleSuite) TestCheckAccess(c *C) {
type check struct {
server Server
@@ -402,12 +473,12 @@ func (s *RoleSuite) TestCheckAccess(c *C) {
}
testCases := []struct {
name string
roles []RoleV2
roles []RoleV3
checks []check
}{
{
name: "empty role set has access to nothing",
roles: []RoleV2{},
roles: []RoleV3{},
checks: []check{
{server: serverA, login: "root", hasAccess: false},
{server: serverB, login: "root", hasAccess: false},
@@ -416,17 +487,21 @@ func (s *RoleSuite) TestCheckAccess(c *C) {
},
{
name: "role is limited to default namespace",
roles: []RoleV2{
RoleV2{
roles: []RoleV3{
RoleV3{
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV2{
MaxSessionTTL: Duration{20 * time.Hour},
Logins: []string{"admin"},
NodeLabels: map[string]string{Wildcard: Wildcard},
Namespaces: []string{defaults.Namespace},
Spec: RoleSpecV3{
Options: RoleOptions{
MaxSessionTTL: Duration{20 * time.Hour},
},
Allow: RoleConditions{
Namespaces: []string{defaults.Namespace},
Logins: []string{"admin"},
NodeLabels: Labels{Wildcard: []string{Wildcard}},
},
},
},
},
@@ -441,17 +516,50 @@ func (s *RoleSuite) TestCheckAccess(c *C) {
},
{
name: "role is limited to labels in default namespace",
roles: []RoleV2{
RoleV2{
roles: []RoleV3{
RoleV3{
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV2{
MaxSessionTTL: Duration{20 * time.Hour},
Logins: []string{"admin"},
NodeLabels: map[string]string{"role": "worker"},
Namespaces: []string{defaults.Namespace},
Spec: RoleSpecV3{
Options: RoleOptions{
MaxSessionTTL: Duration{20 * time.Hour},
},
Allow: RoleConditions{
Logins: []string{"admin"},
NodeLabels: Labels{"role": []string{"worker"}},
Namespaces: []string{defaults.Namespace},
},
},
},
},
checks: []check{
{server: serverA, login: "root", hasAccess: false},
{server: serverA, login: "admin", hasAccess: false},
{server: serverB, login: "root", hasAccess: false},
{server: serverB, login: "admin", hasAccess: true},
{server: serverC, login: "root", hasAccess: false},
{server: serverC, login: "admin", hasAccess: false},
},
},
{
name: "role matches any label out of multiple labels",
roles: []RoleV3{
RoleV3{
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV3{
Options: RoleOptions{
MaxSessionTTL: Duration{20 * time.Hour},
},
Allow: RoleConditions{
Logins: []string{"admin"},
NodeLabels: Labels{"role": []string{"worker2", "worker"}},
Namespaces: []string{defaults.Namespace},
},
},
},
},
@@ -466,29 +574,37 @@ func (s *RoleSuite) TestCheckAccess(c *C) {
},
{
name: "one role is more permissive than another",
roles: []RoleV2{
RoleV2{
roles: []RoleV3{
RoleV3{
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV2{
MaxSessionTTL: Duration{20 * time.Hour},
Logins: []string{"admin"},
NodeLabels: map[string]string{"role": "worker"},
Namespaces: []string{defaults.Namespace},
Spec: RoleSpecV3{
Options: RoleOptions{
MaxSessionTTL: Duration{20 * time.Hour},
},
Allow: RoleConditions{
Logins: []string{"admin"},
NodeLabels: Labels{"role": []string{"worker"}},
Namespaces: []string{defaults.Namespace},
},
},
},
RoleV2{
RoleV3{
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV2{
MaxSessionTTL: Duration{20 * time.Hour},
Logins: []string{"root", "admin"},
NodeLabels: map[string]string{Wildcard: Wildcard},
Namespaces: []string{Wildcard},
Spec: RoleSpecV3{
Options: RoleOptions{
MaxSessionTTL: Duration{20 * time.Hour},
},
Allow: RoleConditions{
Logins: []string{"root", "admin"},
NodeLabels: Labels{Wildcard: []string{Wildcard}},
Namespaces: []string{Wildcard},
},
},
},
},
@@ -506,7 +622,7 @@ func (s *RoleSuite) TestCheckAccess(c *C) {
var set RoleSet
for i := range tc.roles {
set = append(set, tc.roles[i].V3())
set = append(set, &tc.roles[i])
}
for j, check := range tc.checks {
comment := Commentf("test case %v '%v', check %v", i, tc.name, j)
@@ -928,8 +1044,8 @@ func (s *RoleSuite) TestApplyTraits(c *C) {
type rule struct {
inLogins []string
outLogins []string
inLabels map[string]string
outLabels map[string]string
inLabels Labels
outLabels Labels
inKubeGroups []string
outKubeGroups []string
}
@@ -1048,12 +1164,12 @@ func (s *RoleSuite) TestApplyTraits(c *C) {
"hello": []string{"there"},
},
allow: rule{
inLabels: map[string]string{`{{external.foo}}`: "{{external.hello}}"},
outLabels: map[string]string{`bar`: "there"},
inLabels: Labels{`{{external.foo}}`: []string{"{{external.hello}}"}},
outLabels: Labels{`bar`: []string{"there"}},
},
deny: rule{
inLabels: map[string]string{`{{external.hello}}`: "{{external.foo}}"},
outLabels: map[string]string{`there`: "bar"},
inLabels: Labels{`{{external.hello}}`: []string{"{{external.foo}}"}},
outLabels: Labels{`there`: []string{"bar"}},
},
},
@@ -1063,23 +1179,38 @@ func (s *RoleSuite) TestApplyTraits(c *C) {
"foo": []string{"bar"},
},
allow: rule{
inLabels: map[string]string{
`{{external.foo}}`: "value",
`{{external.missing}}`: "missing",
`missing`: "{{external.missing}}",
inLabels: Labels{
`{{external.foo}}`: []string{"value"},
`{{external.missing}}`: []string{"missing"},
`missing`: []string{"{{external.missing}}", "othervalue"},
},
outLabels: Labels{
`bar`: []string{"value"},
"missing": []string{"", "othervalue"},
"": []string{"missing"},
},
outLabels: map[string]string{`bar`: "value", "missing": "", "": "missing"},
},
},
{
comment: "the first variable value is picked for labels",
comment: "the first variable value is picked for label keys",
inTraits: map[string][]string{
"foo": []string{"bar", "baz"},
},
allow: rule{
inLabels: map[string]string{`{{external.foo}}`: "value"},
outLabels: map[string]string{`bar`: "value"},
inLabels: Labels{`{{external.foo}}`: []string{"value"}},
outLabels: Labels{`bar`: []string{"value"}},
},
},
{
comment: "all values are expanded for label values",
inTraits: map[string][]string{
"foo": []string{"bar", "baz"},
},
allow: rule{
inLabels: Labels{`key`: []string{`{{external.foo}}`}},
outLabels: Labels{`key`: []string{"bar", "baz"}},
},
},
}
-47
View File
@@ -59,8 +59,6 @@ type SAMLConnector interface {
GetAttributes() []string
// MapAttributes maps attributes to roles
MapAttributes(assertionInfo saml2.AssertionInfo) []string
// RoleFromTemplate creates a role from a template and claims.
RoleFromTemplate(assertionInfo saml2.AssertionInfo) (Role, error)
// Check checks SAML connector for errors
CheckAndSetDefaults() error
// SetIssuer sets issuer
@@ -497,51 +495,6 @@ func executeSAMLSliceTemplate(raw []string, assertion map[string]string) ([]stri
return sl, nil
}
// RoleFromTemplate creates a role from a template and claims.
func (o *SAMLConnectorV2) RoleFromTemplate(assertionInfo saml2.AssertionInfo) (Role, error) {
assertionMap := buildAssertionMap(assertionInfo)
for _, mapping := range o.Spec.AttributesToRoles {
for assrName, assrValue := range assertionMap {
// match assertion name
if assrName != mapping.Name {
continue
}
// match assertion value
if assrValue != mapping.Value {
continue
}
// claim name and value match, if a role template exists, execute template
roleTemplate := mapping.RoleTemplate
if roleTemplate != nil {
// at the moment, only allow templating for role name and logins
executedName, err := executeSAMLStringTemplate(roleTemplate.GetName(), assertionMap)
if err != nil {
return nil, trace.Wrap(err)
}
executedLogins, err := executeSAMLSliceTemplate(roleTemplate.GetLogins(), assertionMap)
if err != nil {
return nil, trace.Wrap(err)
}
roleTemplate.SetName(executedName)
roleTemplate.SetLogins(executedLogins)
// check all fields and make sure we have have a valid role
err = roleTemplate.CheckAndSetDefaults()
if err != nil {
return nil, trace.Wrap(err)
}
return roleTemplate.V3(), nil
}
}
}
return nil, trace.BadParameter("no matching assertion name/value, assertions: %v", assertionMap)
}
// GetServiceProvider initialises service provider spec from settings
func (o *SAMLConnectorV2) GetServiceProvider(clock clockwork.Clock) (*saml2.SAMLServiceProvider, error) {
if o.Metadata.Name == "" {
+1 -1
View File
@@ -436,7 +436,7 @@ func (s *ServicesTestSuite) RolesCRUD(c *C) {
},
Allow: services.RoleConditions{
Logins: []string{"root", "bob"},
NodeLabels: map[string]string{services.Wildcard: services.Wildcard},
NodeLabels: services.Labels{services.Wildcard: []string{services.Wildcard}},
Namespaces: []string{defaults.Namespace},
Rules: []services.Rule{
services.NewRule(services.KindRole, services.RO()),
+8 -8
View File
@@ -75,8 +75,8 @@ type SrvSuite struct {
const teleportTestUser = "teleport-test"
// wildcardAllow is used in tests to allow access to all labels.
var wildcardAllow map[string]string = map[string]string{
services.Wildcard: services.Wildcard,
var wildcardAllow = services.Labels{
services.Wildcard: []string{services.Wildcard},
}
var _ = Suite(&SrvSuite{})
@@ -343,27 +343,27 @@ func (s *SrvSuite) TestAllowedUsers(c *C) {
func (s *SrvSuite) TestAllowedLabels(c *C) {
var tests = []struct {
inLabelMap map[string]string
inLabelMap services.Labels
outError bool
}{
// Valid static label.
{
inLabelMap: map[string]string{"foo": "bar"},
inLabelMap: services.Labels{"foo": []string{"bar"}},
outError: false,
},
// Invalid static label.
{
inLabelMap: map[string]string{"foo": "baz"},
inLabelMap: services.Labels{"foo": []string{"baz"}},
outError: true,
},
// Valid dynamic label.
{
inLabelMap: map[string]string{"baz": "4"},
inLabelMap: services.Labels{"baz": []string{"4"}},
outError: false,
},
// Invalid dynamic label.
{
inLabelMap: map[string]string{"baz": "5"},
inLabelMap: services.Labels{"baz": []string{"5"}},
outError: true,
},
}
@@ -1081,7 +1081,7 @@ type upack struct {
certSigner ssh.Signer
}
func (s *SrvSuite) newUpack(username string, allowedLogins []string, allowedLabels map[string]string) (*upack, error) {
func (s *SrvSuite) newUpack(username string, allowedLogins []string, allowedLabels services.Labels) (*upack, error) {
auth := s.server.Auth()
upriv, upub, err := auth.GenerateKeyPair("")
if err != nil {
+20
View File
@@ -394,6 +394,26 @@ func (s *Strings) UnmarshalYAML(unmarshal func(interface{}) error) error {
return err
}
// MarshalJSON marshals to scalar value
// if there is only one value in the list
// to list otherwise
func (s Strings) MarshalJSON() ([]byte, error) {
if len(s) == 1 {
return json.Marshal(s[0])
}
return json.Marshal([]string(s))
}
// MarshalYAML marshals to scalar value
// if there is only one value in the list,
// marshals to list otherwise
func (s Strings) MarshalYAML() (interface{}, error) {
if len(s) == 1 {
return s[0], nil
}
return []string(s), nil
}
// Addrs returns strings list converted to address list
func (s Strings) Addrs(defaultPort int) ([]NetAddr, error) {
addrs := make([]NetAddr, len(s))
+1 -1
View File
@@ -447,7 +447,7 @@ func (s *WebSuite) TestSAMLSuccess(c *C) {
MaxSessionTTL: services.NewDuration(defaults.MaxCertDuration),
},
Allow: services.RoleConditions{
NodeLabels: map[string]string{services.Wildcard: services.Wildcard},
NodeLabels: services.Labels{services.Wildcard: []string{services.Wildcard}},
Namespaces: []string{defaults.Namespace},
Rules: []services.Rule{
services.NewRule(services.Wildcard, services.RW()),
+3 -3
View File
@@ -131,13 +131,13 @@ func printActions(rules []services.Rule) string {
return strings.Join(pairs, ",")
}
func printNodeLabels(labels map[string]string) string {
func printNodeLabels(labels services.Labels) string {
pairs := []string{}
for key, val := range labels {
for key, values := range labels {
if key == services.Wildcard {
return "<all nodes>"
}
pairs = append(pairs, fmt.Sprintf("%v=%v", key, val))
pairs = append(pairs, fmt.Sprintf("%v=%v", key, values))
}
return strings.Join(pairs, ",")
}