From c7b1f54ec4b468f74f2b619d647020e49fa17c46 Mon Sep 17 00:00:00 2001
From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>
Date: Wed, 3 Apr 2024 10:14:16 -0500
Subject: [PATCH] UI: Don't show Resultant-ACL banner when wildcard policy
present (#26233)
* Add wildcard calc helpers to permissions service with tests
* Check for wildcard access when calculating permissionsBanner
* Move resultant-acl banner within TokenExpireWarning so it's mutually exclusive with token expired banner
* fix permissions banner if statement
* Add margin to resultant-acl
* cleanup comments
---
ui/app/services/permissions.js | 47 +++++++-
ui/app/templates/vault/cluster.hbs | 8 +-
ui/tests/unit/services/permissions-test.js | 134 +++++++++++++++++++++
3 files changed, 185 insertions(+), 4 deletions(-)
diff --git a/ui/app/services/permissions.js b/ui/app/services/permissions.js
index 3e417d6d691d..c6896c727225 100644
--- a/ui/app/services/permissions.js
+++ b/ui/app/services/permissions.js
@@ -66,6 +66,27 @@ const API_PATHS_TO_ROUTE_PARAMS = {
It fetches a users' policy from the resultant-acl endpoint and stores their
allowed exact and glob paths as state. It also has methods for checking whether
a user has permission for a given path.
+ The data from the resultant-acl endpoint has the following shape:
+ {
+ exact_paths: {
+ [key: string]: {
+ capabilities: string[];
+ };
+ };
+ glob_paths: {
+ [key: string]: {
+ capabilities: string[];
+ };
+ };
+ root: boolean;
+ chroot_namespace?: string;
+ };
+ There are a couple nuances to be aware of about this response. When a
+ chroot_namespace is set, all of the paths in the response will be prefixed
+ with that namespace. Additionally, this endpoint is only added to the default
+ policy in the user's root namespace, so we make the call to the user's root
+ namespace (the namespace where the user's auth method is mounted) no matter
+ what the current namespace is.
*/
export default Service.extend({
@@ -75,7 +96,6 @@ export default Service.extend({
permissionsBanner: null,
chrootNamespace: null,
store: service(),
- auth: service(),
namespace: service(),
get baseNs() {
@@ -101,6 +121,27 @@ export default Service.extend({
}
}),
+ get wildcardPath() {
+ const ns = [sanitizePath(this.chrootNamespace), sanitizePath(this.namespace.userRootNamespace)].join('/');
+ // wildcard path comes back from root namespace as empty string,
+ // but within a namespace it's the namespace itself ending with a slash
+ return ns === '/' ? '' : `${sanitizePath(ns)}/`;
+ },
+
+ /**
+ * hasWildcardAccess checks if the user has a wildcard policy
+ * @param {object} globPaths key is path, value is object with capabilities
+ * @returns {boolean} whether the user's policy includes wildcard access to NS
+ */
+ hasWildcardAccess(globPaths = {}) {
+ // First check if the wildcard path is in the globPaths object
+ if (!Object.keys(globPaths).includes(this.wildcardPath)) return false;
+
+ // if so, make sure the current namespace is a child of the wildcard path
+ return this.namespace.path.startsWith(this.wildcardPath);
+ },
+
+ // This method is called to recalculate whether to show the permissionsBanner when the namespace changes
calcNsAccess() {
if (this.canViewAll) {
this.set('permissionsBanner', null);
@@ -108,7 +149,11 @@ export default Service.extend({
}
const namespace = this.baseNs;
const allowed =
+ // check if the user has wildcard access to the relative root namespace
+ this.hasWildcardAccess(this.globPaths) ||
+ // or if any of their glob paths start with the namespace
Object.keys(this.globPaths).any((k) => k.startsWith(namespace)) ||
+ // or if any of their exact paths start with the namespace
Object.keys(this.exactPaths).any((k) => k.startsWith(namespace));
this.set('permissionsBanner', allowed ? null : PERMISSIONS_BANNER_STATES.noAccess);
},
diff --git a/ui/app/templates/vault/cluster.hbs b/ui/app/templates/vault/cluster.hbs
index 026479172947..bc7993df30bc 100644
--- a/ui/app/templates/vault/cluster.hbs
+++ b/ui/app/templates/vault/cluster.hbs
@@ -70,9 +70,6 @@
@autoloaded={{eq this.activeCluster.licenseState "autoloaded"}}
/>
{{/if}}
- {{#if this.permissionBanner}}
-