-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle empty statement projects for v2 and v2.1 #886
Conversation
vChan chan api.Version) (PolicyServer, PolicyRefresher, error) { | ||
|
||
return NewPoliciesServer(ctx, l, memstore.New(), e, pl, vChan) | ||
return NewPoliciesServer(ctx, l, memstore.New(), e, pl, vSwitch, vChan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, vSwitch and vChan are related but they serve different functions, so I thought it best to keep them cleanly separate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I'm clear on what the difference is anymore. Could we have a helpful comment somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vSwitch
is two things:
- an UnaryServerInterceptor, denying v1 reqs when using v2 and vice-versa (this PreconditionFailed response drives the gateway IAM version switching)
- a struct keeping an api.Version
It would be better, I think, as outlined in #853, to have a version field in the server struct, and have that server implement the Interceptor.
vChan
is a channel we're using to flip the version in that struct; based on a successful migration (upgrade-to-v2) or read on server startup (ie. when it's already been switched to IAM v2). It's a shortcut; it shouldn't be needed. We're creating a portal between two pieces of the service (the migration logic and the policies server), and have the former throw something through it at the latter. It's not how things should be, this is not something inherently calling for the use of a channel.
projects := make([]string, len(statement.Projects)) | ||
|
||
if len(statement.Projects) == 0 { | ||
if s.isBeta2p1() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything above here was to be able to have this function exist--now we can distinguish v2/v2.1 and take appropriate action.
@@ -467,6 +469,159 @@ func TestCreatePolicy(t *testing.T) { | |||
} | |||
} | |||
|
|||
func TestCreatePolicy2p1(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add some explicit 2.1 tests where it matters: create and update with no projects.
@@ -69,7 +69,7 @@ func TestCreatePolicy(t *testing.T) { | |||
assert.Equal(t, []string{"cfgmgmt:nodes:*"}, resp.Statements[0].Actions) | |||
assert.Equal(t, []string{"cfgmgmt:delete", "cfgmgmt:list"}, resp.Statements[0].Resources) | |||
assert.Empty(t, resp.Statements[0].Role) | |||
assert.Empty(t, resp.Statements[0].Projects) | |||
assert.Equal(t, []string{constants_v2.AllProjectsExternalID}, resp.Statements[0].Projects) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This affirms the change that no projects provided in 2.0 now is assigned a default "all projects".
@@ -919,19 +1074,17 @@ func TestUpdatePolicy(t *testing.T) { | |||
}}, | |||
{"successfully updates policy statements", func(t *testing.T) { | |||
storedPol, _ := addSomePoliciesToStore(t, store, prng) | |||
// change original statement effect to allow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments in this test were outdated (read: wrong)... I repurposed the test to handle both explicit and implicit "all" cases.
s0 := pol.Statements[0] | ||
assert.Equal(t, api_v2.Statement_ALLOW, s0.Effect) | ||
assert.Equal(t, []string{"compliance:profiles"}, s0.Resources) | ||
assert.Equal(t, []string{"compliance:profiles:upload"}, s0.Actions) | ||
assert.Equal(t, []string{constants_v2.AllProjectsExternalID}, s0.Projects) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implicit or explicit look the same in the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the implicit
way (as in, no projects in the statement means it applies to all projects) still works like before, why do we need to start enforcing projects on all v2.1 statements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it has never been legal to have a statement with no projects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean according to the acceptance requirements?
storageProject = constants_v2.AllProjectsExternalID | ||
} | ||
assert.Equal(t, storageProject, apiProject, "statement projects differ") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needed just this one little tweak to handle the internal/external representation differences.
return setupV2WithMigrationState(t, nil, writer, nil, nil, make(chan api_v2.Version, 1), | ||
// Returning MigrationStatus of "SuccessBeta2.1" means we've migrated successfully to IAM v2.1 | ||
func(s storage.MigrationStatusProvider) error { return s.SuccessBeta1(context.Background()) }) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @srenatus, for the framework that made adding v2.1 just plug-n-play, as it were!
v2PolServer, policyRefresher, err := v2.NewPostgresPolicyServer(ctx, l, e, migrationsConfig, | ||
dataMigrationsConfig, v1Server.Storage(), vChan) | ||
v2PolServer, policyRefresher, err := v2.NewPostgresPolicyServer( | ||
ctx, l, e, migrationsConfig, dataMigrationsConfig, v1Server.Storage(), switcher, vChan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New addition is the switcher
(line change gave me pause)
@@ -140,7 +146,7 @@ func (s *policyServer) CreatePolicy( | |||
|
|||
// API requests always create custom policies. | |||
|
|||
pol, err := policyFromAPI( | |||
pol, err := s.policyFromAPI( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why the change to have this be a function on server
. . . maybe it's too late for me to be reviewing? 🙃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that's a fair question! It is (almost) turtles all the way down. In other words, at the bottom, we need to access the new vSwitch
which is on s
. Follow that back up to here: s.vSwitch
=> s.isBeta2p1
=> s.statementFromAPI
=> s.policyFromAPI
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 Is that a smell? 🤷♂
} | ||
return resp.Version.Major, nil | ||
} | ||
|
||
func init() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid init()
is rarely the correct move 😉
I'd propse we do the following:
- add an
iam-v2
tag to diagnostic tests that require IAM v2 (here: https://github.com/chef/automate/pull/886/files#diff-78d181d2f500dd5af4a70ec53130c49cR52) - use diagnostic filters for these, like https://github.com/chef/automate/blob/master/integration/tests/upgrade_current_master.sh#L4
- upgrade-to-v2 in the
integration/tests/*.sh
scripts that run the iam v2 diagnostic tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️ we've squashed that away now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to better understand why we need to start enforcing projects on all v2.1 policy statements if the implicit empty projects still works (at least it does according to this UpdatePolicy test case)
vChan chan api.Version) (PolicyServer, PolicyRefresher, error) { | ||
|
||
return NewPoliciesServer(ctx, l, memstore.New(), e, pl, vChan) | ||
return NewPoliciesServer(ctx, l, memstore.New(), e, pl, vSwitch, vChan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I'm clear on what the difference is anymore. Could we have a helpful comment somewhere?
s0 := pol.Statements[0] | ||
assert.Equal(t, api_v2.Statement_ALLOW, s0.Effect) | ||
assert.Equal(t, []string{"compliance:profiles"}, s0.Resources) | ||
assert.Equal(t, []string{"compliance:profiles:upload"}, s0.Actions) | ||
assert.Equal(t, []string{constants_v2.AllProjectsExternalID}, s0.Projects) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the implicit
way (as in, no projects in the statement means it applies to all projects) still works like before, why do we need to start enforcing projects on all v2.1 statements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is #853 territory 😄
I'd prefer we didn't do it this way: We're adding the version switch construct, which "knows" the version, to the server. Instead, we should make the server aware of the version, and have the version switch read that.
This failure is unrelated to diagnostics: https://buildkite.com/chef/chef-automate-master-verify-private/builds/3158#9da55f3b-f5ce-4db4-8e61-f8f52a268eea/579-1323 |
0a3f7a6
to
27a798b
Compare
27a798b
to
94b07f6
Compare
@@ -902,3 +922,8 @@ func (s *policyServer) setVersionForInterceptorSwitch(v api.Version) { | |||
s.vChan <- v | |||
} | |||
} | |||
|
|||
func (s *policyServer) isBeta2p1() bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This falls apart when we're using IAM v2 endpoints with authz-service running on v1. This is what our inspec tests have been doing for a while now.
344f98e
to
a2b9366
Compare
Team: If you look at the tests changes, you'll note that for v2, this will now include It's a by-product of this approach to resolving the issue. I guess we could fiddle around a bit to remove this again if you're not on v2.1; but we already have accepted that there's What I find confusing, however, is that only the statements' |
Also, with disabling the iam-v1 token diagnostic for the iam v2 run, something has become apparent (to me): We have no upgrade test safety whatsoever for iam v2 or iam v2.1 from our diagnostics framework. I think I'll duplicate the upgrade tests...? |
Fine, let's do the dirty thing once more -- there's a card for tackling this version business already.
9a4ae3f
to
7d659b2
Compare
Still working on getting the added diagnostics test to fail properly, and then to fix it. |
@@ -0,0 +1,5 @@ | |||
BEGIN; | |||
|
|||
INSERT INTO iam_projects (id, name, type) VALUES ('*', 'Wildcard project', 'chef-managed'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we do have the ~~ALL PROJECTS~~
project . . . cannot recall why we didn't use *
all the way down . . .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'm not clear on why we need this migration)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frankly, I've made @msorens' approach work. I don't know! 💦
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er, something is amiss 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also confused. This migration makes me think we're already populating empty statements with ~~All Projects~~
@@ -20,7 +20,7 @@ type authPoliciesSave struct { | |||
func CreateAuthPoliciesDiagnostic() diagnostics.Diagnostic { | |||
return diagnostics.Diagnostic{ | |||
Name: "auth-policies", | |||
Tags: diagnostics.Tags{"auth"}, | |||
Tags: diagnostics.Tags{"auth", "iam-v1"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oof shouldn't we have a test where we do migrate the v1 policies? 😩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the v1-to-v2 migration, this is used to (among other things) test an actual upgrade of the A2 version installed.
Also, we've got "did these policies get migrated?" tests; but they only assert that a migrated policy is there, I don't think they assert that they're working. However, implicitly, I believe this is tested for admin tokens 🤔
@@ -10,8 +10,8 @@ test_upgrades=true | |||
# do_test_deploy runs _before_ the upgrade, so we upgrade _from a state with | |||
# IAMv2 enabled_, and the diagnostics are run twice: before and after upgrade. | |||
do_test_deploy() { | |||
log_info "run chef-automate iam upgrade-to-v2 --skip-policy-migration" | |||
chef-automate iam upgrade-to-v2 --skip-policy-migration || return 1 | |||
log_info "run chef-automate iam upgrade-to-v2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh this is what i meant in my prev comment 😂
571ddd3
to
7d659b2
Compare
94c8b22
to
83723bd
Compare
ef084eb
to
c4a8e11
Compare
bc211ce
to
9a48129
Compare
@blakestier as per discussion in the deployment-team channel -- since we're testing upgrades here, too (trying to do a much as we can reasonably do in one test), the first deployment is happening with code in update I was a bit absent-minded there. With #948 we should be fine. update I was a bit absent-minded there. Again. With #951 we should be fine. |
4e010e9
to
67875a4
Compare
67875a4
to
bc37cf8
Compare
We were not dealing with empty statement projects correctly. For v2, it should be coerced to a single element list containing "*" indicating all projects. For v2.1, it should fail as an error. Signed-off-by: michael sorens <[email protected]>
Signed-off-by: michael sorens <[email protected]>
Signed-off-by: Stephan Renatus <[email protected]>
Signed-off-by: Stephan Renatus <[email protected]>
Signed-off-by: Stephan Renatus <[email protected]>
Signed-off-by: Stephan Renatus <[email protected]>
This is returned now. The question we'll have to discuss is if this is acceptable. I think it might be, since we've already included a projects field in the responses for iam v2 (not v2.1). The distinction just isn't as clear-cut as I'd like it to be right now, but it might not be worth fixing now we're so deep into this. Signed-off-by: Stephan Renatus <[email protected]>
These would only pass if we migrated the v1 policies. We don't. Signed-off-by: Stephan Renatus <[email protected]>
Is it that easy? We'll see. Signed-off-by: Stephan Renatus <[email protected]>
Signed-off-by: Stephan Renatus <[email protected]>
Disabled user/team diagnostics when explicitly testing v2 -- there are no corresponding v2 (or v2.1) tests yet! Signed-off-by: Stephan Renatus <[email protected]>
bc37cf8
to
b2f0173
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this gets things working again, let's do it! We have a card to deal refactoring how all the version handling is working so that seems like the place to address any tech debt here. Thanks for digging into this 🕵️♀️
log_info "run chef-automate iam upgrade-to-v2 --skip-policy-migration" | ||
chef-automate iam upgrade-to-v2 --skip-policy-migration || return 1 | ||
|
||
# ensure service startup works with IAM v2: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😍
🔩 Description
In 2.1 policy statements must have a non-empty project.
Any policies created in v1, when migrated, will get "all projects" (
*
) assigned during migration.Any policies created in v2.0, however, do not go through any migration. Therefore they must be created with the same "all projects" (
*
) silently appended if no projects are specified.Bug #1: Creating a policy in v2.0 has an empty projects list on statements.
Bug #2: Creating a policy in v2.1 with no statement projects generates an invalid policy, i.e. a policy with no projects.
👍 Definition of Done
*
).👟 Demo Script / Repro Steps
rebuild components/authz-service
In v2.0, create a policy with no statement projects, e.g. :
Read it back and observe it has the default
*
assigned. Here's one way to do that:Of course, if you explicitly include all projects in the statement (
projects: ["*"]
), that should return an identical result.In v2.1, if you attempt to create a policy with no statement projects you will now receive an error, e.g.:
⛓️ Related Resources
✅ Checklist