Extending the Policy Information Point (PIP) with an Attribute Value Provider
So far, all the authorization decisions you have created have relied on the request context. Either the Identity claims associated with the user performing the request or the values present in the request. The PIP allows the PDP to obtain additional information not present in the request context. The PIP obtains its values from Attribute Value Providers. For example, an environment attribute value provider could provide the time of day. In this part of the tutorial, you will develop an attribute value provider that can provide the max spend level for the users' department.
- Define an attribute in finance.alfa to represent the max purchase order value.
attribute PurchaseOrderLimit
{
id = "MaxPurchaseOrderValue"
category = resourceCat
type = double
}
- For the MaxPurchaseOrderValue attribute to have any values, you will need to define an attribute value provider that can deliver this attribute's values. There are numerous ways to create providers, but the simplest is to create a type that derives from
RecordAttributeValueProvider<>
. Create a folder in the root of the project called PolicyInformationPoint, and create a file inside the folder called FinanceDepartmentAttributeProvider.cs. - Now define a class that can return all the values we need from the Finance system. In this case, it's just one property. Decorate the property with a
PolicyAttributeValue
attribute to bind the property to the ALFA definition of the attribute.
public class FinanceDepartmentLimits
{
[PolicyAttributeValue(PolicyAttributeCategories.Resource, "MaxPurchaseOrderValue")]
public double? MaxPurchaseOrder { get; set; }
}
- The
FinanceDepartmentLimits
class represents the data to be bound to attributes. Now write the logic to populate the data; this takes the form of a class that extendsRecordAttributeValueProvider<FinanceDepartmentLimits>
. This attribute value provider will need to know the department of the user to obtain the current limit. Attribute value providers can make use of other providers if they need to, using the supplied attribute resolver (PIP).
public class FinanceDepartmentAttributeProvider : RecordAttributeValueProvider<FinanceDepartmentLimits>
{
private static readonly PolicyAttribute Department =
new PolicyAttribute("department",
PolicyValueType.String,
PolicyAttributeCategories.Subject);
protected override async Task<FinanceDepartmentLimits> GetRecordValue(IAttributeResolver attributeResolver)
{
// Retrieve the department from the evaluation context
IReadOnlyCollection<string> departments =
await attributeResolver.Resolve<string>(Department);
double purchaseOrderLimit = 0;
switch (departments.Single())
{
case "engineering": purchaseOrderLimit = 500;
break;
case "finance": purchaseOrderLimit = 2000;
break;
}
return new FinanceDepartmentLimits()
{
MaxPurchaseOrder = purchaseOrderLimit
};
}
}
- Now register the attribute value provider as part of the EnforcerBuilder chain in Startup.cs.
services
.AddEnforcer("AcmeCorp.Global", options => {
options.Licensee = licensee;
options.LicenseKey = licenseKey;
})
.AddFileSystemPolicyStore("policies")
.AddPolicyEnforcementPoint(o => o.Bias = PepBias.Deny)
.AddClaimsAttributeValueProvider(o => { })
.AddDefaultAdviceHandling()
.AddPolicyAttributeProvider<FinanceDepartmentAttributeProvider>();
- Finally, update the
RestrictCreation
rule to further to restrict the creation of purchase orders above the managers' department limit.
rule RestrictCreation
{
target clause Action == 'Create'
deny
condition not ((Subject.Role == 'manager' and PurchaseOrderTotal < PurchaseOrderLimit) or
(Subject.Role == 'employee' and PurchaseOrderTotal < 100 ))
}
- Re-run the application and verify that
- Alice can create purchase orders up to 500.
- Sally can create purchase orders up to 2000.
- Bob can only create purchase orders up to 100.