1package acl 2 3import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/mock" 8 "github.com/stretchr/testify/require" 9) 10 11type mockAuthorizer struct { 12 mock.Mock 13} 14 15var _ Authorizer = (*mockAuthorizer)(nil) 16 17// ACLRead checks for permission to list all the ACLs 18func (m *mockAuthorizer) ACLRead(ctx *AuthorizerContext) EnforcementDecision { 19 ret := m.Called(ctx) 20 return ret.Get(0).(EnforcementDecision) 21} 22 23// ACLWrite checks for permission to manipulate ACLs 24func (m *mockAuthorizer) ACLWrite(ctx *AuthorizerContext) EnforcementDecision { 25 ret := m.Called(ctx) 26 return ret.Get(0).(EnforcementDecision) 27} 28 29// AgentRead checks for permission to read from agent endpoints for a 30// given node. 31func (m *mockAuthorizer) AgentRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 32 ret := m.Called(segment, ctx) 33 return ret.Get(0).(EnforcementDecision) 34} 35 36// AgentWrite checks for permission to make changes via agent endpoints 37// for a given node. 38func (m *mockAuthorizer) AgentWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 39 ret := m.Called(segment, ctx) 40 return ret.Get(0).(EnforcementDecision) 41} 42 43// EventRead determines if a specific event can be queried. 44func (m *mockAuthorizer) EventRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 45 ret := m.Called(segment, ctx) 46 return ret.Get(0).(EnforcementDecision) 47} 48 49// EventWrite determines if a specific event may be fired. 50func (m *mockAuthorizer) EventWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 51 ret := m.Called(segment, ctx) 52 return ret.Get(0).(EnforcementDecision) 53} 54 55// IntentionDefaultAllow determines the default authorized behavior 56// when no intentions match a Connect request. 57func (m *mockAuthorizer) IntentionDefaultAllow(ctx *AuthorizerContext) EnforcementDecision { 58 ret := m.Called(ctx) 59 return ret.Get(0).(EnforcementDecision) 60} 61 62// IntentionRead determines if a specific intention can be read. 63func (m *mockAuthorizer) IntentionRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 64 ret := m.Called(segment, ctx) 65 return ret.Get(0).(EnforcementDecision) 66} 67 68// IntentionWrite determines if a specific intention can be 69// created, modified, or deleted. 70func (m *mockAuthorizer) IntentionWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 71 ret := m.Called(segment, ctx) 72 return ret.Get(0).(EnforcementDecision) 73} 74 75// KeyList checks for permission to list keys under a prefix 76func (m *mockAuthorizer) KeyList(segment string, ctx *AuthorizerContext) EnforcementDecision { 77 ret := m.Called(segment, ctx) 78 return ret.Get(0).(EnforcementDecision) 79} 80 81// KeyRead checks for permission to read a given key 82func (m *mockAuthorizer) KeyRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 83 ret := m.Called(segment, ctx) 84 return ret.Get(0).(EnforcementDecision) 85} 86 87// KeyWrite checks for permission to write a given key 88func (m *mockAuthorizer) KeyWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 89 ret := m.Called(segment, ctx) 90 return ret.Get(0).(EnforcementDecision) 91} 92 93// KeyWritePrefix checks for permission to write to an 94// entire key prefix. This means there must be no sub-policies 95// that deny a write. 96func (m *mockAuthorizer) KeyWritePrefix(segment string, ctx *AuthorizerContext) EnforcementDecision { 97 ret := m.Called(segment, ctx) 98 return ret.Get(0).(EnforcementDecision) 99} 100 101// KeyringRead determines if the encryption keyring used in 102// the gossip layer can be read. 103func (m *mockAuthorizer) KeyringRead(ctx *AuthorizerContext) EnforcementDecision { 104 ret := m.Called(ctx) 105 return ret.Get(0).(EnforcementDecision) 106} 107 108// KeyringWrite determines if the keyring can be manipulated 109func (m *mockAuthorizer) KeyringWrite(ctx *AuthorizerContext) EnforcementDecision { 110 ret := m.Called(ctx) 111 return ret.Get(0).(EnforcementDecision) 112} 113 114// NodeRead checks for permission to read (discover) a given node. 115func (m *mockAuthorizer) NodeRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 116 ret := m.Called(segment, ctx) 117 return ret.Get(0).(EnforcementDecision) 118} 119 120func (m *mockAuthorizer) NodeReadAll(ctx *AuthorizerContext) EnforcementDecision { 121 ret := m.Called(ctx) 122 return ret.Get(0).(EnforcementDecision) 123} 124 125// NodeWrite checks for permission to create or update (register) a 126// given node. 127func (m *mockAuthorizer) NodeWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 128 ret := m.Called(segment, ctx) 129 return ret.Get(0).(EnforcementDecision) 130} 131 132// OperatorRead determines if the read-only Consul operator functions 133// can be used. ret := m.Called(segment, ctx) 134func (m *mockAuthorizer) OperatorRead(ctx *AuthorizerContext) EnforcementDecision { 135 ret := m.Called(ctx) 136 return ret.Get(0).(EnforcementDecision) 137} 138 139// OperatorWrite determines if the state-changing Consul operator 140// functions can be used. 141func (m *mockAuthorizer) OperatorWrite(ctx *AuthorizerContext) EnforcementDecision { 142 ret := m.Called(ctx) 143 return ret.Get(0).(EnforcementDecision) 144} 145 146// PreparedQueryRead determines if a specific prepared query can be read 147// to show its contents (this is not used for execution). 148func (m *mockAuthorizer) PreparedQueryRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 149 ret := m.Called(segment, ctx) 150 return ret.Get(0).(EnforcementDecision) 151} 152 153// PreparedQueryWrite determines if a specific prepared query can be 154// created, modified, or deleted. 155func (m *mockAuthorizer) PreparedQueryWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 156 ret := m.Called(segment, ctx) 157 return ret.Get(0).(EnforcementDecision) 158} 159 160// ServiceRead checks for permission to read a given service 161func (m *mockAuthorizer) ServiceRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 162 ret := m.Called(segment, ctx) 163 return ret.Get(0).(EnforcementDecision) 164} 165 166func (m *mockAuthorizer) ServiceReadAll(ctx *AuthorizerContext) EnforcementDecision { 167 ret := m.Called(ctx) 168 return ret.Get(0).(EnforcementDecision) 169} 170 171// ServiceWrite checks for permission to create or update a given 172// service 173func (m *mockAuthorizer) ServiceWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 174 ret := m.Called(segment, ctx) 175 return ret.Get(0).(EnforcementDecision) 176} 177 178// SessionRead checks for permission to read sessions for a given node. 179func (m *mockAuthorizer) SessionRead(segment string, ctx *AuthorizerContext) EnforcementDecision { 180 ret := m.Called(segment, ctx) 181 return ret.Get(0).(EnforcementDecision) 182} 183 184// SessionWrite checks for permission to create sessions for a given 185// node. 186func (m *mockAuthorizer) SessionWrite(segment string, ctx *AuthorizerContext) EnforcementDecision { 187 ret := m.Called(segment, ctx) 188 return ret.Get(0).(EnforcementDecision) 189} 190 191// Snapshot checks for permission to take and restore snapshots. 192func (m *mockAuthorizer) Snapshot(ctx *AuthorizerContext) EnforcementDecision { 193 ret := m.Called(ctx) 194 return ret.Get(0).(EnforcementDecision) 195} 196 197func TestACL_Enforce(t *testing.T) { 198 type testCase struct { 199 method string 200 resource Resource 201 segment string 202 access string 203 ret EnforcementDecision 204 err string 205 } 206 207 testName := func(t testCase) string { 208 if t.segment != "" { 209 return fmt.Sprintf("%s/%s/%s/%s", t.resource, t.segment, t.access, t.ret.String()) 210 } 211 return fmt.Sprintf("%s/%s/%s", t.resource, t.access, t.ret.String()) 212 } 213 214 cases := []testCase{ 215 { 216 method: "ACLRead", 217 resource: ResourceACL, 218 access: "read", 219 ret: Deny, 220 }, 221 { 222 method: "ACLRead", 223 resource: ResourceACL, 224 access: "read", 225 ret: Allow, 226 }, 227 { 228 method: "ACLWrite", 229 resource: ResourceACL, 230 access: "write", 231 ret: Deny, 232 }, 233 { 234 method: "ACLWrite", 235 resource: ResourceACL, 236 access: "write", 237 ret: Allow, 238 }, 239 { 240 resource: ResourceACL, 241 access: "list", 242 ret: Deny, 243 err: "Invalid access level", 244 }, 245 { 246 method: "OperatorRead", 247 resource: ResourceOperator, 248 access: "read", 249 ret: Deny, 250 }, 251 { 252 method: "OperatorRead", 253 resource: ResourceOperator, 254 access: "read", 255 ret: Allow, 256 }, 257 { 258 method: "OperatorWrite", 259 resource: ResourceOperator, 260 access: "write", 261 ret: Deny, 262 }, 263 { 264 method: "OperatorWrite", 265 resource: ResourceOperator, 266 access: "write", 267 ret: Allow, 268 }, 269 { 270 resource: ResourceOperator, 271 access: "list", 272 ret: Deny, 273 err: "Invalid access level", 274 }, 275 { 276 method: "KeyringRead", 277 resource: ResourceKeyring, 278 access: "read", 279 ret: Deny, 280 }, 281 { 282 method: "KeyringRead", 283 resource: ResourceKeyring, 284 access: "read", 285 ret: Allow, 286 }, 287 { 288 method: "KeyringWrite", 289 resource: ResourceKeyring, 290 access: "write", 291 ret: Deny, 292 }, 293 { 294 method: "KeyringWrite", 295 resource: ResourceKeyring, 296 access: "write", 297 ret: Allow, 298 }, 299 { 300 resource: ResourceKeyring, 301 access: "list", 302 ret: Deny, 303 err: "Invalid access level", 304 }, 305 { 306 method: "AgentRead", 307 resource: ResourceAgent, 308 segment: "foo", 309 access: "read", 310 ret: Deny, 311 }, 312 { 313 method: "AgentRead", 314 resource: ResourceAgent, 315 segment: "foo", 316 access: "read", 317 ret: Allow, 318 }, 319 { 320 method: "AgentWrite", 321 resource: ResourceAgent, 322 segment: "foo", 323 access: "write", 324 ret: Deny, 325 }, 326 { 327 method: "AgentWrite", 328 resource: ResourceAgent, 329 segment: "foo", 330 access: "write", 331 ret: Allow, 332 }, 333 { 334 resource: ResourceAgent, 335 segment: "foo", 336 access: "list", 337 ret: Deny, 338 err: "Invalid access level", 339 }, 340 { 341 method: "EventRead", 342 resource: ResourceEvent, 343 segment: "foo", 344 access: "read", 345 ret: Deny, 346 }, 347 { 348 method: "EventRead", 349 resource: ResourceEvent, 350 segment: "foo", 351 access: "read", 352 ret: Allow, 353 }, 354 { 355 method: "EventWrite", 356 resource: ResourceEvent, 357 segment: "foo", 358 access: "write", 359 ret: Deny, 360 }, 361 { 362 method: "EventWrite", 363 resource: ResourceEvent, 364 segment: "foo", 365 access: "write", 366 ret: Allow, 367 }, 368 { 369 resource: ResourceEvent, 370 segment: "foo", 371 access: "list", 372 ret: Deny, 373 err: "Invalid access level", 374 }, 375 { 376 method: "IntentionRead", 377 resource: ResourceIntention, 378 segment: "foo", 379 access: "read", 380 ret: Deny, 381 }, 382 { 383 method: "IntentionRead", 384 resource: ResourceIntention, 385 segment: "foo", 386 access: "read", 387 ret: Allow, 388 }, 389 { 390 method: "IntentionWrite", 391 resource: ResourceIntention, 392 segment: "foo", 393 access: "write", 394 ret: Deny, 395 }, 396 { 397 method: "IntentionWrite", 398 resource: ResourceIntention, 399 segment: "foo", 400 access: "write", 401 ret: Allow, 402 }, 403 { 404 resource: ResourceIntention, 405 segment: "foo", 406 access: "list", 407 ret: Deny, 408 err: "Invalid access level", 409 }, 410 { 411 method: "NodeRead", 412 resource: ResourceNode, 413 segment: "foo", 414 access: "read", 415 ret: Deny, 416 }, 417 { 418 method: "NodeRead", 419 resource: ResourceNode, 420 segment: "foo", 421 access: "read", 422 ret: Allow, 423 }, 424 { 425 method: "NodeWrite", 426 resource: ResourceNode, 427 segment: "foo", 428 access: "write", 429 ret: Deny, 430 }, 431 { 432 method: "NodeWrite", 433 resource: ResourceNode, 434 segment: "foo", 435 access: "write", 436 ret: Allow, 437 }, 438 { 439 resource: ResourceNode, 440 segment: "foo", 441 access: "list", 442 ret: Deny, 443 err: "Invalid access level", 444 }, 445 { 446 method: "PreparedQueryRead", 447 resource: ResourceQuery, 448 segment: "foo", 449 access: "read", 450 ret: Deny, 451 }, 452 { 453 method: "PreparedQueryRead", 454 resource: ResourceQuery, 455 segment: "foo", 456 access: "read", 457 ret: Allow, 458 }, 459 { 460 method: "PreparedQueryWrite", 461 resource: ResourceQuery, 462 segment: "foo", 463 access: "write", 464 ret: Deny, 465 }, 466 { 467 method: "PreparedQueryWrite", 468 resource: ResourceQuery, 469 segment: "foo", 470 access: "write", 471 ret: Allow, 472 }, 473 { 474 resource: ResourceQuery, 475 segment: "foo", 476 access: "list", 477 ret: Deny, 478 err: "Invalid access level", 479 }, 480 { 481 method: "ServiceRead", 482 resource: ResourceService, 483 segment: "foo", 484 access: "read", 485 ret: Deny, 486 }, 487 { 488 method: "ServiceRead", 489 resource: ResourceService, 490 segment: "foo", 491 access: "read", 492 ret: Allow, 493 }, 494 { 495 method: "ServiceWrite", 496 resource: ResourceService, 497 segment: "foo", 498 access: "write", 499 ret: Deny, 500 }, 501 { 502 method: "ServiceWrite", 503 resource: ResourceService, 504 segment: "foo", 505 access: "write", 506 ret: Allow, 507 }, 508 { 509 resource: ResourceSession, 510 segment: "foo", 511 access: "list", 512 ret: Deny, 513 err: "Invalid access level", 514 }, 515 { 516 method: "SessionRead", 517 resource: ResourceSession, 518 segment: "foo", 519 access: "read", 520 ret: Deny, 521 }, 522 { 523 method: "SessionRead", 524 resource: ResourceSession, 525 segment: "foo", 526 access: "read", 527 ret: Allow, 528 }, 529 { 530 method: "SessionWrite", 531 resource: ResourceSession, 532 segment: "foo", 533 access: "write", 534 ret: Deny, 535 }, 536 { 537 method: "SessionWrite", 538 resource: ResourceSession, 539 segment: "foo", 540 access: "write", 541 ret: Allow, 542 }, 543 { 544 resource: ResourceSession, 545 segment: "foo", 546 access: "list", 547 ret: Deny, 548 err: "Invalid access level", 549 }, 550 { 551 method: "KeyRead", 552 resource: ResourceKey, 553 segment: "foo", 554 access: "read", 555 ret: Deny, 556 }, 557 { 558 method: "KeyRead", 559 resource: ResourceKey, 560 segment: "foo", 561 access: "read", 562 ret: Allow, 563 }, 564 { 565 method: "KeyWrite", 566 resource: ResourceKey, 567 segment: "foo", 568 access: "write", 569 ret: Deny, 570 }, 571 { 572 method: "KeyWrite", 573 resource: ResourceKey, 574 segment: "foo", 575 access: "write", 576 ret: Allow, 577 }, 578 { 579 method: "KeyList", 580 resource: ResourceKey, 581 segment: "foo", 582 access: "list", 583 ret: Deny, 584 }, 585 { 586 method: "KeyList", 587 resource: ResourceKey, 588 segment: "foo", 589 access: "list", 590 ret: Allow, 591 }, 592 { 593 resource: ResourceKey, 594 segment: "foo", 595 access: "deny", 596 ret: Deny, 597 err: "Invalid access level", 598 }, 599 { 600 resource: "not-a-real-resource", 601 access: "read", 602 ret: Deny, 603 err: "Invalid ACL resource requested:", 604 }, 605 } 606 607 for _, tcase := range cases { 608 t.Run(testName(tcase), func(t *testing.T) { 609 m := &mockAuthorizer{} 610 611 if tcase.err == "" { 612 var nilCtx *AuthorizerContext 613 if tcase.segment != "" { 614 m.On(tcase.method, tcase.segment, nilCtx).Return(tcase.ret) 615 } else { 616 m.On(tcase.method, nilCtx).Return(tcase.ret) 617 } 618 } 619 620 ret, err := Enforce(m, tcase.resource, tcase.segment, tcase.access, nil) 621 if tcase.err == "" { 622 require.NoError(t, err) 623 } else { 624 require.Error(t, err) 625 require.Contains(t, err.Error(), tcase.err) 626 } 627 require.Equal(t, tcase.ret, ret) 628 m.AssertExpectations(t) 629 }) 630 } 631} 632