OPA for HTTP Authorization

Open Policy Agent[1] is a promising, light weight and very generic policy engine to govern authorization is any type of domain. I found this comparion[2] very attractive in evaluating OPA for a project I am currently working on, where they demonstrate how OPA can cater same functionality defined in RBAC, RBAC with Seperation of Duty, ABAC and XACML.  

Here are the steps to a brief demonstration of OPA used for HTTP API authorization based on the sample [3], taking it another level up.

Running OPA Server

First we need to download OPA from [4], based on the operating system we are running on. 
For linux, 
curl -L -o opa https://github.com/open-policy-agent/opa/releases/download/v0.10.3/opa_linux_amd64 
Make it executable,
chmod 755 ./opa
Once done, we can start OPA policy engine as a server.
./opa run --server

Define Data and Rules

Next we need to load data and authorization rules to the server, so it can make decisions. OPA defines these in files in the format of .rego. Below is a sample file I used.
package httpapi.authz

subordinates = {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]}

# HTTP API request
import input as http_api
# http_api = {
#   "path": ["finance", "salary", "alice"],
#   "user": "alice",
#   "method": "GET"
#   "user_agent": "cURL/1.0"
#   "remote_addr": "127.0.0.1"
# }

default allow = false

# Allow users to get their own salaries.
allow {
  http_api.method = "GET"
  http_api.path = ["finance", "salary", username]
  username = http_api.user
}

# Allow managers to get their subordinates' salaries.
allow {
  http_api.method = "GET"
  http_api.path = ["finance", "salary", username]
  subordinates[http_api.user][_] = username
}

# Allow managers to edit their subordinates' salaries only if the request came
# from user agent cURL and address 127.0.0.1.
allow {
  http_api.method = "POST"
  http_api.path = ["finance", "salary", username]
  subordinates[http_api.user][_] = username
  http_api.remote_addr = "127.0.0.1"
  http_api.user_agent = "curl/7.47.0"
}

At first it defines a data set, which represents the relationship subordinates. For example as per this dataset, alice is a subordinate of bob. Then it defines 3 rules that will give feedback as 'allow'.
  • If user tries to get own salary it is allowed.
  • If a user tries to get the salary of a subordinate it is allowed.
  • If a user tries to modify the salary, it is allowed only if it is of a subordinate, request is initiated from remote address '127.0.0.1' and user agent 'curl/7.47.0'
To load this policy into the OPA engine we use below call.
curl -X PUT --data-binary @salary-example.rego  localhost:8181/v1/policies/example
The above policy is stored into a file named 'salary-example.rego' and referred in the above command.

Evaluate at API Invocation

Below is a sample API implementation in python, that consults the OPA engine on the decision whether to provide a response or deny as unauthorized.

#!/usr/bin/env python

import base64
import os

from flask import Flask
from flask import request
import json
import requests

import logging
import sys
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

app = Flask(__name__)

opa_url = os.environ.get("OPA_ADDR", "http://localhost:8181")
policy_path = os.environ.get("POLICY_PATH", "/v1/data/httpapi/authz")

def check_auth(url, user, method, user_agent, remote_addr,url_as_array, token):
    input_dict = {"input": {
        "user": user,
        "path": url_as_array,
        "method": method,
    "user_agent": user_agent,
    "remote_addr": remote_addr
    }}
    if token is not None:
        input_dict["input"]["token"] = token

    logging.info("Checking auth...")
    logging.info(json.dumps(input_dict, indent=2))
    try:
        rsp = requests.post(url, data=json.dumps(input_dict))
    except Exception as err:
        logging.info(err)
        return {}
    if rsp.status_code >= 300:
        logging.info("Error checking auth, got status %s and message: %s" % (j.status_code, j.text))
        return {}
    j = rsp.json()
    logging.info("Auth response:")
    logging.info(json.dumps(j, indent=2))
    return j

@app.route('/', defaults={'path': ''}, methods = ['GET', 'POST', 'DELETE'])
@app.route('/<path:path>', methods = ['GET', 'POST'])
def root(path):
    user_encoded = request.headers.get('Authorization', "Anonymous:none")
    logging.info("User Agent: %s" % request.user_agent.string)
    logging.info("Remote Address: %s" % request.remote_addr)
    if user_encoded:
        user_encoded = user_encoded.split("Basic ")[1]
    user, _ = base64.b64decode(user_encoded).decode("utf-8").split(":")
    url = opa_url + policy_path
    path_as_array = path.split("/")
    token = request.args["token"] if "token" in request.args else None
    j = check_auth(url, user, request.method, request.user_agent.string, request.remote_addr, path_as_array, token).get("result", {})
    if j.get("allow", False) == True:
        return "Success: user %s is authorized \n" % user
    return "Error: user %s is not authorized to %s url /%s \n" % (user, request.method, path)

if __name__ == "__main__":
    app.run()

The function 'check_auth' is responsible to retreive the decision from OPA engine, providing the input details required for authorization. Run the above python script with below command. It uses python modules 'flask' and 'request'.

python echo_server.py
Now we can try to call this API served by this python server and see the authorization policy in action.
curl --user alice:password localhost:5000/finance/salary/alice
Above is allowed based on the 1st rule, user trying to read own salary.
curl --user bob:password localhost:5000/finance/salary/alice
Above is allowed based on the 2nd rule, user trying to read the salary of a subordinate.
curl -X POST -d "empoyeeID=100&value=2000" --user bob:password localhost:5000/finance/salary/alice
This will be allowed based on the 3rd rule, if the user agent also matches the exact same cURL client version we have defined in the policy.
curl -X POST -d "empoyeeID=100&value=2000" --user bob:password localhost:5000/finance/salary/alice
 Even though the previous request was allowed for bob to edit alice's salary, the above request is failed as a user cannot modify own salary based on the defined rule.

Comments

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. This comment has been removed by a blog administrator.

    ReplyDelete
  8. This comment has been removed by a blog administrator.

    ReplyDelete
  9. This comment has been removed by a blog administrator.

    ReplyDelete
  10. Thanks for sharing Information to us. If someone wants to know about,I think this is the right place for you!

    mobile app development in coimbatore
    mobile app development company in atlanta
    chatbot development company

    ReplyDelete
  11. A befuddling web diary I visit this blog, it's incredibly grand. Strangely, in this present blog's substance made motivation behind fact and sensible. The substance of information is instructive
    Oracle Fusion Financials Online Training
    Oracle Fusion HCM Online Training
    Oracle Fusion SCM Online Training

    ReplyDelete
  12. Thank you for excellent article.You made an article that is interesting.
    Tavera car for rent in coimbatore|Indica car for rent in coimbatore|innova car for rent in coimbatore|mini bus for rent in coimbatore|tempo traveller for rent in coimbatore|kodaikanal tour package from chennai

    Keep on the good work and write more article like this...

    Great work !!!!Congratulations for this blog

    ReplyDelete
  13. An overwhelming web journal I visit this blog, it's unfathomably amazing. Unusually, in this present blog's substance made inspiration driving truth and reasonable. The substance of data is enlightening
    Oracle Fusion Financials Online Training
    Oracle Fusion HCM Online Training
    Oracle Fusion SCM Online Training

    ReplyDelete
  14. Wow, amazing blog layout! How long have you been blogging for? you make blogging look easy. The overall look of your website is fantastic, let alone the content!

    3D animation Company
    Chatbot Development Company
    Android app development in Coimbatore

    ReplyDelete
  15. That was really helpful. thanks for sharing.

    ReplyDelete
  16. I think SQL and SOAP API both together provide solutions to some of very complex IT problems.

    SQL Server Load Soap Api

    ReplyDelete
  17. Beautiful detailed and sensitive position. I can not forget to read such a useful article about the art of researchers in general. Rather cool to check that your message has been added.

    DedicatedHosting4u.com

    ReplyDelete

Post a Comment

Popular posts from this blog

Signing SOAP Messages - Generation of Enveloped XML Signatures

How to send an HTML email in Java (Using Google SMTP Server)

How to convert WSDL to Java