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:
-
Configure a proxy for the Meme Game API.
-
Create a policy blocking all users from viewing shared memes.
-
Add policy condition logic to allow users not from Youngstown to view shared memes.
-
Add policy condition logic to allow users from Youngstown to view shared memes rated for ages under 13.
-
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
-
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.
-
Sign on to the administrative console using the URL and credentials from Accessing the GUIs.
-
Click External Servers.
-
Click New External Server and choose API External Server.
-
For Name, specify
Meme Game API
. -
For Base URL, specify
https://meme-game.com
.The following image shows this configuration.
-
Click Save.
-
-
Configure a Gateway API Endpoint. A Gateway API Endpoint controls how PingAuthorize Server proxies incoming HTTP client requests to an upstream API server.
-
In the administrative console, click Configuration and then Gateway API Endpoints.
-
Click New Gateway API Endpoint.
-
For Name, specify
Meme Game - Shared Answers
. -
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.
-
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.
-
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.
-
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
-
From the PingAuthorize Policy Editor, go to Trust Framework and click Services.
-
From the menu, select Add new service.
-
For the name, replace Untitled with
Meme Game - Shared Answers
. -
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:
-
Click Save changes.
Creating a policy for the Shared Answers endpoint
Create a policy to prevent users from accessing the Shared Answers endpoint.
Steps
-
In the PingAuthorize Policy Editor, go to the Policies tab.
-
Select Global Decision Point.
-
From the menu, select Add Policy.
-
For the name, replace Untitled with
Users viewing shared memes
. -
Click next to Applies to.
-
In the upper-right corner of the left pane, click Components.
-
From the Actions list, drag outbound-GET to the Add definitions and targets, or drag from Components box.
-
From the Services list, drag Meme Game - Shared Answers to the Add definitions and targets, or drag from Components box.
-
For the combining algorithm, select Unless one decision is permit, the decision will be deny.
-
Click Save changes.
Your policy should look like the example in the following image:
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
-
In the PingAuthorize Policy Editor, go to Trust Framework and click Attributes.
-
From the menu, select Add new Attribute.
-
For the name, replace Untitled with
city
. -
For Parent, select TokenOwner.
-
Click the next to Resolvers and click Add Resolver.
-
For Resolver type, select Attribute and specify a value of TokenOwner.
-
Click the next to Value Processors and click Add Processor.
-
For Processor, select JSON Path and specify a value of
$.l[0]
. (The LDAP attributel
represents locality.) -
For the processor’s Value type, select String.
-
For Value Settings, set the Type to String.
-
Click Save changes.
Result:
You have an attribute for the user’s city, as shown in the following image.
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
-
From the PingAuthorize Policy Editor, go to the Policies tab.
-
Select Users viewing shared memes.
-
Click Add Rule.
-
For the name, replace Untitled with
Allow people outside of Youngstown
. -
For Effect, select Permit.
-
To specify a Condition, perform the following steps:
-
Click Comparison.
-
From the Select an Attribute list, select TokenOwner.city.
-
In the second field, select Does Not Equal.
-
In the third field, type
Youngstown
.
-
-
Click Save changes.
Result:
You have a rule that allows users from outside Youngstown.
Testing that the policy blocks Youngstown users
You can test the new rule with cURL or Postman.
Steps
-
Issue a GET request to
https://localhost:7443/meme-game/api/v1/users/user.0/answers/1
asuser.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": {} }
-
Issue a GET request to
https://localhost:7443/meme-game/api/v1/users/user.0/answers/1
asuser.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
-
From the PingAuthorize Policy Editor, go to Trust Framework and click Attributes.
-
From the menu, select Add new Attribute.
-
For the name, replace Untitled with
Meme game answer rating
. -
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.
-
Click the next to Resolvers and click Add Resolver.
-
For Resolver type, select Attribute and specify a value of HttpRequest.ResponseBody.
-
Click the next to Value Processors and click Add Processor.
-
For Processor, select JSON Path and specify a value of
$.data.attributes.rating
. -
For the processor’s Value type, select Number.
-
For Value Settings, set the Type to Number.
-
Click Save changes.
Result:
You have a new attribute for the answer’s age rating.
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
-
From the PingAuthorize Policy Editor, go to the Policies tab.
-
Select Users viewing shared memes.
-
Click Add Rule.
-
For the name, replace Untitled with
Anyone can view family-friendly answers
. -
For Effect, select Permit.
-
Specify a Condition.
-
Click Comparison.
-
From the Select an Attribute list, select Meme game answer rating.
-
In the second field, select Less Than.
-
In the third field, type
13
.
-
-
Click Save changes.
Result:
Your new rule to allow family-friendly memes should look like the following image.
Testing that the policy blocks Youngstown users from viewing age 13+ memes
You can test the newly created rule with cURL or Postman.
Steps
-
Issue a GET request to
https://localhost:7443/meme-game/api/v1/users/user.0/answers/2
asuser.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 a200 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": {} }
-
Issue a GET request to
https://localhost:7443/meme-game/api/v1/users/user.0/answers/2
asuser.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 a403 Forbidden
response with the following body.{ "errorMessage": "Access Denied", "status": 403 }
-
Issue a GET request to
https://localhost:7443/meme-game/api/v1/users/user.0/answers/1
asuser.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 a200 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": {} }
-
Issue a GET request to
https://localhost:7443/meme-game/api/v1/users/user.0/answers/1
asuser.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 a403 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
-
In the PingAuthorize Policy Editor, go to Trust Framework and click Attributes.
-
Select Meme game answer rating.
-
For Value Settings, select the
Default Value
checkbox, and specify a value of0
. -
Click Save changes.
Result:
Your attribute for answer age ratings has a default value of 0, as shown in the following image.
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
asuser.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
-
From the PingAuthorize Policy Editor, go to the Policies tab.
-
Select Users viewing shared memes.
-
Click Statements.
-
Click Add Statement and select Denied Reason.
-
For the name, replace Untitled with
Send "not permitted" error
. -
From the Applies to drop-down list, select Deny.
-
For a Payload value, enter
\{"status": 451, "message": "Restricted", "detail": "Not permitted per regulation"}
. -
Click Save changes.
Result:
You have a new statement, which looks something like the following image.
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
asuser.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.