Skip to content
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

Merged
merged 11 commits into from
Jul 19, 2019

Conversation

msorens
Copy link
Contributor

@msorens msorens commented Jul 12, 2019

🔩 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

  1. V2.0 policies with an empty projects list are assigned the default "all projects" (*).
  2. Attempt to create v2.1 policies with an empty projects list are rejected.

👟 Demo Script / Repro Steps

rebuild components/authz-service

In v2.0, create a policy with no statement projects, e.g. :

curl -sSkH "api-token: $TOK" -d \
'{"id":"team-view-pol", "name":"team viewer", "members":["user:local:coolbean"], \
"statements": [{"role": "team-viewer"}]}' $TARGET_HOST/apis/iam/v2beta/policies

Read it back and observe it has the default * assigned. Here's one way to do that:

authQuery -o -2 policies id team-view-pol | jq

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.:

{
  "error": "error parsing policy \"team-view-pol3\": parse statement: project statements must include projects",
  "message": "error parsing policy \"team-view-pol3\": parse statement: project statements must include projects",
  "code": 3,
  "details": []
}

⛓️ Related Resources

✅ Checklist

  • Necessary tests added/updated?
  • Necessary docs added/updated?
  • Code actually executed?
  • Vetting performed (unit tests, lint, etc.)?

@msorens msorens added automate-auth auth-team anything that needs to be on the auth team board labels Jul 12, 2019
@msorens msorens self-assigned this Jul 12, 2019
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)
Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor

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() {
Copy link
Contributor Author

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) {
Copy link
Contributor Author

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)
Copy link
Contributor Author

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
Copy link
Contributor Author

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)
Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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")
}
Copy link
Contributor Author

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()) })
}
Copy link
Contributor Author

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!

@msorens msorens requested a review from a team July 12, 2019 23:35
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)

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(

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? 🙃

Copy link
Contributor Author

@msorens msorens Jul 13, 2019

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.

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() {
Copy link
Contributor

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:

Copy link
Contributor

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.

Copy link
Contributor

@bcmdarroch bcmdarroch left a 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)
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor

@srenatus srenatus left a 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.

@srenatus
Copy link
Contributor

@msorens msorens force-pushed the ms/empty-projects-fix branch from 0a3f7a6 to 27a798b Compare July 16, 2019 21:12
@srenatus srenatus force-pushed the ms/empty-projects-fix branch from 27a798b to 94b07f6 Compare July 17, 2019 07:27
@msorens msorens requested a review from a team as a code owner July 17, 2019 08:32
@@ -902,3 +922,8 @@ func (s *policyServer) setVersionForInterceptorSwitch(v api.Version) {
s.vChan <- v
}
}

func (s *policyServer) isBeta2p1() bool {
Copy link
Contributor

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.

@srenatus srenatus force-pushed the ms/empty-projects-fix branch 2 times, most recently from 344f98e to a2b9366 Compare July 17, 2019 11:16
@srenatus
Copy link
Contributor

Team: If you look at the tests changes, you'll note that for v2, this will now include "*" in the statements' bodies: a2-iam-v2-integration/iam_v2.

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 projects keys in the return messages... so, adding this doesn't seem too bad.

What I find confusing, however, is that only the statements' projects is set. 🤔

@srenatus
Copy link
Contributor

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...?

@srenatus srenatus dismissed their stale review July 17, 2019 12:14

Fine, let's do the dirty thing once more -- there's a card for tackling this version business already.

@srenatus srenatus force-pushed the ms/empty-projects-fix branch from 9a4ae3f to 7d659b2 Compare July 17, 2019 13:45
@srenatus
Copy link
Contributor

Still working on getting the added diagnostics test to fail properly, and then to fix it.

@srenatus srenatus self-assigned this Jul 17, 2019
@@ -0,0 +1,5 @@
BEGIN;

INSERT INTO iam_projects (id, name, type) VALUES ('*', 'Wildcard project', 'chef-managed');
Copy link

@blakestier blakestier Jul 17, 2019

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 . . .

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)

Copy link
Contributor

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! 💦

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Er, something is amiss 🤔

Copy link
Contributor

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"},

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? 😩

Copy link
Contributor

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"

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 😂

@blakestier blakestier force-pushed the ms/empty-projects-fix branch from 571ddd3 to 7d659b2 Compare July 17, 2019 17:08
@srenatus srenatus force-pushed the ms/empty-projects-fix branch from 94c8b22 to 83723bd Compare July 17, 2019 17:31
@blakestier blakestier force-pushed the ms/empty-projects-fix branch 2 times, most recently from ef084eb to c4a8e11 Compare July 17, 2019 19:21
@blakestier blakestier force-pushed the ms/empty-projects-fix branch 5 times, most recently from bc211ce to 9a48129 Compare July 17, 2019 23:50
@srenatus
Copy link
Contributor

srenatus commented Jul 18, 2019

@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 dev. So, our changes to the deployment-service (through diagnostics) aren't there when we need them. I've thus split them off into their own PR: #946. When that's merged, we can rebase this one, and see where we get with it.

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.

@srenatus srenatus force-pushed the ms/empty-projects-fix branch 2 times, most recently from 4e010e9 to 67875a4 Compare July 18, 2019 14:55
@blakestier blakestier force-pushed the ms/empty-projects-fix branch from 67875a4 to bc37cf8 Compare July 19, 2019 16:09
msorens and others added 11 commits July 19, 2019 09:17
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]>
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]>
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]>
Copy link
Contributor

@tylercloke tylercloke left a 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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

@blakestier blakestier merged commit 1e932f3 into master Jul 19, 2019
@blakestier blakestier deleted the ms/empty-projects-fix branch July 19, 2019 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth-team anything that needs to be on the auth team board automate-auth
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants