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

chore: release #114

Merged
merged 10 commits into from
Sep 23, 2024
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions packages/acs-client/src/acs/authz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const createSubjectTarget = (subject: DeepPartial<Subject>): Attribute[]
attributes: []
}];
}
let flattened: Attribute[] = [
const flattened: Attribute[] = [
{
id: urns.subjectID,
value: subject.id,
Expand All @@ -81,13 +81,13 @@ export const createSubjectTarget = (subject: DeepPartial<Subject>): Attribute[]
];

if (subject.scope) {
flattened = flattened.concat([
flattened.push(
{
id: urns.roleScopingInstance,
value: subject.scope,
attributes: []
}
]);
);
}
return flattened;
};
Expand Down
13 changes: 11 additions & 2 deletions packages/acs-client/src/acs/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,17 @@ export const accessRequest = async (

// create filters to enforce applicable policies and custom query / args if applicable
// TODO check and modify this
const resourceFilters = await createResourceFilterMap(resource, policySetResponse,
ctx.resources, action, subClone, subjectID, authzEnforced, targetScope, database);
const resourceFilters = await createResourceFilterMap(
resource,
policySetResponse,
ctx.resources,
action,
subClone,
subjectID,
authzEnforced,
targetScope,
database
);

if ((resourceFilters as DecisionResponse).decision) {
return resourceFilters as DecisionResponse;
Expand Down
77 changes: 42 additions & 35 deletions packages/acs-client/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ const checkTargetScopeExists = (hrScopes: HierarchicalScope[], targetScope: stri
});
};

const checkSubjectMatch = (user: ResolvedSubject, ruleSubjectAttributes: Attribute[],
reducedUserScope?: string[]): boolean => {
const checkSubjectMatch = (
user: ResolvedSubject,
ruleSubjectAttributes: Attribute[],
reducedUserScope?: string[]
): boolean => {
// 1) Iterate through ruleSubjectAttributes and check if the roleScopingEntity URN and
// role URN exists
// 2) Now check if the subject rule role value matches with one of the users ctx role_associations
Expand Down Expand Up @@ -93,27 +96,28 @@ const checkSubjectMatch = (user: ResolvedSubject, ruleSubjectAttributes: Attribu
}

if (ruleRoleValue && ruleRoleScopeEntityName) {
const matchingRoleScopedInstance: string[] = user?.role_associations?.filter((roleObj) => {
return roleObj?.attributes?.some((roleAttributeObj) => {
if (roleAttributeObj?.id === urns?.roleScopingEntity
&& roleAttributeObj?.value === ruleRoleScopeEntityName
) {
return roleAttributeObj?.attributes?.some((roleScopingInstanceObj) => {
if (roleScopingInstanceObj?.id === urns?.roleScopingInstance) {
return roleScopingInstanceObj?.value;
}
});
}
});
}).flatMap((roleObj) => roleObj?.attributes?.map(
roleObjAttr => roleObjAttr?.attributes?.map((attrInstObj) => attrInstObj.value)[0]
));
const matchingRoleScopedInstance: string[] = user?.role_associations?.flatMap(
ra => ra.attributes
).filter(
a => a?.id === urns?.roleScopingEntity
&& a?.value === ruleRoleScopeEntityName
).flatMap(
a => a?.attributes
).filter(
aa => aa?.id === urns?.roleScopingInstance
).map(
aa => aa.value
);

logger.debug('Role scoped instances for matching entity', { id: user?.id, ruleRoleScopeEntityName, matchingRoleScopedInstance });
// validate HR scope root ID contains the role scope instances
const hrScopeExist = user?.hierarchical_scopes?.some((hrScope) => matchingRoleScopedInstance.includes(hrScope.id));
logger.debug('HR Scopes exist', { hrScopeExist });
if (!hrScopeExist) {
logger.info('Hierarchial scopes for matching role does not exist', { role: ruleRoleValue, instances: matchingRoleScopedInstance });
logger.info('Hierarchial scopes for matching role does not exist', {
role: ruleRoleValue,
hrScopes: user?.hierarchical_scopes,
instances: matchingRoleScopedInstance
});
return false;
} else if (hrScopeExist && user?.scope) {
logger.debug('Target scope set and HR scopes exist, validate target scope from HR scopes', { targetScope: user?.scope });
Expand All @@ -127,10 +131,13 @@ const checkSubjectMatch = (user: ResolvedSubject, ruleSubjectAttributes: Attribu
// HR scope match exist but user has not provided scope so still a match is considered
logger.debug('Target scope not provided using full HR tree for matched role', { role: ruleRoleValue });
// if no scope is provided then use the complete HR tree for user scopes
user?.hierarchical_scopes?.filter((hrScope) => matchingRoleScopedInstance?.includes(hrScope?.id) && hrScope?.role === ruleRoleValue).forEach((eachHRScope) => {
user?.hierarchical_scopes?.filter(
(hrScope) => matchingRoleScopedInstance?.includes(hrScope?.id)
&& hrScope?.role === ruleRoleValue
).forEach((eachHRScope) => {
reduceUserScope(eachHRScope, reducedUserScope, hierarchicalRoleScopingCheck);
});
return hrScopeExist;
return reducedUserScope?.length > 0;
}
} else if (ruleRoleValue) {
return user?.role_associations?.some(
Expand Down Expand Up @@ -191,7 +198,7 @@ const buildQueryFromTarget = (
try {
filterId = validateCondition(condition, request);
// special filter added to filter user read for his own entity
if (typeof filterId === 'boolean') {
if (filterId === true) {
return;
} else if (typeof filterId === 'string') {
if (filterId && !scopingUpdated) {
Expand Down Expand Up @@ -228,14 +235,7 @@ const buildQueryFromTarget = (
filter.push(filterId);
}
}
else if (filterId && !scopingUpdated) {
ruleCondition = true;
filter.push({
field: 'id',
operation: Filter_Operation.eq,
value: filterId
});
} else {
else {
return;
}
} catch (err) {
Expand All @@ -244,12 +244,13 @@ const buildQueryFromTarget = (
return;
}
}
const scopingAttribute = _.find(subjects, (attribute: Attribute) =>
attribute.id == urns.roleScopingEntity);
const scopingAttribute = subjects?.find(
(attribute: Attribute) => attribute.id == urns.roleScopingEntity
);
if (!!scopingAttribute && effect == Effect.PERMIT && database === 'arangoDB' && !ruleCondition) { // note: there is currently no query to exclude scopes
// userTotalScope is an array accumulated scopes for each rule
query['scope'] = {
custom_query: 'filterByOwnership',
custom_query: cfg.get('authorization:custom_query_name') ?? 'filterByOwnership',
custom_arguments: {
// value: Buffer.from(JSON.stringify({
entity: scopingAttribute.value,
Expand Down Expand Up @@ -354,8 +355,8 @@ export const buildFilterPermissions = async (
}
}
else {
subject.hierarchical_scopes ??=[];
subject.role_associations ??=[];
subject.hierarchical_scopes ??= [];
subject.role_associations ??= [];
}

const urns = cfg.get('authorization:urns');
Expand Down Expand Up @@ -422,6 +423,12 @@ export const buildFilterPermissions = async (
}
if (!_.isEmpty(filterPermissions)) {
policyFiltersArr.push(filterPermissions);
// if reducedUserScope is empty - no filters are applied further
// as this is a rule without scoping and should override the filters
// from other Rules which have scoping entity
if (_.isEmpty(reducedUserScope) && rule.effect === effect) {
return { filters: [] } as QueryArguments;
}
}
}
policyEffects.push(effect);
Expand Down
Loading
Loading