-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* test: write specifications for nearestPath computation * ui: write logic for getting all paths * ui: nearestPathMatching algorithm * test: nearestPathMatching algorithm test * ui: handle namespace filtering for capabilities check (#13475) * ui: add namespace handling * refact: add logical OR operator to handle unstructured object. * ui: acceptance test for create flow in secure variables (#13500) * test: write happy path test for creating variable * refact: add missing data-test attributes * test: sad path for disabled button * fix: move comment in file * test: acceptance test for editing a variable (#13529) * refact: add data-test variable * test: happy path and sad path for edit flow * refact: update test language to say disabled * ui: glob matching algorithm (#13533) * ui: compute length difference (#13542) * ui: compute length difference * refact: use glob matching and sorting algos in `nearestMatchingPath` (#13544) * refact: use const in compute * ui: smallest difference logic * refact: use glob matching and sorting algo in _nearestPathPath helper * ui: add can edit to variable capabilities (#13545) * ui: create edit capabilities getter * ui: add ember-can check for edit button * refact: update test to mock edit capabilities in policy * fix: remove unused var * Edit capabilities for variables depend on Create Co-authored-by: Phil Renaud <[email protected]> Co-authored-by: Phil Renaud <[email protected]> Co-authored-by: Phil Renaud <[email protected]> * refact: update token factory (#13596) * refact: update rulesJSON in token factory to reflect schema update * refact: update capability names (#13597) * refact: update rules to match rulesJSON * refact: update create to write * ui: add `canDestroy` permissions (#13598) * refact: update rulesJSON in token factory to reflect schema update * refact: update rules to match rulesJSON * refact: update create to write * ui: add canDestroy capability * test: unit test for canDestroy * ui: add permission check to template * test: acceptance test for delete flow * refact: update test to use correct capability name * refact: update tests to reflect rulesJSON schema change * ui: update path matching logic to account for schema change (#13605) * refact: update path matching logic * refact: update tests to reflect rulesJSON change Co-authored-by: Phil Renaud <[email protected]>
- Loading branch information
1 parent
5de37c8
commit 187ce04
Showing
9 changed files
with
1,117 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,11 @@ import { computed, get } from '@ember/object'; | |
import { or } from '@ember/object/computed'; | ||
import AbstractAbility from './abstract'; | ||
|
||
const WILDCARD_GLOB = '*'; | ||
const WILDCARD_PATTERN = '/'; | ||
const GLOBAL_FLAG = 'g'; | ||
const WILDCARD_PATTERN_REGEX = new RegExp(WILDCARD_PATTERN, GLOBAL_FLAG); | ||
|
||
export default class Variable extends AbstractAbility { | ||
// Pass in a namespace to `can` or `cannot` calls to override | ||
// https://github.com/minutebase/ember-can#additional-attributes | ||
|
@@ -22,9 +27,16 @@ export default class Variable extends AbstractAbility { | |
@or( | ||
'bypassAuthorization', | ||
'selfTokenIsManagement', | ||
'policiesSupportVariableCreation' | ||
'policiesSupportVariableWriting' | ||
) | ||
canWrite; | ||
|
||
@or( | ||
'bypassAuthorization', | ||
'selfTokenIsManagement', | ||
'policiesSupportVariableDestroy' | ||
) | ||
canCreate; | ||
canDestroy; | ||
|
||
@computed('[email protected]') | ||
get policiesSupportVariableView() { | ||
|
@@ -33,12 +45,143 @@ export default class Variable extends AbstractAbility { | |
}); | ||
} | ||
|
||
@computed('[email protected]', 'path') | ||
get policiesSupportVariableCreation() { | ||
return this.rulesForNamespace.some((rules) => { | ||
const keyName = `SecureVariables.Path "${this.path}".Capabilities`; | ||
const capabilities = get(rules, keyName) || []; | ||
return capabilities.includes('create'); | ||
}); | ||
@computed('path', 'allPaths') | ||
get policiesSupportVariableWriting() { | ||
const matchingPath = this._nearestMatchingPath(this.path); | ||
return this.allPaths | ||
.find((path) => path.name === matchingPath) | ||
?.capabilities?.includes('write'); | ||
} | ||
|
||
@computed('path', 'allPaths') | ||
get policiesSupportVariableDestroy() { | ||
const matchingPath = this._nearestMatchingPath(this.path); | ||
return this.allPaths | ||
.find((path) => path.name === matchingPath) | ||
?.capabilities?.includes('destroy'); | ||
} | ||
|
||
@computed('token.selfTokenPolicies.[]', '_namespace') | ||
get allPaths() { | ||
return (get(this, 'token.selfTokenPolicies') || []) | ||
.toArray() | ||
.reduce((paths, policy) => { | ||
const matchingNamespace = this._findMatchingNamespace( | ||
get(policy, 'rulesJSON.Namespaces') || [], | ||
this._namespace | ||
); | ||
|
||
const variables = (get(policy, 'rulesJSON.Namespaces') || []).find( | ||
(namespace) => namespace.Name === matchingNamespace | ||
)?.SecureVariables; | ||
|
||
const pathNames = variables?.Paths?.map((path) => ({ | ||
name: path.PathSpec, | ||
capabilities: path.Capabilities, | ||
})); | ||
|
||
if (pathNames) { | ||
paths = [...paths, ...pathNames]; | ||
} | ||
|
||
return paths; | ||
}, []); | ||
} | ||
|
||
_formatMatchingPathRegEx(path, wildCardPlacement = 'end') { | ||
const replacer = () => '\\/'; | ||
if (wildCardPlacement === 'end') { | ||
const trimmedPath = path.slice(0, path.length - 1); | ||
const pattern = trimmedPath.replace(WILDCARD_PATTERN_REGEX, replacer); | ||
return pattern; | ||
} else { | ||
const trimmedPath = path.slice(1, path.length); | ||
const pattern = trimmedPath.replace(WILDCARD_PATTERN_REGEX, replacer); | ||
return pattern; | ||
} | ||
} | ||
|
||
_computeAllMatchingPaths(pathNames, path) { | ||
const matches = []; | ||
|
||
for (const pathName of pathNames) { | ||
if (this._doesMatchPattern(pathName, path)) matches.push(pathName); | ||
} | ||
|
||
return matches; | ||
} | ||
|
||
_nearestMatchingPath(path) { | ||
const pathNames = this.allPaths.map((path) => path.name); | ||
|
||
if (pathNames.includes(path)) { | ||
return path; | ||
} | ||
|
||
const allMatchingPaths = this._computeAllMatchingPaths(pathNames, path); | ||
|
||
if (!allMatchingPaths.length) return WILDCARD_GLOB; | ||
|
||
return this._smallestDifference(allMatchingPaths, path); | ||
} | ||
|
||
_doesMatchPattern(pattern, path) { | ||
const parts = pattern?.split(WILDCARD_GLOB); | ||
const hasLeadingGlob = pattern?.startsWith(WILDCARD_GLOB); | ||
const hasTrailingGlob = pattern?.endsWith(WILDCARD_GLOB); | ||
const lastPartOfPattern = parts[parts.length - 1]; | ||
const isPatternWithoutGlob = parts.length === 1 && !hasLeadingGlob; | ||
|
||
if (!pattern || !path || isPatternWithoutGlob) { | ||
return pattern === path; | ||
} | ||
|
||
if (pattern === WILDCARD_GLOB) { | ||
return true; | ||
} | ||
|
||
let subPathToMatchOn = path; | ||
for (let i = 0; i < parts.length; i++) { | ||
const part = parts[i]; | ||
const idx = subPathToMatchOn?.indexOf(part); | ||
const doesPathIncludeSubPattern = idx > -1; | ||
const doesMatchOnFirstSubpattern = idx === 0; | ||
|
||
if (i === 0 && !hasLeadingGlob && !doesMatchOnFirstSubpattern) { | ||
return false; | ||
} | ||
|
||
if (!doesPathIncludeSubPattern) { | ||
return false; | ||
} | ||
|
||
subPathToMatchOn = subPathToMatchOn.slice(0, idx + path.length); | ||
} | ||
|
||
return hasTrailingGlob || path.endsWith(lastPartOfPattern); | ||
} | ||
|
||
_computeLengthDiff(pattern, path) { | ||
const countGlobsInPattern = pattern | ||
?.split('') | ||
.filter((el) => el === WILDCARD_GLOB).length; | ||
|
||
return path?.length - pattern?.length + countGlobsInPattern; | ||
} | ||
|
||
_smallestDifference(matches, path) { | ||
const sortingCallBack = (patternA, patternB) => | ||
this._computeLengthDiff(patternA, path) - | ||
this._computeLengthDiff(patternB, path); | ||
|
||
const sortedMatches = matches?.sort(sortingCallBack); | ||
const isTie = | ||
this._computeLengthDiff(sortedMatches[0], path) === | ||
this._computeLengthDiff(sortedMatches[1], path); | ||
const doesFirstMatchHaveLeadingGlob = sortedMatches[0][0] === WILDCARD_GLOB; | ||
|
||
return isTie && doesFirstMatchHaveLeadingGlob | ||
? sortedMatches[1] | ||
: sortedMatches[0]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.