Complex Authorization using XACML

1.5k views Asked by At

My company is looking to implement a centralized security service, and it seems like a popular standard for that is XACML. I have a complex authorization scenario and I've been having trouble figuring out how it could be defined using attributes for XACML policies.

The system I'm working on has several pieces relevant to this authentication scenario:

  • Users create projects to organize their work. Each project has a list of team members and viewers (users or groups who can view the project but not modify it).
  • Within these projects, users create recipes to describe how something should be manufactured.
  • Users make requests for these recipes to be manufactured by another group.

In the case where a user wants to view the recipe for a particular item, any of the following must be true:

  • The user must be the owner of the recipe (the person who wrote it).
  • The user must be a team member on the project where the recipe was created. (either directly or via group membership)
  • The user must be a member of a group manufacturing the recipe. (They need to see the recipe to manufacture it.)
  • The user must be a member of a group that has manufactured the recipe within the past two weeks. (i.e., after completing a request to manufacture the recipe, they can continue to view the recipe for two weeks to correct any problems.)
  • The user must be an administrator.

With those rules, it seems like the attributes needed to determine if a user can view a recipe include:

  • The user
  • The user's group membership (for project access, manufacturing group, or administrator access)
  • The project team members and viewers
  • Manufacturing requests for the recipe

Questions:

  • How would a PIP gather this information? Directly from a database? Via service calls to the system that stores this information?
  • How would this information be represented for XACML (in general)? Most of the examples I've seen use simple models that don't use collections of data (like a list of manufacturing requests); simply attributes directly on the object being accessed. Would the data be flattened somehow, like "isBeingManufacturedByUserGroup"? (and if so, how would the value for that attribute be determined?)
  • How would the policies be structured to evaluate these rules?
  • Are there any alternatives for handling this sort of authorization (besides XACML)? Would OAuth 2.0 be able to handle this sort of problem any easier?
1

There are 1 answers

6
David Brossard On BEST ANSWER

(disclaimer - I work for Axiomatics, the leading implementation of XACML and ABAC)

Great question. I've actually put together an authorization policy lifecycle which walks users through the process of gathering requirements, identifying attributes, and implementing policies.

Authorization Policy Lifecycle

In your case, let's walk through your requirements

Requirements Gathering

Your original requirements

Any of the following must be true:

  • The user must be the owner of the recipe (the person who wrote it).
  • The user must be a team member on the project where the recipe was created. (either directly or via group membership)
  • The user must be a member of a group manufacturing the recipe. (They need to see the recipe to manufacture it.)
  • The user must be a member of a group that has manufactured the recipe within the past two weeks. (i.e., after completing a request to manufacture the recipe, they can continue to view the recipe for two weeks to correct any problems.)
  • The user must be an administrator.

Restructured requirements

I like to rework requirements as subject - verb - object e.g. A user can view a recipe... Based on this model your original requirements can be reworded as the following:

  1. A user can view a recipe he/she owns.
  2. A user can view a recipe that was created inside a project the user belongs to
  3. A user can view a recipe in the manufacturing stage that belongs to a (manufacturing) group the user belongs to.
  4. A user can view a recipe in the manufactured stage that belongs to a (manufacturing) group the user belongs to and if the manufacturing date is within 2 weeks of today's date.
  5. Administrators can view recipes.

Identify attributes

You can break down attributes into different categories (or grammatical functions). XACML uses categories itself so this is a natural step towards implementing using XACML (or ALFA).

Subject Attributes

  • userId: the key attribute in this category. It will be used to identify the user but also retrieve all other derived attributes.
  • project: the list of projects a user belongs to
  • group: the list of groups a user belongs to
  • role: the role of the user e.g.

Resource Attributes

  • objectType: this is an attribute you use to distinguish between different types of items you want to control access to e.g. a recipe, a document, or a transaction
  • recipeId: the key attribute for recipes. It will be used to identify which particular recipe you want to get access to and will also serve as a key to look up other attributes e.g. the state the recipe is in, the group and project it belongs to.
  • group: the group the recipe belongs to
  • project: the project the recipe belongs to

Action Attributes

In this category, by the looks of it, you only have:

  • actionId with potential values of {view, edit, delete...}.

Requirements rewritten using the attributes

  1. Anyone can do the action == "view" on objectType == "recipe" if recipe.owner == userId
  2. Anyone can do the action == "view" on objectType == "recipe" if recipe.project == user.project
  3. Anyone can do the action == "view" on objectType == "recipe" if recipe.stage == "manufacturing" and recipe.group == user.group
  4. Anyone can do the action == "view" on objectType == "recipe" if recipe.stage == "manufactured" and recipe.group == user.group and currentDate <= recipe.manufacturedDate + "2 weeks".
  5. Anyone with the role == "administrator" can do the action == "view" on objectType == "recipe".

Implementing the policies using ALFA or XACML

The next step is to implement your policies. You can use ALFA (Abbreviated Language for Authorization). Axiomatics has an Eclipse plugin that translates ALFA into XACML 3.0.

Let's create a policy set that handles recipes. That policy set will contain a policy that handles the action view. That policy in turn will contain rules that address each requirement.

policyset recipe{
    target clause objectType == "recipe"
    apply firstApplicable
    /**
     * View recipes
     */
    policy viewRecipe{
        target clause actionId == "view"
        apply firstApplicable
        /**
         * Administrators can view all recipes
         */
        rule administrator{
            target clause user.role == "administrator"
            permit
        }
        /**
         * Recipe owners can view their own recipes
         */
        rule owner{
            permit
            condition user.userId == recipe.owner
        }
        /**
         * Users can view recipes in their project
         */
         rule sameProject{
             permit
             condition user.assignedProject == recipe.assignedProject
         }
        /**
         * Users can view recipes in their project
         */
         rule sameGroup{
             target clause recipe.stage == "manufacturing"
             permit
             condition user.assignedGroup == recipe.assignedGroup
         }
        /**
         * Users can view recipes in their project
         */
         rule sameGroupManufactured{
             target clause recipe.stage == "manufacturing"
             permit
             condition user.assignedGroup == recipe.assignedGroup && currentDate<=dateTimeAddDayTimeDuration(dateTimeOneAndOnly(recipe.manufacturedDate),"P14D":dayTimeDuration)
         }
    }
}

Using Policy Information Points

How would a PIP gather this information? Directly from a database? Via service calls to the system that stores this information?

A PIP is just an abstract notion of an external attribute source. It can be anything. Different implementations will have different PIP connectors. For instance Axiomatics Policy Server provides connectors to SQL, LDAP, and REST services. This covers most of a client's needs.

XACML and OAuth 2.0

You compare both technologies but they are somehow a little different. OAuth 2.0 focuses first and foremost on authentication. It came about to defeat the password anti-pattern. Then there was a need to define permissions or, as they call them in OAuth, scopes. However those scopes are only punctual permissions. You still rely on the target application to publish a set of valid scopes and you still cannot do fine-grained access control. My colleague wrote a three-part blog on the topic, the first part of which you can read here.

I hope this helps. Feel free to ask follow-up questions or tweet to me.

XACML Output (Bonus)

<?xml version="1.0" encoding="UTF-8"?>
 <!--This file was generated by the ALFA Plugin for Eclipse from Axiomatics AB (http://www.axiomatics.com). 
 Any modification to this file will be lost upon recompilation of the source ALFA file-->
<xacml3:PolicySet xmlns:xacml3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
    PolicySetId="http://axiomatics.com/alfa/identifier/so.recipe"
    PolicyCombiningAlgId="urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:first-applicable"
    Version="1.0">
    <xacml3:Description>Control access to recipes</xacml3:Description>
    <xacml3:PolicySetDefaults>
        <xacml3:XPathVersion>http://www.w3.org/TR/1999/REC-xpath-19991116</xacml3:XPathVersion>
    </xacml3:PolicySetDefaults>
    <xacml3:Target>
        <xacml3:AnyOf>
            <xacml3:AllOf>
                <xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                    <xacml3:AttributeValue
                        DataType="http://www.w3.org/2001/XMLSchema#string">recipe</xacml3:AttributeValue>
                    <xacml3:AttributeDesignator 
                        AttributeId="so.objectType"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                        MustBePresent="false"
                    />
                </xacml3:Match>
            </xacml3:AllOf>
        </xacml3:AnyOf>
    </xacml3:Target>
    <xacml3:Policy xmlns:xacml3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
        PolicyId="http://axiomatics.com/alfa/identifier/so.recipe.viewRecipe"
        RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
        Version="1.0">
        <xacml3:Description>View recipes</xacml3:Description>
        <xacml3:PolicyDefaults>
            <xacml3:XPathVersion>http://www.w3.org/TR/1999/REC-xpath-19991116</xacml3:XPathVersion>
        </xacml3:PolicyDefaults>
        <xacml3:Target>
            <xacml3:AnyOf>
                <xacml3:AllOf>
                    <xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <xacml3:AttributeValue
                            DataType="http://www.w3.org/2001/XMLSchema#string">view</xacml3:AttributeValue>
                        <xacml3:AttributeDesignator 
                            AttributeId="so.actionId"
                            DataType="http://www.w3.org/2001/XMLSchema#string"
                            Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action"
                            MustBePresent="false"
                        />
                    </xacml3:Match>
                </xacml3:AllOf>
            </xacml3:AnyOf>
        </xacml3:Target>
        <xacml3:Rule 
                Effect="Permit"
                RuleId="http://axiomatics.com/alfa/identifier/so.recipe.viewRecipe.administrator">
            <xacml3:Description>Administrators can view all recipes</xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">administrator</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator 
                                AttributeId="so.user.role"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                                MustBePresent="false"
                            />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
        </xacml3:Rule>
        <xacml3:Rule 
                Effect="Permit"
                RuleId="http://axiomatics.com/alfa/identifier/so.recipe.viewRecipe.owner">
            <xacml3:Description>Recipe owners can view their own recipes</xacml3:Description>
            <xacml3:Target />
            <xacml3:Condition>
                <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:any-of-any">
                    <xacml3:Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal"/>
                    <xacml3:AttributeDesignator 
                        AttributeId="so.user.userId"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                        MustBePresent="false"
                    />
                    <xacml3:AttributeDesignator 
                        AttributeId="so.recipe.owner"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                        MustBePresent="false"
                    />
                </xacml3:Apply>
            </xacml3:Condition>
        </xacml3:Rule>
        <xacml3:Rule 
                Effect="Permit"
                RuleId="http://axiomatics.com/alfa/identifier/so.recipe.viewRecipe.sameProject">
            <xacml3:Description>Users can view recipes in their project</xacml3:Description>
            <xacml3:Target />
            <xacml3:Condition>
                <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:any-of-any">
                    <xacml3:Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal"/>
                    <xacml3:AttributeDesignator 
                        AttributeId="so.user.assignedProject"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                        MustBePresent="false"
                    />
                    <xacml3:AttributeDesignator 
                        AttributeId="so.recipe.assignedProject"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                        MustBePresent="false"
                    />
                </xacml3:Apply>
            </xacml3:Condition>
        </xacml3:Rule>
        <xacml3:Rule 
                Effect="Permit"
                RuleId="http://axiomatics.com/alfa/identifier/so.recipe.viewRecipe.sameGroup">
            <xacml3:Description>Users can view recipes in their project</xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">manufacturing</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator 
                                AttributeId="so.recipe.stage"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                                MustBePresent="false"
                            />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
            <xacml3:Condition>
                <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:any-of-any">
                    <xacml3:Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal"/>
                    <xacml3:AttributeDesignator 
                        AttributeId="so.user.assignedGroup"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                        MustBePresent="false"
                    />
                    <xacml3:AttributeDesignator 
                        AttributeId="so.recipe.assignedGroup"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                        MustBePresent="false"
                    />
                </xacml3:Apply>
            </xacml3:Condition>
        </xacml3:Rule>
        <xacml3:Rule 
                Effect="Permit"
                RuleId="http://axiomatics.com/alfa/identifier/so.recipe.viewRecipe.sameGroupManufactured">
            <xacml3:Description>Users can view recipes in their project</xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">manufacturing</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator 
                                AttributeId="so.recipe.stage"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                                MustBePresent="false"
                            />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
            <xacml3:Condition>
                <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:and">
                    <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:any-of-any">
                        <xacml3:Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal"/>
                        <xacml3:AttributeDesignator 
                            AttributeId="so.user.assignedGroup"
                            DataType="http://www.w3.org/2001/XMLSchema#string"
                            Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                            MustBePresent="false"
                        />
                        <xacml3:AttributeDesignator 
                            AttributeId="so.recipe.assignedGroup"
                            DataType="http://www.w3.org/2001/XMLSchema#string"
                            Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                            MustBePresent="false"
                        />
                    </xacml3:Apply>
                    <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:any-of">
                        <xacml3:Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:dateTime-greater-than-or-equal"/>
                        <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:3.0:function:dateTime-add-dayTimeDuration" >
                            <xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:dateTime-one-and-only" >
                                <xacml3:AttributeDesignator 
                                    AttributeId="so.recipe.manufacturedDate"
                                    DataType="http://www.w3.org/2001/XMLSchema#dateTime"
                                    Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                                    MustBePresent="false"
                                />
                            </xacml3:Apply>
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#dayTimeDuration">P14D</xacml3:AttributeValue>
                        </xacml3:Apply>
                        <xacml3:AttributeDesignator 
                            AttributeId="currentDate"
                            DataType="http://www.w3.org/2001/XMLSchema#dateTime"
                            Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment"
                            MustBePresent="false"
                        />
                    </xacml3:Apply>
                </xacml3:Apply>
            </xacml3:Condition>
        </xacml3:Rule>
    </xacml3:Policy>
</xacml3:PolicySet>