FIWARE Security XACML 3.0

Description: This tutorial introduces an additional security generic enabler - Authzforce and adds fine grained control to the security rules generated by Keyrock. Access to the entities created in the previous tutorial is now configured and controlled using an XACML access control policy - this creates a flexible ruleset which can be uploaded and reinterpreted on the fly so complex business rules can be created and changed according to current circumstances.

The tutorial discusses code showing how to integrate Authzforce within a web application and demonstrates examples of Authzforce XACML Server-PDP interactions. cUrl commands are used to show the interactions between generic enablers. Postman documentation is available.

Run in Postman


Ruleset Based Permissions

"Say: Come, I will rehearse what Allah hath prohibited you from:

  • Join not anything as equal with Him
  • Be good to your parents
  • Kill not your children on a plea of want - We provide sustenance for you and for them
  • Come not nigh to shameful deeds. Whether open or secret
  • Take not life, which Allah hath made sacred, except by way of justice and law

thus doth He command you, that ye may learn wisdom."

— Quran 6.151, Sūrat al-Anʻām

Previous tutorials have introduced a simple access control system based on authentication (level 1) or basic authorization access to resources based on a role (level 2). These policies are easy to create, but the rules within them are very black and white, rules cannot rely on one another, have exception clauses or offer access based on time limits or attribute values. There is also no mechanism to resolve different rules in the case of conflict.

To satisfy a complex access control scenario, an additional arbitration microservice is required, which is able to come to a judgement on each Permit/Deny policy decision by reading and interpreting the full set of access control rules, and based their judgement on the evidence provided by the requesting service.

FIWARE Authzforce is a service which is able to provide such an interpretive Policy Decision Point (PDP). It is an advanced access control Generic Enabler which is able to interpret rules supplied using the XACML standard. Rulesets can be amended and uploaded at any time providing a flexible method to maintain security policies which can change according to business need. Furthermore the language used to describe the access policy is designed to be highly extensible and cover any access control scenario.

What is XACML

eXtensible Access Control Markup Language (XACML) is a vendor neutral declarative access control policy language. It was created to promote common access control terminology and interoperability. The architectural naming conventions for elements such as Policy Execution Point (PEP) and Policy Decision Point (PDP) come from the XACML specifications.

XACML policies are split into a hierarchy of three levels - <PolicySet>, <Policy> and <Rule>, the <PolicySet> is a collection of <Policy> elements each of which contain one or more <Rule> elements.

Each <Rule> within a <Policy> is evaluated as to whether it should grant access to a resource - the overall <Policy> result is defined by the overall result of all <Rule> elements processed in turn. Separate <Policy> results are then evaluated against each other using combining alogorthms define which <Policy> wins in case of conflict.

A <Rule> element consists of a <Target> and a <Condition>. This is an example <Rule>, it states access will be granted (Effect="Permit") when a POST request is sent to the /bell/ring endpoint, provided that the subject:role has been provided and that the role=security-role-0000-0000-000000000000 :

<Rule RuleId="alrmbell-ring-0000-0000-000000000000" Effect="Permit">
  <Description>Ring Alarm Bell</Description>
  <Target>
    <AnyOf>
      <AllOf>
        <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
          <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">/bell/ring</AttributeValue>
          <AttributeDesignator Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" AttributeId="urn:thales:xacml:2.0:resource:sub-resource-id" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true" />
        </Match>
      </AllOf>
    </AnyOf>
    <AnyOf>
      <AllOf>
        <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
          <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">POST</AttributeValue>
          <AttributeDesignator Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true" />
        </Match>
      </AllOf>
    </AnyOf>
  </Target>
  <Condition>
    <Apply FunctionId="urn:oasis:names:tc:xacml:3.0:function:any-of">
      <Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal" />
      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">security-role-0000-0000-000000000000</AttributeValue>
      <AttributeDesignator Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" AttributeId="urn:oasis:names:tc:xacml:2.0:subject:role" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false" />
    </Apply>
  </Condition>
</Rule>

This is a very verbose method for creating a simple Verb-Resource access rule, but unlike simple Verb-Resource rules, with XACML, other more complex comparisons can be made, for example checking that time is before a certain hour of day, or checking that a URL starts with or contains a certain string. Conditions can be specified down to the attribute level or combined to make complex calculations, for example - an XACML <Rule> could be created to apply the following policy:

A store manager is able to amend Product prices only the first of the month, and can only alter prices of products she or her immediate superior has created in the first place

Such a <Rule> would require that the <Condition> includes separate clauses/clarifications for the following:

  • What is the User's role? (e.g. manager)
  • What action is being invoked? (e.g PATCH or PUT)
  • Which resource is being protected URL string? (e.g. /v2/entities)
  • What other information must be present in the body of the request? (e.g. Entity type must equal Product)
  • When is the resource being requested? (e.g. the current date)
  • What other additional information must be retrieved from elsewhere prior to making the request
    • Who created the entity? Is it me or my manager?

As you can see these rules can quickly become very complex. For this initial introduction to XACML, the basic rule set used will be kept as simple as possible to avoid unnecessary confusion, suffice it to say that an access policy based on XACML can be expanded to fit the security needs of any complex system.

Further information can be found within the XACML standard and additional resources can be found on the web.


Architecture

This application adds level 3 Advanced Authorization security into the existing Stock Management and Sensors-based application created in previous tutorials and secures access to the context broker behind a PEP Proxy. It will make use of five FIWARE components - the Orion Context Broker,the IoT Agent for UltraLight 2.0, the Keyrock Identity Manager, the Wilma PEP Proxy and the Authzforce XACML Server. All access control decisions will be delegated to Authzforce which will read the ruleset from a previously uploaded policy domain.

Both the Orion Context Broker and the IoT Agent rely on open source MongoDB technology to keep persistence of the information they hold. We will also be using the dummy IoT devices created in the previous tutorial. Keyrock uses its own MySQL database.

Therefore the overall architecture will consist of the following elements:

  • The FIWARE Orion Context Broker which will receive requests using NGSI
  • The FIWARE IoT Agent for UltraLight 2.0 which will receive southbound requests using NGSI and convert them to UltraLight 2.0 commands for the devices
  • FIWARE Keyrock offer a complement Identity Management System including:
    • An OAuth2 authentication system for Applications and Users
    • A site graphical frontend for Identity Management Administration
    • An equivalent REST API for Identity Management via HTTP requests
  • FIWARE Authzforce is a XACML Server providing an interpretive Policy Decision Point (PDP) access to the Orion and/or IoT Agent microservices
  • FIWARE Wilma is a PEP Proxy securing access to the Orion microservices, it delegates the passing of authorisation decisions to Authzforce PDP
  • The underlying MongoDB database :
    • Used by the Orion Context Broker to hold context data information such as data entities, subscriptions and registrations
    • Used by the IoT Agent to hold device information such as device URLs and Keys
  • A MySQL database :
    • Used to persist user identities, applications, roles and permissions
  • The Stock Management Frontend does the following:
    • Displays store information
    • Shows which products can be bought at each store
    • Allows users to "buy" products and reduce the stock count.
    • Allows authorized users into restricted areas, it also delegates authorization decisions to the Authzforce PDP
  • A webserver acting as set of dummy IoT devices using the UltraLight 2.0 protocol running over HTTP - access to certain resources is restricted.

Since all interactions between the services are initiated by HTTP requests, the services can be containerized and run from exposed ports.

The specific architecture of each section of the tutorial is discussed below.

Keyrock Configuration

keyrock:
    image: fiware/idm
    container_name: fiware-keyrock
    hostname: keyrock
    networks:
        default:
            ipv4_address: 172.18.1.5
    depends_on:
        - mysql-db
        - authzforce
    ports:
        - "3005:3005"
    environment:
        - DEBUG=idm:*
        - DATABASE_HOST=mysql-db
        - IDM_DB_PASS_FILE=/run/secrets/my_secret_data
        - IDM_DB_USER=root
        - IDM_HOST=http://localhost:3005
        - IDM_PORT=3005
        - IDM_ADMIN_USER=alice
        - IDM_ADMIN_EMAIL=alice-the-admin@test.com
        - IDM_ADMIN_PASS=test
        - IDM_PDP_LEVEL=advanced
        - IDM_AUTHZFORCE_ENABLED=true
        - IDM_AUTHZFORCE_HOST=authzforce
        - IDM_AUTHZFORCE_PORT=8080
    secrets:
        - my_secret_data

The keyrock container is a web application server listening on a single port:

  • Port 3005 has been exposed for HTTP traffic so we can display the web page and interact with the REST API.

The keyrock container is connecting to Authzforce and is driven by environment variables as shown:

Key Value Description
IDM_PDP_LEVEL advanced Flag indicating that Keyrock should delegate PDP decisions to Authzforce
PEP_PROXY_AZF_PROTOCOL http Transport protocol used by the XACML Server microservice
PEP_PROXY_AZF_HOST authzforce This is URL where the Authzforce is found users
PEP_PROXY_AZF_PORT 8080 Port that Authzforce is listening on

The other keyrock container configuration values described in the YAML file have been described in previous tutorials

PEP Proxy Configuration

orion-proxy:
    image: fiware/pep-proxy
    container_name: fiware-orion-proxy
    hostname: orion-proxy
    networks:
        default:
            ipv4_address: 172.18.1.10
    depends_on:
        - keyrock
        - authzforce
    ports:
        - "1027:1027"
    expose:
        - "1027"
    environment:
        - PEP_PROXY_APP_HOST=orion
        - PEP_PROXY_APP_PORT=1026
        - PEP_PROXY_PORT=1027
        - PEP_PROXY_IDM_HOST=keyrock
        - PEP_PROXY_HTTPS_ENABLED=false
        - PEP_PROXY_IDM_SSL_ENABLED=false
        - PEP_PROXY_IDM_PORT=3005
        - PEP_PROXY_APP_ID=tutorial-dckr-site-0000-xpresswebapp
        - PEP_PROXY_USERNAME=pep_proxy_00000000-0000-0000-0000-000000000000
        - PEP_PASSWORD=test
        - PEP_PROXY_PDP=authzforce
        - PEP_PROXY_AUTH_ENABLED=true
        - PEP_PROXY_MAGIC_KEY=1234
        - PEP_PROXY_AZF_PROTOCOL=http
        - PEP_PROXY_AZF_HOST=authzforce
        - PEP_PROXY_AZF_PORT=8080

The orion-proxy container is an instance of FIWARE Wilma listening on port 1027, it is configured to forward traffic to orion on port 1026, which is the default port that the Orion Context Broker is listening to for NGSI Requests.

The orion-proxy container is delegating PDP decisions to Authzforce and is driven by environment variables as shown:

Key Value Description
PEP_PROXY_PDP authzforce Flag ensuring that the PEP Proxy uses Authzforce as a PDP
PEP_PROXY_AZF_PROTOCOL http Flag to enable use of the XACML PDP
PEP_PROXY_AZF_HOST authzforce This is URL where the Authzforce is found users
PEP_PROXY_AZF_PORT 8080 Port that Authzforce is listening on

The other orion-proxy container configuration values described in the YAML file have been described in previous tutorials

Authzforce Configuration

authzforce:
    image: fiware/authzforce-ce-server
    hostname: authzforce
    container_name: fiware-authzforce
    networks:
        default:
            ipv4_address: 172.18.1.12
    ports:
        - "8080:8080"
    volumes:
        - ./authzforce/domains:/opt/authzforce-ce-server/data/domains

The authzforce container is listening on port 8080, where it receives requests to make PDP decisions. A volume has been exposed to upload a pre-configured domain so that a set of XACML access control policies has already been supplied.

Tutorial Security Configuration

tutorial:
    image: fiware/tutorials.context-provider
    hostname: tutorial
    container_name: fiware-tutorial
    networks:
        default:
            ipv4_address: 172.18.1.7
    expose:
        - "3000"
        - "3001"
    ports:
        - "3000:3000"
        - "3001:3001"
    environment:
        - "DEBUG=tutorial:*"
        - "WEB_APP_PORT=3000"
        - "KEYROCK_URL=http://localhost"
        - "KEYROCK_IP_ADDRESS=http://172.18.1.5"
        - "KEYROCK_PORT=3005"
        - "KEYROCK_CLIENT_ID=tutorial-dckr-site-0000-xpresswebapp"
        - "KEYROCK_CLIENT_SECRET=tutorial-dckr-site-0000-clientsecret"
        - "CALLBACK_URL=http://localhost:3000/login"
        - "AUTHZFORCE_ENABLED=true"
        - "AUTHZFORCE_URL=http://authzforce"
        - "AUTHZFORCE_PORT=8080"

The tutorial container is listening on two ports:

  • Port 3000 is exposed so we can see the web page displaying the Dummy IoT devices.
  • Port 3001 is exposed purely for tutorial access - so that cUrl or Postman can make UltraLight commands without being part of the same network.

The tutorial container is now secured by Authzforce, and is driven by environment variables as shown:

Key Value Description
AUTHZFORCE_ENABLED true Flag to enable use of the XACML PDP
AUTHZFORCE_URL http://authzforce This is URL where the Authzforce is found users
AUTHZFORCE_PORT 8080 Port that Authzforce is listening on

The other tutorial container configuration values described in the YAML file have been described in previous tutorials

Start Up

To start the installation, do the following:

git clone git@github.com:FIWARE/tutorials.XACML-Access-Rules.git
cd tutorials.XACML-Access-Rules

./services create

Note The initial creation of Docker images can take up to three minutes

Thereafter, all services can be initialized from the command-line by running the services Bash script provided within the repository:

./services start

Note: If you want to clean up and start over again you can do so with the following command:

./services stop

Dramatis Personae

The following people at test.com legitimately have accounts within the Application

  • Alice, she will be the Administrator of the Keyrock Application
  • Bob, the Regional Manager of the supermarket chain - he has several store managers under him:
    • Manager1
    • Manager2
  • Charlie, the Head of Security of the supermarket chain - he has several store detectives under him:
    • Detective1
    • Detective2
Name eMail Password
alice alice-the-admin@test.com test
bob bob-the-manager@test.com test
charlie charlie-security@test.com test
manager1 manager1@test.com test
manager2 manager2@test.com test
detective1 detective1@test.com test
detective2 detective2@test.com test

The following people at example.com have signed up for accounts, but have no reason to be granted access

  • Eve - Eve the Eavesdropper
  • Mallory - Mallory the malicious attacker
  • Rob - Rob the Robber
Name eMail Password
eve eve@example.com test
mallory mallory@example.com test
rob rob@example.com test

Authzforce - Obtain Version Information

Once Authzforce is running, you can check the status by making an HTTP request to the exposed administration port (usually 8080. If the response is blank, this is usually because Authzforce is not running or is listening on another port.

1 Request

curl -X GET \
  http://localhost:8080/authzforce-ce/version \
  -H 'Accept: application/xml'

Response

The response returns information about the version of Authzforce.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<productMetadata xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
   xmlns:ns2="http://www.w3.org/2005/Atom"
   xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
   xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
   xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6"
   name="AuthzForce CE Server"
   version="8.0.1"
   release_date="2017-12-05"
   uptime="P0Y0M0DT0H8M47.642S"
   doc="https://authzforce.github.io/fiware/authorization-pdp-api-spec/5.2/"/>

Using an XACML Server

Authzforce is a Policy Decision Point (PDP) Generic Enabler, which makes authorization decisions based on <PolicySet> information written in XACML. This example starts with a running XACML server containing an existing set of rules. An XACML server should offer an API to administrate policies and invoke access control policy decisions. This tutorial is mainly concerned with the decision making side - the creation and administration of access control policies will be dealt with in a subsequent tutorial.

Reading XACML Rulesets

A single XACML server can be used to administrate access control policies for multiple applications. Authzforce is implicitly multi-tenant, in that it allows separate organizations to work on their policies in isolation from one another. This is done by separating the security policies for each application into a separate domain where they can access their own <PolicySets>. A domain holds metadata about the secured application along with versions of the policies themselves (effectively a series of files which can be accessed by a file server). The domain management API can be used to query Authzforce about the domains served and policies held.

List all domains

To request domain information from Authzforce, make a request to the /authzforce-ce/domains endpoint.

2 Request

curl -X GET \
  http://localhost:8080/authzforce-ce/domains

Response

The response lists the domains which are available in Authzforce. This corresponds to the directory structure uploaded to Authzforce on start-up.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
    <ns2:link rel="item" href="gQqnLOnIEeiBFQJCrBIBDA" title="gQqnLOnIEeiBFQJCrBIBDA"/>
</resources>

Read a single domain

To read information about a domain, and to explore further, make a request to the authzforce-ce/domains/{{domain-id}} endpoint. The following request obtains information about the gQqnLOnIEeiBFQJCrBIBDA domain, which has been generated using a random key by an external Policy Administration Point in this case Keyrock has been used as the PAP, and pre-generated the rule sets.

3 Request

curl -X GET \
  http://localhost:8080/authzforce-ce/domains/gQqnLOnIEeiBFQJCrBIBDA

Response

The response lists more information about the domain, including the ID used within Keyrock (tutorial-dckr-site-0000-xpresswebapp)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<domain xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
    <properties externalId="tutorial-dckr-site-0000-xpresswebapp"/>
    <childResources>
        <ns2:link rel="item" href="/properties" title="Domain properties"/>
        <ns2:link rel="item" href="/pap" title="Policy Administration Point"/>
        <ns2:link rel="http://docs.oasis-open.org/ns/xacml/relation/pdp"
          href="/pdp" title="Policy Decision Point"/>
    </childResources>
</domain>

List all PolicySets available within a Domain

To list the generated IDs for all of the PolicySets found within a domain make a request to the authzforce-ce/domains/{{domain-id}}/pap/policies endpoint. The following request obtains a list of a given policy IDs found within the gQqnLOnIEeiBFQJCrBIBDA domain.

4 Request

curl -X GET \
  http://localhost:8080/authzforce-ce/domains/gQqnLOnIEeiBFQJCrBIBDA/pap/policies

Response

The response returns a list of available revisions of the given policy which are available within. the Authzforce container. This corresponds the named XML files 1.xml, 2.xml etc.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
    <ns2:link rel="item" href="f8194af5-8a07-486a-9581-c1f05d05483c"/>
    <ns2:link rel="item" href="root"/>
</resources>

List the available revisions of a PolicySet

To list the available revisions of a policy, make a request to the authzforce-ce/domains/{{domain-id}}/pap/policies/{{policy-id}} endpoint. Available policy ID are randomly generated, and can be obtained by drilling down using the previous request. The following request obtains a list revision of a given policy found within the gQqnLOnIEeiBFQJCrBIBDA domain.

5 Request

curl -X GET \
  http://localhost:8080/authzforce-ce/domains/gQqnLOnIEeiBFQJCrBIBDA/pap/policies/f8194af5-8a07-486a-9581-c1f05d05483c

Response

The response returns a list of available revisions of the given policy which are available within the Authzforce container. This corresponds the named XML files 1.xml, 2.xml etc.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
    <ns2:link rel="item" href="2"/>
    <ns2:link rel="item" href="1"/>
</resources>

Read a single version of a PolicySet

To obtain a single revision of a <PolicySet>, make a request to the authzforce-ce/domains/{{domain-id}}/pap/policies/{{policy-id}}/{{revision-number}} endpoint. The following request obtains the second revision of the given policy found within the gQqnLOnIEeiBFQJCrBIBDA domain.

6 Request

curl -X GET \
  http://localhost:8080/authzforce-ce/domains/gQqnLOnIEeiBFQJCrBIBDA/pap/policies/f8194af5-8a07-486a-9581-c1f05d05483c/2

Response

The response contains the full <PolicySet> for the given revision. This is a copy of the file held within Authzforce.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:PolicySet xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6" PolicySetId="f8194af5-8a07-486a-9581-c1f05d05483c" Version="2" PolicyCombiningAlgId="urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:deny-unless-permit">
    <ns3:Description>Policy Set for application tutorial-dckr-site-0000-xpresswebapp</ns3:Description>
    <ns3:Target/>
    <ns3:Policy PolicyId="security-role-0000-0000-000000000000"
      Version="1.0"
      RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit">
        <ns3:Description>Role security-role-0000-0000-000000000000 from application tutorial-dckr-site-0000-xpresswebapp</ns3:Description>
        <ns3:Target>
           ...etc
        </ns3:Target>
        <ns3:Rule RuleId="alrmbell-ring-0000-0000-000000000000" Effect="Permit">
            ...etc
        </ns3:Rule>
        ..etc
    </ns3:Policy>
</ns3:PolicySet>

Requesting Policy Decisions

For the purpose of this tutorial, Authzforce has been just been supplied with a simple set of basic role-based rules in a similar fashion to the level 2 authorization example found in the previous Securing Access tutorial:

  • The unlock door command can only be sent by Security staff.
  • Access to the price-change and order-stock areas are only available to Managers
  • People with either the Manager or Security role can ring the bell
  • Both Manager or Security can access and interact with the store data

The only difference is that access to all store entities is now restricted to users with an assigned role rather than being based on level 1 authentication access.

To request a decision from Authzforce, a structured request containing all relevant information must be sent to the domains/{domain-id}/pdp endpoint. In this case, the Body of the request includes information such as the roles that the User has, the application ID that is being requested (tutorial-dckr-site-0000-xpresswebapp) and the HTTP verb and resource that are being requested ( a GET request on the /app/price-change URL). Obviously the information passed in the Body can be expanded as the rules become more complex.

Permit Access to a Resource

To request a decision from Authzforce, make a POST request to the domains/{domain-id}/pdp endpoint. In this case the user has the managers-role-0000-0000-000000000000 and is requesting access the /app/price-change resource.

7 Request

curl -X POST \
  http://localhost:8080/authzforce-ce/domains/gQqnLOnIEeiBFQJCrBIBDA/pdp \
  -H 'Content-Type: application/xml' \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" CombinedDecision="false" ReturnPolicyIdList="false">
   <Attributes Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:2.0:subject:role" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">managers-role-0000-0000-000000000000</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">tutorial-dckr-site-0000-xpresswebapp</AttributeValue>
      </Attribute>
      <Attribute AttributeId="urn:thales:xacml:2.0:resource:sub-resource-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">/app/price-change</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">GET</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment" />
</Request>'

Response

The managers-role-0000-0000-000000000000 permits access to the /app/price-change endpoint. The response for a successful request includes a <Decision> element to Permit access to the resource.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:Response xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
    <ns3:Result>
        <ns3:Decision>Permit</ns3:Decision>
    </ns3:Result>
</ns3:Response>

Deny Access to a Resource

To request a decision from Authzforce, make a POST request to the domains/{domain-id}/pdp endpoint. In this case the user has the security-role-0000-0000-000000000000 and is requesting access the /app/price-change resource.

8 Request

curl -X POST \
  http://localhost:8080/authzforce-ce/domains/gQqnLOnIEeiBFQJCrBIBDA/pdp \
  -H 'Content-Type: application/xml' \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" CombinedDecision="false" ReturnPolicyIdList="false">
   <Attributes Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:2.0:subject:role" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">security-role-0000-0000-000000000000</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">tutorial-dckr-site-0000-xpresswebapp</AttributeValue>
      </Attribute>
      <Attribute AttributeId="urn:thales:xacml:2.0:resource:sub-resource-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">/app/price-change</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">GET</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment" />
</Request>'

Response

The security-role-0000-0000-000000000000 does not permit access to the /app/price-change endpoint. The response for an unsuccessful request includes a <Decision> element which will Deny access to the resource.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:Response xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
    <ns3:Result>
        <ns3:Decision>Deny</ns3:Decision>
    </ns3:Result>
</ns3:Response>

PDP - Advanced Authorization

As a reminder, there are three Levels of PDP Access Control:

  • Level 1: Authentication Access - Allow all actions to every signed in user and no actions to an anonymous user.
  • Level 2: Basic Authorization - Check which resources and verbs the currently logged in user should have access to
  • Level 3: Advanced Authorization - Fine grained control through XACML

Within FIWARE, Level 3 access control can be provided by adding Authzforce to the existing security microservices (IDM and PEP Proxy) within the Smart Application infrastructure. Access control levels 1 and 2 have been covered in previous tutorials and can be fulfilled using Keyrock alone or with or without an associated PEP Proxy.

Advanced Authorization

Advanced Authorization is able to deal with complex rulesets. Permissions are no longer merely based on a fixed role, resource and an action, but can be extended as necessary.

For example users in role XXX can access URL starting with YYY provided that the HTTP verb is either GET, PUT or POST. Such users may also DELETE provided that they were the creator in the first place.

Within the tutorial programatic example we are using our own trusted instance of Keyrock - once a user has signed in and obtained an access_token, the access_token can be stored in session and used to retrieve user details on demand. All access to the Orion context broker is hidden behind a PEP Proxy. Whenever a request is made to Orion, the access_token is passed in the header of the request, and the PEP proxy handles the decision to whether to execute the request.

User Obtains an Access Token

In order to identify themselves, every user must obtain an access token, in order to do so, they must use one of the OAuth2 access grants described in a previous tutorial.

To log in using the user-credentials flow send a POST request to the oauth2/token endpoint of Keyrock with the grant_type=password

9 Request

curl -X POST \
  http://localhost:3005/oauth2/token \
  -H 'Accept: application/json' \
  -H 'Authorization: Basic dHV0b3JpYWwtZGNrci1zaXRlLTAwMDAteHByZXNzd2ViYXBwOnR1dG9yaWFsLWRja3Itc2l0ZS0wMDAwLWNsaWVudHNlY3JldA==' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=bob-the-manager@test.com&password=test&grant_type=password'

Response

The response returns an access_token to identify the user (in this case Bob the Manager)

{
    "access_token": "08fef363c429cb34cfff3f56dfe751a8d1890690",
    "token_type": "Bearer",
    "expires_in": 3599,
    "refresh_token": "35a644094b598cb0d720fcb323369a53820a6a44",
    "scope": ["bearer"]
}

Obtain Roles and Domain

If a user has logged in, the access_token can be used in combination with the /user endpoint to obtain access permissions to a resource. This example retrieves Bob's permissions to a given resource.

10 Request

curl -X GET \
  'http://localhost:3005/user?access_token={{access_token}}&app_id={{app-id}}&authzforce=true'

Where :

  • {{access-token}} is the current access token of a logged in user (e.g. 08fef363c429cb34cfff3f56dfe751a8d1890690)
  • {{app-id}} holds the application to request tutorial-dckr-site-0000-xpresswebapp and authzforce=true indicates that we want to obtain an Authzforce Domain from Keyrock

Response

The response include an authorization_decision attribute which denies direct access for the request, but includes additional information so that an additional request a decision from Authzforce

In the example below the access token used belonged to Bob the manager, and his roles and the app_azf_domain associated to the app-id are returned.

{
    "organizations": [],
    "displayName": "",
    "roles": [
        {
            "id": "managers-role-0000-0000-000000000000",
            "name": "Management"
        }
    ],
    "app_id": "tutorial-dckr-site-0000-xpresswebapp",
    "trusted_apps": [],
    "isGravatarEnabled": false,
    "email": "bob-the-manager@test.com",
    "id": "bbbbbbbb-good-0000-0000-000000000000",
    "authorization_decision": "",
    "app_azf_domain": "gQqnLOnIEeiBFQJCrBIBDA",
    "eidas_profile": {},
    "username": "bob"
}

Apply a Policy to a Request

To request a decision from Authzforce, a structured request containing all relevant information must be sent to the domains/{domain-id}/pdp endpoint. In this case, the Body of the request includes information such as the roles that the User has (managers-role-0000-0000-000000000000), the application ID that is being requested (tutorial-dckr-site-0000-xpresswebapp) and the HTTP verb and resource that are being requested ( a POST request on the /v2/entities URL)

11 Request

curl -X POST \
  http://localhost:8080/authzforce-ce/domains/gQqnLOnIEeiBFQJCrBIBDA/pdp \
  -H 'Content-Type: application/xml' \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" CombinedDecision="false" ReturnPolicyIdList="false">
   <Attributes Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:2.0:subject:role" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">managers-role-0000-0000-000000000000</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">tutorial-dckr-site-0000-xpresswebapp</AttributeValue>
      </Attribute>
      <Attribute AttributeId="urn:thales:xacml:2.0:resource:sub-resource-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">/v2/entities</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action">
      <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" IncludeInResult="false">
         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">POST</AttributeValue>
      </Attribute>
   </Attributes>
   <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment" />
</Request>'

Response

The response includes a <Decision> element which will either Permit or Deny the request.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:Response xmlns="http://authzforce.github.io/rest-api-model/xmlns/authz/5"
  xmlns:ns2="http://www.w3.org/2005/Atom"
  xmlns:ns3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
  xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/6.0"
  xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
    <ns3:Result>
        <ns3:Decision>Permit</ns3:Decision>
    </ns3:Result>
</ns3:Response>

Advanced Authorization - Sample Code

Programmatically, any Policy Execution Point consists of two parts, an OAuth request to Keyrock retrieves information about the user (such as the assigned roles) as well as the policy domain to be queried.

A second request is sent to the relevant domain endpoint within Authzforce, providing all of the information necessary for Authzforce to provide a judgement. Authzforce responds with a permit or deny response, and the decision whether to continue can be made thereafter.

function authorizeAdvancedXACML(req, res, next, resource = req.url) {
    const keyrockUserUrl =
        "http://keyrock/user?access_token=" + req.session.access_token + "&app_id=" + clientId + "&authzforce=true";

    return oa
        .get(keyrockUserUrl)
        .then(response => {
            const user = JSON.parse(response);
            return azf.policyDomainRequest(user.app_azf_domain, user.roles, resource, req.method);
        })
        .then(authzforceResponse => {
            res.locals.authorized = authzforceResponse === "Permit";
            return next();
        })
        .catch(error => {
            debug(error);
            res.locals.authorized = false;
            return next();
        });
}

The full code to supply each request to Authzforce can be found within the tutorials' Git Repository - the actual information to supply will depend on business use case - it could be expanded to include temporal information, relationships between records and so on, but in this very simple example only roles are necessary.

const xml2js = require("xml2js");
const request = require("request");

function policyDomainRequest(domain, roles, resource, action) {
    let body =
        '<?xml version="1.0" encoding="UTF-8"?>\n' +
        '<Request xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" CombinedDecision="false" ReturnPolicyIdList="false">\n';
    // Code to create the XML body for the request is omitted
    body = body + "</Request>";

    const options = {
        method: "POST",
        url: "http://authzforceUrl/authzforce-ce/domains/" + domain + "/pdp",
        headers: { "Content-Type": "application/xml" },
        body
    };

    return new Promise((resolve, reject) => {
        request(options, function(error, response, body) {
            let decision;
            xml2js.parseString(body, { tagNameProcessors: [xml2js.processors.stripPrefix] }, function(err, jsonRes) {
                // The decision is found within the /Response/Result[0]/Decision[0] XPath
                decision = jsonRes.Response.Result[0].Decision[0];
            });
            decision = String(decision);
            return error ? reject(error) : resolve(decision);
        });
    });
}

Advanced Authorization - PEP Proxy

Applying advanced authorization within a PEP proxy requires very similar code to the programmatic example described above. The Wilma generic enabler extracts a token from the header supplied by the request and makes a request to Keyrock to obtain further information about the user. A PDP request is then made to Authzforce to decide whether to proceed.

Obviously any scalable solution should also cache information about the PDP requests made and the responses to avoid making unnecessary requests.

PDP - Advanced Authorization - Running the Example

Note Five resources have been secured at level 3:

  • sending the unlock door command
  • sending the ring bell command
  • access to the price-change area
  • access to the order-stock area
  • access to Orion (behind a PEP Proxy)

Eve the Eavesdropper

Eve has an account, but no roles in the application.

  • From http://localhost:3000, log in as eve@example.com with the password test
Level 3 : Advanced Authorization Access
  • Click on any store page

    • access to view the page is permitted for any logged in users
    • access to retrieve Orion data is now denied since Eve has no role which permits access.
  • Click on the restricted access links at http://localhost:3000 - access is denied

  • Open the Device Monitor on http://localhost:3000/device/monitor
    • Unlock a door - access is denied
    • Ring a bell - access is denied

Note: As Eve has a recognized account, she gains full authentication access (level 1).
This means she is able to gain access to view the store page on the application, even though her account has no roles attached.
However, the PEP Proxy (secured at level 3) denies authorization access to get the store data itself.

Bob The Regional Manager

Bob has the management role

  • From http://localhost:3000, log in as bob-the-manager@test.com with the password test
Level 3 : Advanced Authorization Access
  • Click on the restricted access links at http://localhost:3000 - access is permitted - This is a management only permission
  • Open the Device Monitor on http://localhost:3000/device/monitor
    • Unlock a door - access is denied. - This is a security only permission
    • Ring a bell - access is permitted - This is permitted to management users

Charlie the Security Manager

Charlie has the security role

  • From http://localhost:3000, log in as charlie-security@test.com with the password test
Level 3: Advanced Authorization Access
  • Click on the restricted access links at http://localhost:3000 - access is denied - This is a management only permission
  • Open the Device Monitor on http://localhost:3000/device/monitor
    • Unlock a door - access is permitted - This is a security only permission
    • Ring a bell - access is permitted - This is permitted to security users