PingAuthorize

Tutorial 3: Configuring attribute-based access control for API resources

This tutorial describes how to build and test fine-grained access control (FGAC) policies that restrict access to a protected resource based on attributes of both the resource and the caller.

Scenario

In some data use cases, it is necessary to know both the resource being requested and the requesting user. For example, a counselor can only view the records of students in their department. In the scenario of the meme game, users are allowed to invite their friends or family to like or critique their memes. Because some memes are inappropriate for younger audiences, the city of Youngstown, Ohio passes an ordinance that does not allow you to serve its citizens memes rated for ages 13 and older. You must create a policy to enforce this by checking the city of the user’s profile and the age rating of the shared meme.

Obviously, not all Youngstown residents are young. In a more realistic scenario, we might compare the age of the requesting user to the age rating of the meme. However, computing the user’s age from their date of birth adds unnecessary complexity.

Tasks

This tutorial teaches you how to configure attribute-based application programming interface (API) access control rules by walking you through the following tasks:

  1. Configure a proxy for the Meme Game API.

  2. Create a policy blocking all users from viewing shared memes.

  3. Add policy condition logic to allow users not from Youngstown to view shared memes.

  4. Add policy condition logic to allow users from Youngstown to view shared memes rated for ages under 13.

  5. Add a statement to set the API error response when a policy blocks access.

The following sections provide the details for completing these tasks.

Configuring the API security gateway

This tutorial describes how to use the API security gateway to allow requests to a parameterized endpoint.

You will configure https://localhost:7443/meme-game/api/v1/users/{user}/answers to proxy to https://meme-game.com/api/v1/users/{user}/answers, where user can be any username.

Creating the gateway API endpoint

Configure a reverse proxy by configuring an API External Server and a Gateway API Endpoint.

Steps

  1. Optional: Configure an API External Server for the Meme Game API. An API External Server controls how PingAuthorize Server handles connections to an HTTPS API server, including configuration related to TLS. In this case, we simply need to provide a base URL.

    If you completed Tutorial 2: Configuring fine-grained access control for an API, then you already set up this API External Server.

    1. Sign on to the administrative console using the URL and credentials from Accessing the GUIs.

    2. Click External Servers.

    3. Click New External Server and choose API External Server.

    4. For Name, specify Meme Game API.

    5. For Base URL, specify https://meme-game.com.

      The following image shows this configuration.

      Screen capture of the New API External Server screen with the Name and Base URL fields configured and a Save button in the upper right
    6. Click Save.

  2. Configure a Gateway API Endpoint. A Gateway API Endpoint controls how PingAuthorize Server proxies incoming HTTP client requests to an upstream API server.

    1. In the administrative console, click Configuration and then Gateway API Endpoints.

    2. Click New Gateway API Endpoint.

    3. For Name, specify Meme Game - Shared Answers.

    4. For Inbound Base Path, specify /meme-game/api/v1/users/{user}/answers.

      The inbound base path defines the base request path for requests to be received by PingAuthorize Server.

      By surrounding a value in curly braces, you can add a parameter to a gateway API endpoint’s inbound-base-path and use it to fill in a parameter of the same name in the outbound path. You can also use this parameter to inform other elements of the policy request, such as the service.

    5. For Outbound Base Path, specify /api/v1/users/{user}/answers.

      The outbound base path defines the base request path for requests that PingAuthorize Server forwards to an API server.

    6. For API Server, specify Meme Game API. This is the API External Server you defined in Configuring a reverse proxy for the Meme Game API.

      Your screen should look like the following one.

      Screen capture of the General Configuration section of the Gateway API Endpoint screen, with the Name, Inbound Base Path, Outbound Base Path, and API Server configured as specified and the Save To PingAuthorize Server Cluster button in the upper right
    7. Save your changes.

Testing the gateway

You can test the newly created Gateway API Endpoint with cURL or Postman.

Steps

  • Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers \
      -H 'Authorization: Bearer {"active": true, "sub": "user.0"}'
    Result:

    You should get a 200 OK response with a JSON response body that contains a series of answers in an array titled data.

Creating a policy based on user credentials

This tutorial describes how to create a policy that acts on information about the user.

Creating a service for the Shared Answers endpoint

Create a service in the Trust Framework to ensure that our policy only affects requests to our new endpoint.

About this task

This task passes the name of the Gateway API Endpoint configured in PingAuthorize Server as the service to the PingAuthorize policy decision point (PDP).

Steps

  1. From the PingAuthorize Policy Editor, go to Trust Framework and click Services.

  2. From the menu, select Add new service.

  3. For the name, replace Untitled with Meme Game - Shared Answers.

  4. Verify that, in the Parent field, no parent is selected.

    To remove a parent, click the delete icon to the right of the Parent field.

    Your service should look like the example in the following image:

    Screen capture of
  5. Click Save changes.

Creating a policy for the Shared Answers endpoint

Create a policy to prevent users from accessing the Shared Answers endpoint.

Steps

  1. In the PingAuthorize Policy Editor, go to the Policies tab.

  2. Select Global Decision Point.

  3. From the menu, select Add Policy.

  4. For the name, replace Untitled with Users viewing shared memes.

  5. Click next to Applies to.

  6. In the upper-right corner of the left pane, click Components.

  7. From the Actions list, drag outbound-GET to the Add definitions and targets, or drag from Components box.

  8. From the Services list, drag Meme Game - Shared Answers to the Add definitions and targets, or drag from Components box.

  9. For the combining algorithm, select Unless one decision is permit, the decision will be deny.

  10. Click Save changes.

    Your policy should look like the example in the following image:

    Screen capture of

Testing the policy

You can test the new policy with cURL or Postman.

Steps

  • Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/1. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 \
       -H 'Authorization: Bearer {"active": true, "sub": "user.0"}'
    Result:

    You should get a 403 Forbidden response with the following body.

    {
    	"errorMessage": "Access Denied",
    	"status": 403
    }

Creating an attribute from user data

Create an attribute to represent the city the user lives in.

Steps

  1. In the PingAuthorize Policy Editor, go to Trust Framework and click Attributes.

  2. From the menu, select Add new Attribute.

  3. For the name, replace Untitled with city.

  4. For Parent, select TokenOwner.

  5. Click the next to Resolvers and click Add Resolver.

  6. For Resolver type, select Attribute and specify a value of TokenOwner.

  7. Click the next to Value Processors and click Add Processor.

  8. For Processor, select JSON Path and specify a value of $.l[0]. (The LDAP attribute l represents locality.)

  9. For the processor’s Value type, select String.

  10. For Value Settings, set the Type to String.

  11. Click Save changes.

    Result:

    You have an attribute for the user’s city, as shown in the following image.

    Screen capture of an attribute definition with a Resolver, Value Processor, and Value Setting configured as specified

Adding logic to allow non-Youngstown users

Add a rule to the Users viewing shared memes API policy to allow users who are not from Youngstown to view answers.

Steps

  1. From the PingAuthorize Policy Editor, go to the Policies tab.

  2. Select Users viewing shared memes.

  3. Click Add Rule.

  4. For the name, replace Untitled with Allow people outside of Youngstown.

  5. For Effect, select Permit.

  6. To specify a Condition, perform the following steps:

    1. Click Comparison.

    2. From the Select an Attribute list, select TokenOwner.city.

    3. In the second field, select Does Not Equal.

    4. In the third field, type Youngstown.

  7. Click Save changes.

    Result:

    You have a rule that allows users from outside Youngstown.

    Screen capture of rule to allow people outside of Youngstown with a condition configured as specified

Testing that the policy blocks Youngstown users

You can test the new rule with cURL or Postman.

Steps

  1. Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 as user.0. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.0"}'
    Result:

    A 200 OK response with the following body.

    {
    	"data": {
        	"id": "1",
        	"type": "answers",
        	"attributes": {
            	"url": "https://i.imgflip.com/2fm6x.jpg",
            	"captions": [
                	"Still waiting for the bus to Jennie’s"
            	],
            	"rating": null,
            	"created_at": "2020-05-06T22:25:06+00:00"
        	}
    	},
    	"meta": {}
    }
  2. Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 as user.660. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.660"}'
    Result:

    The user is from Youngstown, so the result is a 403 Forbidden response with the following body.

    {
    	"errorMessage": "Access Denied",
    	"status": 403
    }

Creating a policy based on the API response

This tutorial describes how to create a policy that acts on information about the response received from the API server.

Creating an attribute from response data

Create an attribute to represent the age rating of the meme being requested.

Steps

  1. From the PingAuthorize Policy Editor, go to Trust Framework and click Attributes.

  2. From the menu, select Add new Attribute.

  3. For the name, replace Untitled with Meme game answer rating.

  4. Verify that in the Parent field, no parent is selected.

    To remove a parent, click the delete icon to the right of the Parent field.

  5. Click the next to Resolvers and click Add Resolver.

  6. For Resolver type, select Attribute and specify a value of HttpRequest.ResponseBody.

  7. Click the next to Value Processors and click Add Processor.

  8. For Processor, select JSON Path and specify a value of $.data.attributes.rating.

  9. For the processor’s Value type, select Number.

  10. For Value Settings, set the Type to Number.

  11. Click Save changes.

    Result:

    You have a new attribute for the answer’s age rating.

    Screen capture of the attribute definition window with a Resolver, Value Processor, and Value Setting configured as specified

Adding logic to allow family-friendly memes

Add a rule to the Users viewing shared memes API policy to allow users to view answers that are rated for ages under 13.

Steps

  1. From the PingAuthorize Policy Editor, go to the Policies tab.

  2. Select Users viewing shared memes.

  3. Click Add Rule.

  4. For the name, replace Untitled with Anyone can view family-friendly answers.

  5. For Effect, select Permit.

  6. Specify a Condition.

    1. Click Comparison.

    2. From the Select an Attribute list, select Meme game answer rating.

    3. In the second field, select Less Than.

    4. In the third field, type 13.

  7. Click Save changes.

    Result:

    Your new rule to allow family-friendly memes should look like the following image.

    Screen capture of the Anyone can view family-friendly answers rule with a Permit effect and a comparison condition configured as specified

Testing that the policy blocks Youngstown users from viewing age 13+ memes

You can test the newly created rule with cURL or Postman.

Steps

  1. Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/2 as user.0. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/2 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.0"}'
    Result:

    When requesting answer 2 as user.0, expect a 200 OK response with the following body.

    {
    	"data": {
        	"id": "2",
        	"type": "answers",
        	"attributes": {
            	"url": "https://i.imgflip.com/23ls.jpg",
            	"captions": [
                	"There was a spider",
                	"it's gone now"
            	],
            	"rating": 13,
            	"created_at": "2020-05-06T22:25:06+00:00"
        	}
    	},
    	"meta": {}
    }
  2. Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/2 as user.660. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/2 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.660"}'
    Result:

    When requesting answer 2, which is rated age 13, as user.660, who is from Youngstown, OH, expect a 403 Forbidden response with the following body.

    {
    	"errorMessage": "Access Denied",
    	"status": 403
    }
  3. Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 as user.0. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.0"}'
    Result:

    When requesting answer 1 as user.0, expect a 200 OK response with the following body.

    {
    	"data": {
        	"id": "1",
        	"type": "answers",
        	"attributes": {
            	"url": "https://i.imgflip.com/2fm6x.jpg",
            	"captions": [
                	"Still waiting for the bus to Jennie’s"
            	],
            	"rating": null,
            	"created_at": "2020-05-06T22:25:06+00:00"
        	}
    	},
    	"meta": {}
    }
  4. Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 as user.660. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.660"}'
    Result:

    When requesting answer 1, which is unrated, as user.660, who is from Youngstown, OH, expect a 403 Forbidden response with the following body. Be aware that this is not the correct behavior; however, to resolve it, we would need to change our attribute definitions.

    {
    	"errorMessage": "Access Denied",
    	"status": 403
    }

Allowing unrated memes

Answer 1 is not being served to user.660, even though it has not been rated as 13+. In this scenario, an unrated answer should be considered friendly to all users. Consider why an unrated meme is being blocked for this user. To resolve this, you can add a default value to the age rating.

Steps

  1. In the PingAuthorize Policy Editor, go to Trust Framework and click Attributes.

  2. Select Meme game answer rating.

  3. For Value Settings, select the Default Value checkbox, and specify a value of 0.

  4. Click Save changes.

    Result:

    Your attribute for answer age ratings has a default value of 0, as shown in the following image.

    Screen capture of the

Testing the default value

You can test that the policy now works correctly with cURL or Postman.

Steps

  • Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 as user.660. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/1 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.660"}'
    Result:

    You should get a 200 OK response with the following body.

    {
    	"data": {
        	"id": "1",
        	"type": "answers",
        	"attributes": {
            	"url": "https://i.imgflip.com/2fm6x.jpg",
            	"captions": [
                	"Still waiting for the bus to Jennie’s"
            	],
            	"rating": null,
            	"created_at": "2020-05-06T22:25:06+00:00"
        	}
    	},
    	"meta": {}
    }

Creating a statement to provide a more useful error message

Add a command, known as a statement, that instructs PingAuthorize to set the HTTP response code and provide a more useful error message when rejecting the outbound response.

About this task

Because this problem is due to an attribute of a user (namely their location), use a 4xx response code to indicate a user issue. The 451 response code has been suggested for use in cases where content cannot be displayed for legal reasons.

Steps

  1. From the PingAuthorize Policy Editor, go to the Policies tab.

  2. Select Users viewing shared memes.

  3. Click Statements.

  4. Click Add Statement and select Denied Reason.

  5. For the name, replace Untitled with Send "not permitted" error.

  6. From the Applies to drop-down list, select Deny.

  7. For a Payload value, enter \{"status": 451, "message": "Restricted", "detail": "Not permitted per regulation"}.

  8. Click Save changes.

    Result:

    You have a new statement, which looks something like the following image.

    Statement for more helpful error message

Testing the statement

You can test that the statement works correctly with cURL or Postman.

Steps

  • Issue a GET request to https://localhost:7443/meme-game/api/v1/users/user.0/answers/2 as user.660. The following cURL command makes such a request.

    curl --insecure -X GET \
      https://localhost:7443/meme-game/api/v1/users/user.0/answers/2 \
      -H 'Authorization: Bearer {"active": true, "sub": "user.660"}'
    Result:

    Expect a 451 Unavailable For Legal Reasons response with the following body.

    {
    	"errorMessage": "Restricted: Not permitted per regulation",
    	"status": 451
    }

Conclusion

In this tutorial, you allowed users to access the meme game’s shared answers functionality through PingAuthorize. Following a request from government authorities, you blocked users from the town of Youngstown, Ohio from viewing memes intended for audiences aged 13 or older. In doing so, you learned about the PingAuthorize ability to control access to resources based on attributes of both the requesting user and the resource being requested. You also learned how to use statements to modify response bodies.

You also learned:

  • Policies can apply to outbound upstream server API responses before they are sent to the API client.

  • HttpRequest.ResponseBody is the upstream server API response body before it is sent to the client.

  • Attributes that cannot be resolved because of any reason, including processing errors, might impact policy outcomes.

  • PingAuthorize supplies the user profile of the access token subject as the Trust Framework attribute TokenOwner.

  • You must populate the child attributes of the TokenOwner that you want to use in a policy.

  • Many attributes in LDAP are multivalued.

  • Statements are used to modify the API response in some way.

  • In this case, denied-reason was used to set the HTTP status code and message body.