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

UI Hide Secrets Sync from nav if not on license and/or no policy permissions #27262

Merged
merged 24 commits into from
Jun 11, 2024

Conversation

Monkeychip
Copy link
Contributor

@Monkeychip Monkeychip commented May 29, 2024

Adding Do not merge label so this is target for 1.17.1 and does not get into GA.

  • ent test pass

This PR hides Secrets Sync from the sidebar navigation under the following circumstances. The decisions was made to remove the upsell badges because showing Secrets Sync to users who do not have this feature or have the feature but do not have any access to the sys/sync endpoint is an anti-pattern to how the UI currently handles showing/hiding features.

Notes:

  • I do not do any type of redirect or navigation prevention if a user manually finds the Secrets Sync page.
  • I do very little to client counts: I change the name of a getter and use the same flag service getter to show or hide the link to Secrets Sync overview.
  • I do not address the situation where a user—with policy permissions—still cannot activate Secrets Sync from within a namespace. That will be addressed in another PR and targeted for 1.17.1.
  • The permissions paths I gathered from scanning through the sync api.

These changes do the following:

  1. Hides Secrets Sync nav from all OSS users.
    image

  2. Displays only one upsell badge for HVD managed clusters. We cannot tell which tier they are on. In the future this will likely change.
    image

  3. If you're an Enterprise user without Secrets Sync on your license, you will not see it in the sidebar nav.
    image

  4. If you're an Enterprise user with Secrets Sync on your license and it's NOT activated, you will see the sidebar nav (regardless of your policy permissions).

# user bob with the following policy
path "sys/sync/*" {
  capabilities = [ "deny" ]
}

path "sys/activation-flags/secrets-sync/*" {
  capabilities = [ "deny" ]
}

image

  1. If you're an Enterprise user with Secrets Sync on your license and it IS activated, you will only see Secrets Sync if you have access to sys/sync endpoints.

Below is a user Bob with the ability to read on sys/sync/destinations only, and he can see it.

Below is a user Maria with the default policy (no access to sys/sync), she can not see it.
image

@Monkeychip Monkeychip added this to the 1.17.0 milestone May 29, 2024
@github-actions github-actions bot added the hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed label May 29, 2024
Copy link

github-actions bot commented May 29, 2024

CI Results:
All Go tests succeeded! ✅

@@ -43,7 +43,7 @@ export default class SyncActivationModal extends Component<Args> {
.adapterFor('application')
.ajax('/v1/sys/activation-flags/secrets-sync/activate', 'POST', { namespace });
// must refresh and not transition because transition does not refresh the model from within a namespace
yield this.router.refresh();
yield this.router.refresh('vault.cluster');
Copy link
Contributor

@andaley andaley May 29, 2024

Choose a reason for hiding this comment

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

you can probably take this or leave it, but i tried explicitly passing in the parent route that we want refreshed here to see if it'd help us assert that the get activated features was successfully re-fetched. i tried this based on the docs i read here:
Screenshot 2024-05-29 at 3 39 22 PM

doing this works fine in the UI, and after adding a bunch of console.log statements i can see even in the test output that _/activation-flags is fetched after a POST, but i can't get the assertions working how i'd like. because we have these assertions that effectively test that the UI updates once the feature is activated i think we're safe skipping the extra assertions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The list here came from me scanning through the sync API. We should test lots of policies to make sure I've gotten all the options.

@Monkeychip Monkeychip marked this pull request as ready for review May 30, 2024 17:30
@Monkeychip Monkeychip requested a review from a team as a code owner May 30, 2024 17:30
Copy link

github-actions bot commented May 30, 2024

Build Results:
All builds succeeded! ✅

Copy link
Contributor

@andaley andaley left a comment

Choose a reason for hiding this comment

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

code here looks good! coupla small comments and then i'll circle back to this to do a thorough QA. nice job!

ui/app/components/clients/page/counts.hbs Outdated Show resolved Hide resolved
ui/app/services/permissions.js Show resolved Hide resolved
Comment on lines 28 to 29
syncHandlers(this.server);
await authPage.login();
Copy link
Contributor

Choose a reason for hiding this comment

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

a note for other code reviewers: Angel and i moved authPage.login out of the top-level module and into these individual ones because fetchActivatedFeatures is called at the vault.cluster route, which happens before the user is authenticated. as a result auth.login was executing and calling fetchActivatedFeatures before our mirage overrides like those on lines 74-95 had the chance to intercept the request.

Comment on lines +191 to +193
// the activate endpoint is located at a different path than sys/sync.
// the expected UX experience: if the feature is not activated, regardless of permissions
// the user should see the landing page and a banner that tells them to either have an admin activate the feature or activate it themselves
Copy link
Contributor

Choose a reason for hiding this comment

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

very helpful comment! 👏

// Response could change between user sessions.
// Fire off endpoint without checking if activated features are already set.
if (this.version.isCommunity) return;
Copy link
Contributor

Choose a reason for hiding this comment

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

Edge case question - if a customer downgrades from enterprise, are their activated flags preserved? (Not necessary for this PR, mostly just curious)

Copy link
Contributor Author

@Monkeychip Monkeychip May 31, 2024

Choose a reason for hiding this comment

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

I think I've asked Robert this, and more or less no. I think because there's a conditional wrapping that endpoint which is checking for enterprise, so it would return nothing. That being said I haven't tested this workflow.

Comment on lines +98 to +99
if (!isEnterprise) return false;
if (isHvdManaged) return true;
Copy link
Contributor

@hellobontempo hellobontempo May 30, 2024

Choose a reason for hiding this comment

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

Are HVD clusters enterprise? ~ til ~ I was going to suggest rearranging this order, but disregard if HVD clusters are enterprise

Copy link
Contributor

Choose a reason for hiding this comment

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

they sure are 🙂

@@ -63,6 +63,10 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, {
'Cannot use VAULT_CLOUD_ADMIN_NAMESPACE flag with non-enterprise Vault version',
!(managedRoot && this.version.isCommunity)
);

// activatedFlags are called this high in routing to return a response used to show/hide Secrets sync on sidebar nav.
Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for this comment!

@@ -86,4 +88,21 @@ export default class flagsService extends Service {
this.secretsSyncActivatePath.get('canUpdate') !== false
);
}

get showSecretsSync() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you explain your reasoning for putting this getter here? Since it's compiling logic from flags, permissions and versions services for just secret sync, it feels a little arbitrary living in the flag service, which I understood to be just for returning info about cluster flags.

Though looking at the other getter here canActivateSecretsSync it seems like this flags service is also sort of a secrets sync permissions service 😅

I realize we're limited on time for this PR, but want to suggest that a future improvement is maybe separating or clarifying concerns here. Perhaps that means having another service (auth service?) responsible for managing user state related logic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noelle and I chatted about this. Because we're using it in more than one places, and the concept is to keep access to this route consistent in both clients and sidebar nav we opted to leave it in the service.

But yes agree, flags (similar to what has happened to version service) is growing past it's original scope.

await runCmd(`write sys/namespaces/admin -f`, false);
await authPage.loginNs('admin');
await runCmd(`write sys/namespaces/foo -f`, false);
await authPage.loginNs('admin/foo');
Copy link
Contributor

@hellobontempo hellobontempo May 30, 2024

Choose a reason for hiding this comment

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

Is the full test suite passing locally? Typically with namespace tests we have to manually clear the auth form namespace query params otherwise they stick for subsequent (unrelated) test runs.

https://github.com/hashicorp/vault/blob/main/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js#L422

Copy link
Contributor Author

@Monkeychip Monkeychip May 31, 2024

Choose a reason for hiding this comment

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

I'm really struggling with replication and oidc tests running enterprise locally, but chunking it out between these two suites everything passes.

Also these tests were not changed/added just moved because we deleted things

@@ -84,31 +84,6 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
});
});

test('it should render badge for promotional links on community version', async function (assert) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have a test that it renders the HVD plus badge?

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.version.type = 'community';

this.server.get('sys/activation-flags', () => {
assert.true(false, 'activation-flags is not called');
Copy link
Contributor

Choose a reason for hiding this comment

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

If we don't want this called, could we have this endpoint throw an error? This can make it a little clearer that the assertion inside that mirage request should not actually be hit

this.server.get(
'sys/internal/counters/activity',
() => new Error('uh oh! a request was made to sys/internal/counters/activity')
);

    this.server.get(
        'sys/activation-flags',
        () => new Error('uh oh! a request was made to sys/activation-flags, this should not happen for community versions')
      );

Copy link
Contributor

Choose a reason for hiding this comment

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

👆 love that idea!

Copy link
Contributor

Choose a reason for hiding this comment

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

@noelledaley You gave it to me on one of my recent PRs! 🎉 😄

@@ -49,6 +49,12 @@ const API_PATHS = {
settings: {
customMessages: 'sys/config/ui/custom-messages',
},
sync: {
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 still learning out this service works...but do you know why just having

sync: 'sys/sync/',

which ends in a / isn't sufficient here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nope! 🙃 This is what I pinged Chelsea about in the dev channel. The logic of comparison is backwards from what you'd think they be comparing. I'll ping you on that thread which has links, etc. But it's also something that I can more easily clarify in standup.

@Monkeychip Monkeychip removed this from the 1.17.0 milestone May 31, 2024
@Monkeychip Monkeychip added this to the 1.17.1 milestone Jun 3, 2024
* routes: add redirect if user does not have access to sync

* tests: verify redirect on sync overview page happens

* tests: organize tests modules to ensure enterprise is explicitly set up
@@ -13,13 +13,8 @@ export default class SyncSecretsRoute extends Route {
@service declare readonly router: RouterService;
@service declare readonly flags: FlagService;

beforeModel() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Commented in slack about refresh ramifications removing this fetch to only happen in the cluster.js route

Copy link
Contributor

@hellobontempo hellobontempo left a comment

Choose a reason for hiding this comment

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

Nice work! Just the question about fetching the activation flags

@Monkeychip Monkeychip merged commit 927da85 into main Jun 11, 2024
34 checks passed
@Monkeychip Monkeychip deleted the ui/VAULT-27608/hide-secrets-sync-when-no-access branch June 11, 2024 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed ui
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants