mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-29 14:41:09 +01:00
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
This commit is contained in:
parent
dbe6e4ee2d
commit
f8f40c3d16
@ -82,6 +82,12 @@ const API_PATHS_TO_ROUTE_PARAMS = {
|
||||
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 class PermissionsService extends Service {
|
||||
@ -91,7 +97,6 @@ export default class PermissionsService extends Service {
|
||||
@tracked permissionsBanner = null;
|
||||
@tracked chrootNamespace = null;
|
||||
@service store;
|
||||
@service auth;
|
||||
@service namespace;
|
||||
|
||||
get baseNs() {
|
||||
@ -117,6 +122,27 @@ export default class PermissionsService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
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.permissionsBanner = null;
|
||||
@ -124,7 +150,11 @@ export default class PermissionsService extends Service {
|
||||
}
|
||||
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.permissionsBanner = allowed ? null : PERMISSIONS_BANNER_STATES.noAccess;
|
||||
}
|
||||
|
||||
@ -70,9 +70,6 @@
|
||||
@autoloaded={{eq this.activeCluster.licenseState "autoloaded"}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#if this.permissionBanner}}
|
||||
<ResultantAclBanner @isEnterprise={{this.activeCluster.version.isEnterprise}} @failType={{this.permissionBanner}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="global-flash">
|
||||
{{#each this.flashMessages.queue as |flash|}}
|
||||
@ -84,6 +81,11 @@
|
||||
|
||||
{{#if this.auth.isActiveSession}}
|
||||
<TokenExpireWarning @expirationDate={{this.auth.tokenExpirationDate}} @allowingExpiration={{this.auth.allowExpiration}}>
|
||||
{{#if this.permissionBanner}}
|
||||
<div class="has-top-margin-m">
|
||||
<ResultantAclBanner @isEnterprise={{this.activeCluster.version.isEnterprise}} @failType={{this.permissionBanner}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
{{outlet}}
|
||||
</TokenExpireWarning>
|
||||
{{else}}
|
||||
|
||||
@ -250,4 +250,138 @@ module('Unit | Service | permissions', function (hooks) {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
module('wildcardPath calculates correctly', function () {
|
||||
[
|
||||
{
|
||||
scenario: 'no user root or chroot',
|
||||
userRoot: '',
|
||||
chroot: null,
|
||||
expectedPath: '',
|
||||
},
|
||||
{
|
||||
scenario: 'user root = child ns and no chroot',
|
||||
userRoot: 'bar',
|
||||
chroot: null,
|
||||
expectedPath: 'bar/',
|
||||
},
|
||||
{
|
||||
scenario: 'user root = child ns and chroot set',
|
||||
userRoot: 'bar',
|
||||
chroot: 'admin/',
|
||||
expectedPath: 'admin/bar/',
|
||||
},
|
||||
{
|
||||
scenario: 'no user root and chroot set',
|
||||
userRoot: '',
|
||||
chroot: 'admin/',
|
||||
expectedPath: 'admin/',
|
||||
},
|
||||
].forEach((testCase) => {
|
||||
test(`when ${testCase.scenario}`, function (assert) {
|
||||
const namespaceService = Service.extend({
|
||||
userRootNamespace: testCase.userRoot,
|
||||
path: 'current/path/does/not/matter',
|
||||
});
|
||||
this.owner.register('service:namespace', namespaceService);
|
||||
this.service.set('chrootNamespace', testCase.chroot);
|
||||
assert.strictEqual(this.service.wildcardPath, testCase.expectedPath);
|
||||
});
|
||||
});
|
||||
test('when user root =child ns and chroot set', function (assert) {
|
||||
const namespaceService = Service.extend({
|
||||
path: 'bar/baz',
|
||||
userRootNamespace: 'bar',
|
||||
});
|
||||
this.owner.register('service:namespace', namespaceService);
|
||||
this.service.set('chrootNamespace', 'admin/');
|
||||
assert.strictEqual(this.service.wildcardPath, 'admin/bar/');
|
||||
});
|
||||
});
|
||||
|
||||
module('hasWildcardAccess calculates correctly', function () {
|
||||
// The resultant-acl endpoint returns paths with chroot and
|
||||
// relative root prefixed on all paths.
|
||||
[
|
||||
{
|
||||
scenario: 'when root wildcard in root namespace',
|
||||
chroot: null,
|
||||
userRoot: '',
|
||||
currentNs: 'foo/bar',
|
||||
globs: {
|
||||
'': { capabilities: ['read'] },
|
||||
},
|
||||
expectedAccess: true,
|
||||
},
|
||||
{
|
||||
scenario: 'when root wildcard in chroot ns',
|
||||
chroot: 'admin/',
|
||||
userRoot: '',
|
||||
currentNs: 'admin/child',
|
||||
globs: {
|
||||
'admin/': { capabilities: ['read'] },
|
||||
},
|
||||
expectedAccess: true,
|
||||
},
|
||||
{
|
||||
scenario: 'when namespace wildcard in child ns',
|
||||
chroot: null,
|
||||
userRoot: 'bar',
|
||||
currentNs: 'bar/baz',
|
||||
globs: {
|
||||
'bar/': { capabilities: ['read'] },
|
||||
},
|
||||
expectedAccess: true,
|
||||
},
|
||||
{
|
||||
scenario: 'when namespace wildcard in child ns & chroot',
|
||||
chroot: 'foo/',
|
||||
userRoot: 'bar',
|
||||
currentNs: 'foo/bar/baz',
|
||||
globs: {
|
||||
'foo/bar/': { capabilities: ['read'] },
|
||||
},
|
||||
expectedAccess: true,
|
||||
},
|
||||
{
|
||||
scenario: 'when namespace wildcard in different ns with chroot & user root',
|
||||
chroot: 'foo/',
|
||||
userRoot: 'bar',
|
||||
currentNs: 'foo/bing',
|
||||
globs: {
|
||||
'foo/bar/': { capabilities: ['read'] },
|
||||
},
|
||||
expectedAccess: false,
|
||||
},
|
||||
{
|
||||
scenario: 'when namespace wildcard in different ns without chroot',
|
||||
chroot: null,
|
||||
userRoot: 'bar',
|
||||
currentNs: 'foo/bing',
|
||||
globs: {
|
||||
'bar/': { capabilities: ['read'] },
|
||||
},
|
||||
expectedAccess: false,
|
||||
},
|
||||
{
|
||||
scenario: 'when globs is empty',
|
||||
chroot: 'foo/',
|
||||
userRoot: 'bar',
|
||||
currentNs: 'foo/bing',
|
||||
globs: {},
|
||||
expectedAccess: false,
|
||||
},
|
||||
].forEach((testCase) => {
|
||||
test(`when ${testCase.scenario}`, function (assert) {
|
||||
const namespaceService = Service.extend({
|
||||
path: testCase.currentNs,
|
||||
userRootNamespace: testCase.userRoot,
|
||||
});
|
||||
this.owner.register('service:namespace', namespaceService);
|
||||
this.service.set('chrootNamespace', testCase.chroot);
|
||||
const result = this.service.hasWildcardAccess(testCase.globs);
|
||||
assert.strictEqual(result, testCase.expectedAccess);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user