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: Don't show Resultant-ACL banner when wildcard policy present #26233

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion ui/app/services/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -91,7 +97,6 @@ export default class PermissionsService extends Service {
@tracked permissionsBanner = null;
@tracked chrootNamespace = null;
@service store;
@service auth;
@service namespace;

get baseNs() {
Expand All @@ -117,14 +122,39 @@ 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;
return;
}
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;
}
Expand Down
8 changes: 5 additions & 3 deletions ui/app/templates/vault/cluster.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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|}}
Expand All @@ -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}}
Expand Down
134 changes: 134 additions & 0 deletions ui/tests/unit/services/permissions-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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