github.com/ilhicas/nomad@v0.11.8/acl/acl_test.go (about)

     1  package acl
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  )
     8  
     9  func TestCapabilitySet(t *testing.T) {
    10  	var cs capabilitySet = make(map[string]struct{})
    11  
    12  	// Check no capabilities by default
    13  	if cs.Check(PolicyDeny) {
    14  		t.Fatalf("unexpected check")
    15  	}
    16  
    17  	// Do a set and check
    18  	cs.Set(PolicyDeny)
    19  	if !cs.Check(PolicyDeny) {
    20  		t.Fatalf("missing check")
    21  	}
    22  
    23  	// Clear and check
    24  	cs.Clear()
    25  	if cs.Check(PolicyDeny) {
    26  		t.Fatalf("unexpected check")
    27  	}
    28  }
    29  
    30  func TestMaxPrivilege(t *testing.T) {
    31  	type tcase struct {
    32  		Privilege      string
    33  		PrecedenceOver []string
    34  	}
    35  	tcases := []tcase{
    36  		{
    37  			PolicyDeny,
    38  			[]string{PolicyDeny, PolicyWrite, PolicyRead, ""},
    39  		},
    40  		{
    41  			PolicyWrite,
    42  			[]string{PolicyWrite, PolicyRead, ""},
    43  		},
    44  		{
    45  			PolicyRead,
    46  			[]string{PolicyRead, ""},
    47  		},
    48  	}
    49  
    50  	for idx1, tc := range tcases {
    51  		for idx2, po := range tc.PrecedenceOver {
    52  			if maxPrivilege(tc.Privilege, po) != tc.Privilege {
    53  				t.Fatalf("failed %d %d", idx1, idx2)
    54  			}
    55  			if maxPrivilege(po, tc.Privilege) != tc.Privilege {
    56  				t.Fatalf("failed %d %d", idx1, idx2)
    57  			}
    58  		}
    59  	}
    60  }
    61  
    62  func TestACLManagement(t *testing.T) {
    63  	assert := assert.New(t)
    64  
    65  	// Create management ACL
    66  	acl, err := NewACL(true, nil)
    67  	assert.Nil(err)
    68  
    69  	// Check default namespace rights
    70  	assert.True(acl.AllowNamespaceOperation("default", NamespaceCapabilityListJobs))
    71  	assert.True(acl.AllowNamespaceOperation("default", NamespaceCapabilitySubmitJob))
    72  	assert.True(acl.AllowNamespace("default"))
    73  
    74  	// Check non-specified namespace
    75  	assert.True(acl.AllowNamespaceOperation("foo", NamespaceCapabilityListJobs))
    76  	assert.True(acl.AllowNamespace("foo"))
    77  
    78  	// Check the other simpler operations
    79  	assert.True(acl.IsManagement())
    80  	assert.True(acl.AllowAgentRead())
    81  	assert.True(acl.AllowAgentWrite())
    82  	assert.True(acl.AllowNodeRead())
    83  	assert.True(acl.AllowNodeWrite())
    84  	assert.True(acl.AllowOperatorRead())
    85  	assert.True(acl.AllowOperatorWrite())
    86  	assert.True(acl.AllowQuotaRead())
    87  	assert.True(acl.AllowQuotaWrite())
    88  }
    89  
    90  func TestACLMerge(t *testing.T) {
    91  	assert := assert.New(t)
    92  
    93  	// Merge read + write policy
    94  	p1, err := Parse(readAll)
    95  	assert.Nil(err)
    96  	p2, err := Parse(writeAll)
    97  	assert.Nil(err)
    98  	acl, err := NewACL(false, []*Policy{p1, p2})
    99  	assert.Nil(err)
   100  
   101  	// Check default namespace rights
   102  	assert.True(acl.AllowNamespaceOperation("default", NamespaceCapabilityListJobs))
   103  	assert.True(acl.AllowNamespaceOperation("default", NamespaceCapabilitySubmitJob))
   104  	assert.True(acl.AllowNamespace("default"))
   105  
   106  	// Check non-specified namespace
   107  	assert.False(acl.AllowNamespaceOperation("foo", NamespaceCapabilityListJobs))
   108  	assert.False(acl.AllowNamespace("foo"))
   109  
   110  	// Check the other simpler operations
   111  	assert.False(acl.IsManagement())
   112  	assert.True(acl.AllowAgentRead())
   113  	assert.True(acl.AllowAgentWrite())
   114  	assert.True(acl.AllowNodeRead())
   115  	assert.True(acl.AllowNodeWrite())
   116  	assert.True(acl.AllowOperatorRead())
   117  	assert.True(acl.AllowOperatorWrite())
   118  	assert.True(acl.AllowQuotaRead())
   119  	assert.True(acl.AllowQuotaWrite())
   120  
   121  	// Merge read + blank
   122  	p3, err := Parse("")
   123  	assert.Nil(err)
   124  	acl, err = NewACL(false, []*Policy{p1, p3})
   125  	assert.Nil(err)
   126  
   127  	// Check default namespace rights
   128  	assert.True(acl.AllowNamespaceOperation("default", NamespaceCapabilityListJobs))
   129  	assert.False(acl.AllowNamespaceOperation("default", NamespaceCapabilitySubmitJob))
   130  
   131  	// Check non-specified namespace
   132  	assert.False(acl.AllowNamespaceOperation("foo", NamespaceCapabilityListJobs))
   133  
   134  	// Check the other simpler operations
   135  	assert.False(acl.IsManagement())
   136  	assert.True(acl.AllowAgentRead())
   137  	assert.False(acl.AllowAgentWrite())
   138  	assert.True(acl.AllowNodeRead())
   139  	assert.False(acl.AllowNodeWrite())
   140  	assert.True(acl.AllowOperatorRead())
   141  	assert.False(acl.AllowOperatorWrite())
   142  	assert.True(acl.AllowQuotaRead())
   143  	assert.False(acl.AllowQuotaWrite())
   144  
   145  	// Merge read + deny
   146  	p4, err := Parse(denyAll)
   147  	assert.Nil(err)
   148  	acl, err = NewACL(false, []*Policy{p1, p4})
   149  	assert.Nil(err)
   150  
   151  	// Check default namespace rights
   152  	assert.False(acl.AllowNamespaceOperation("default", NamespaceCapabilityListJobs))
   153  	assert.False(acl.AllowNamespaceOperation("default", NamespaceCapabilitySubmitJob))
   154  
   155  	// Check non-specified namespace
   156  	assert.False(acl.AllowNamespaceOperation("foo", NamespaceCapabilityListJobs))
   157  
   158  	// Check the other simpler operations
   159  	assert.False(acl.IsManagement())
   160  	assert.False(acl.AllowAgentRead())
   161  	assert.False(acl.AllowAgentWrite())
   162  	assert.False(acl.AllowNodeRead())
   163  	assert.False(acl.AllowNodeWrite())
   164  	assert.False(acl.AllowOperatorRead())
   165  	assert.False(acl.AllowOperatorWrite())
   166  	assert.False(acl.AllowQuotaRead())
   167  	assert.False(acl.AllowQuotaWrite())
   168  }
   169  
   170  var readAll = `
   171  namespace "default" {
   172  	policy = "read"
   173  }
   174  agent {
   175  	policy = "read"
   176  }
   177  node {
   178  	policy = "read"
   179  }
   180  operator {
   181  	policy = "read"
   182  }
   183  quota {
   184  	policy = "read"
   185  }
   186  `
   187  
   188  var writeAll = `
   189  namespace "default" {
   190  	policy = "write"
   191  }
   192  agent {
   193  	policy = "write"
   194  }
   195  node {
   196  	policy = "write"
   197  }
   198  operator {
   199  	policy = "write"
   200  }
   201  quota {
   202  	policy = "write"
   203  }
   204  `
   205  
   206  var denyAll = `
   207  namespace "default" {
   208  	policy = "deny"
   209  }
   210  agent {
   211  	policy = "deny"
   212  }
   213  node {
   214  	policy = "deny"
   215  }
   216  operator {
   217  	policy = "deny"
   218  }
   219  quota {
   220  	policy = "deny"
   221  }
   222  `
   223  
   224  func TestAllowNamespace(t *testing.T) {
   225  	tests := []struct {
   226  		Policy string
   227  		Allow  bool
   228  	}{
   229  		{
   230  			Policy: `namespace "foo" {}`,
   231  			Allow:  false,
   232  		},
   233  		{
   234  			Policy: `namespace "foo" { policy = "deny" }`,
   235  			Allow:  false,
   236  		},
   237  		{
   238  			Policy: `namespace "foo" { capabilities = ["deny"] }`,
   239  			Allow:  false,
   240  		},
   241  		{
   242  			Policy: `namespace "foo" { capabilities = ["list-jobs"] }`,
   243  			Allow:  true,
   244  		},
   245  		{
   246  			Policy: `namespace "foo" { policy = "read" }`,
   247  			Allow:  true,
   248  		},
   249  	}
   250  
   251  	for _, tc := range tests {
   252  		t.Run(tc.Policy, func(t *testing.T) {
   253  			assert := assert.New(t)
   254  
   255  			policy, err := Parse(tc.Policy)
   256  			assert.Nil(err)
   257  
   258  			acl, err := NewACL(false, []*Policy{policy})
   259  			assert.Nil(err)
   260  
   261  			assert.Equal(tc.Allow, acl.AllowNamespace("foo"))
   262  		})
   263  	}
   264  }
   265  
   266  func TestWildcardNamespaceMatching(t *testing.T) {
   267  	tests := []struct {
   268  		Policy string
   269  		Allow  bool
   270  	}{
   271  		{ // Wildcard matches
   272  			Policy: `namespace "prod-api-*" { policy = "write" }`,
   273  			Allow:  true,
   274  		},
   275  		{ // Non globbed namespaces are not wildcards
   276  			Policy: `namespace "prod-api" { policy = "write" }`,
   277  			Allow:  false,
   278  		},
   279  		{ // Concrete matches take precedence
   280  			Policy: `namespace "prod-api-services" { policy = "deny" }
   281  			         namespace "prod-api-*" { policy = "write" }`,
   282  			Allow: false,
   283  		},
   284  		{
   285  			Policy: `namespace "prod-api-*" { policy = "deny" }
   286  			         namespace "prod-api-services" { policy = "write" }`,
   287  			Allow: true,
   288  		},
   289  		{ // The closest character match wins
   290  			Policy: `namespace "*-api-services" { policy = "deny" }
   291  			         namespace "prod-api-*" { policy = "write" }`, // 4 vs 8 chars
   292  			Allow: false,
   293  		},
   294  		{
   295  			Policy: `namespace "prod-api-*" { policy = "write" }
   296                 namespace "*-api-services" { policy = "deny" }`, // 4 vs 8 chars
   297  			Allow: false,
   298  		},
   299  	}
   300  
   301  	for _, tc := range tests {
   302  		t.Run(tc.Policy, func(t *testing.T) {
   303  			assert := assert.New(t)
   304  
   305  			policy, err := Parse(tc.Policy)
   306  			assert.NoError(err)
   307  			assert.NotNil(policy.Namespaces)
   308  
   309  			acl, err := NewACL(false, []*Policy{policy})
   310  			assert.Nil(err)
   311  
   312  			assert.Equal(tc.Allow, acl.AllowNamespace("prod-api-services"))
   313  		})
   314  	}
   315  }
   316  
   317  func TestWildcardHostVolumeMatching(t *testing.T) {
   318  	tests := []struct {
   319  		Policy string
   320  		Allow  bool
   321  	}{
   322  		{ // Wildcard matches
   323  			Policy: `host_volume "prod-api-*" { policy = "write" }`,
   324  			Allow:  true,
   325  		},
   326  		{ // Non globbed volumes are not wildcards
   327  			Policy: `host_volume "prod-api" { policy = "write" }`,
   328  			Allow:  false,
   329  		},
   330  		{ // Concrete matches take precedence
   331  			Policy: `host_volume "prod-api-services" { policy = "deny" }
   332  			         host_volume "prod-api-*" { policy = "write" }`,
   333  			Allow: false,
   334  		},
   335  		{
   336  			Policy: `host_volume "prod-api-*" { policy = "deny" }
   337  			         host_volume "prod-api-services" { policy = "write" }`,
   338  			Allow: true,
   339  		},
   340  		{ // The closest character match wins
   341  			Policy: `host_volume "*-api-services" { policy = "deny" }
   342  			         host_volume "prod-api-*" { policy = "write" }`, // 4 vs 8 chars
   343  			Allow: false,
   344  		},
   345  		{
   346  			Policy: `host_volume "prod-api-*" { policy = "write" }
   347                 host_volume "*-api-services" { policy = "deny" }`, // 4 vs 8 chars
   348  			Allow: false,
   349  		},
   350  	}
   351  
   352  	for _, tc := range tests {
   353  		t.Run(tc.Policy, func(t *testing.T) {
   354  			assert := assert.New(t)
   355  
   356  			policy, err := Parse(tc.Policy)
   357  			assert.NoError(err)
   358  			assert.NotNil(policy.HostVolumes)
   359  
   360  			acl, err := NewACL(false, []*Policy{policy})
   361  			assert.Nil(err)
   362  
   363  			assert.Equal(tc.Allow, acl.AllowHostVolume("prod-api-services"))
   364  		})
   365  	}
   366  }
   367  func TestACL_matchingCapabilitySet_returnsAllMatches(t *testing.T) {
   368  	tests := []struct {
   369  		Policy        string
   370  		NS            string
   371  		MatchingGlobs []string
   372  	}{
   373  		{
   374  			Policy:        `namespace "production-*" { policy = "write" }`,
   375  			NS:            "production-api",
   376  			MatchingGlobs: []string{"production-*"},
   377  		},
   378  		{
   379  			Policy:        `namespace "prod-*" { policy = "write" }`,
   380  			NS:            "production-api",
   381  			MatchingGlobs: nil,
   382  		},
   383  		{
   384  			Policy: `namespace "production-*" { policy = "write" }
   385  							 namespace "production-*-api" { policy = "deny" }`,
   386  
   387  			NS:            "production-admin-api",
   388  			MatchingGlobs: []string{"production-*", "production-*-api"},
   389  		},
   390  	}
   391  
   392  	for _, tc := range tests {
   393  		t.Run(tc.Policy, func(t *testing.T) {
   394  			assert := assert.New(t)
   395  
   396  			policy, err := Parse(tc.Policy)
   397  			assert.NoError(err)
   398  			assert.NotNil(policy.Namespaces)
   399  
   400  			acl, err := NewACL(false, []*Policy{policy})
   401  			assert.Nil(err)
   402  
   403  			var namespaces []string
   404  			for _, cs := range findAllMatchingWildcards(acl.wildcardNamespaces, tc.NS) {
   405  				namespaces = append(namespaces, cs.name)
   406  			}
   407  
   408  			assert.Equal(tc.MatchingGlobs, namespaces)
   409  		})
   410  	}
   411  }
   412  
   413  func TestACL_matchingCapabilitySet_difference(t *testing.T) {
   414  	tests := []struct {
   415  		Policy     string
   416  		NS         string
   417  		Difference int
   418  	}{
   419  		{
   420  			Policy:     `namespace "production-*" { policy = "write" }`,
   421  			NS:         "production-api",
   422  			Difference: 3,
   423  		},
   424  		{
   425  			Policy:     `namespace "production-*" { policy = "write" }`,
   426  			NS:         "production-admin-api",
   427  			Difference: 9,
   428  		},
   429  		{
   430  			Policy:     `namespace "production-**" { policy = "write" }`,
   431  			NS:         "production-admin-api",
   432  			Difference: 9,
   433  		},
   434  		{
   435  			Policy:     `namespace "*" { policy = "write" }`,
   436  			NS:         "production-admin-api",
   437  			Difference: 20,
   438  		},
   439  		{
   440  			Policy:     `namespace "*admin*" { policy = "write" }`,
   441  			NS:         "production-admin-api",
   442  			Difference: 15,
   443  		},
   444  	}
   445  
   446  	for _, tc := range tests {
   447  		t.Run(tc.Policy, func(t *testing.T) {
   448  			assert := assert.New(t)
   449  
   450  			policy, err := Parse(tc.Policy)
   451  			assert.NoError(err)
   452  			assert.NotNil(policy.Namespaces)
   453  
   454  			acl, err := NewACL(false, []*Policy{policy})
   455  			assert.Nil(err)
   456  
   457  			matches := findAllMatchingWildcards(acl.wildcardNamespaces, tc.NS)
   458  			assert.Equal(tc.Difference, matches[0].difference)
   459  		})
   460  	}
   461  
   462  }