{"id":13787873,"url":"https://github.com/anderseknert/clj-opa","last_synced_at":"2025-03-22T07:32:11.077Z","repository":{"id":62431478,"uuid":"327662956","full_name":"anderseknert/clj-opa","owner":"anderseknert","description":"Clojure client for Open Policy Agent","archived":false,"fork":false,"pushed_at":"2024-01-18T11:17:48.000Z","size":30,"stargazers_count":11,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-14T20:50:41.533Z","etag":null,"topics":["authorization","authorization-middleware","clj-opa","compojure","middleware","opa","open-policy-agent","rego","ring"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anderseknert.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-07T16:13:50.000Z","updated_at":"2024-07-14T13:11:21.000Z","dependencies_parsed_at":"2024-01-18T12:38:55.270Z","dependency_job_id":"c380f84e-88f4-4acb-9e18-4147fcc35349","html_url":"https://github.com/anderseknert/clj-opa","commit_stats":{"total_commits":39,"total_committers":2,"mean_commits":19.5,"dds":0.02564102564102566,"last_synced_commit":"94ece1282242094f7498671606c12e1434bdeb9d"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderseknert%2Fclj-opa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderseknert%2Fclj-opa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderseknert%2Fclj-opa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderseknert%2Fclj-opa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anderseknert","download_url":"https://codeload.github.com/anderseknert/clj-opa/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244925175,"owners_count":20532873,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authorization","authorization-middleware","clj-opa","compojure","middleware","opa","open-policy-agent","rego","ring"],"created_at":"2024-08-03T21:00:32.603Z","updated_at":"2025-03-22T07:32:10.795Z","avatar_url":"https://github.com/anderseknert.png","language":"Clojure","funding_links":[],"categories":["Language and Platform Integrations"],"sub_categories":["Clojure"],"readme":"# clj-opa\n\n[![Clojars Project](https://img.shields.io/clojars/v/clj-opa.svg)](https://clojars.org/clj-opa)\n\nRing middleware for app authorization using [Open Policy Agent](https://www.openpolicyagent.org/) (OPA). \n\n## Install\n\nTo install, add the following to your project `:dependencies`\n\n```\n[clj-opa \"0.1.1\"]\n```\n\n## Usage\n\n#### wrap-opa-authorize\n\nThe `wrap-opa-authorize` middleware will intercept any incoming request and delegate the authorization decision to OPA.\nBy default, the `wrap-opa-authorize` middleware expects a response containing an `allow` attribute equal to `true` or \nelse the `:enforce-fn` will be called, which in the default configuration serves a **403 Forbidden** to the caller.\n\nExample configuration using [Compojure](https://github.com/weavejester/compojure) for routing:\n\n```clojure\n(ns my.app\n  (:require [compojure.core :refer [defroutes ANY]]\n            [ring.middleware.defaults :refer [wrap-defaults api-defaults]] \n            [opa.middleware :refer [wrap-opa-authorize]]))\n\n(defroutes app-routes \n           (ANY \"/public\" [] \"Public endpoint\")\n           (ANY \"/*\" [] \"Protected endpoint\"))\n\n(def app (-\u003e (wrap-defaults app-routes api-defaults)\n             (wrap-opa-authorize)))\n```\n\nExample Rego policy that allows `GET` requests on the `/public` endpoint:\n\n```rego\npackage policy\n\ndefault allow := false\n\nallow {\n    input.method == \"GET\"\n    input.path[0] == \"public\"\n}\n```\n\n##### Configuration\n\nThe `wrap-opa-authorize` function takes a configuration map as an optional argument, offering the following options:\n\n| Option | Description | Default |\n|--------|-------------|---------|\n| `:server-addr` | Base URL of OPA server | http://localhost:8181 |\n| `:policy-path` | Path to policy document (excluding `/v1/data`) | /policy |\n| `:input-fn`    | Function taking request and returning the input map to provide OPA | See below |\n| `:enforce-fn`  | Function to call when authorization fails | Function returning a 403 Forbidden response |\n\nUnless a custom `:input-fn` is provided, an input map containing the path components (split on /), the request method \nand - if found in the request authorization header - a bearer token to use for authN/authZ:\n\n```json\n{\n  \"path\": [\"public\", \"assets\", \"images\"],\n  \"method\": \"GET\",\n  \"token\": \"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbmRlcnNla25lcnQifQ.odt_88oGgYHoYU2SRdMjLYA0aG-NXDyeYqj_x9voAa4\"\n}\n```\n**Note** that the input map should _not_ contain the `input` attribute itself - that is added by the middleware.\n\nIf you would like to change or extend the default input map with custom attributes, you may do so by calling the \n`opa.middleware/default-input-fn` and change the result to your liking.\n\nExample, custom configuration:\n\n```clojure\n(def app \n  (-\u003e (wrap-defaults app-routes api-defaults)\n      ; Query the OPA server at `https://opa.example.com` for authorization decisions on the \n      ; `/authz/policy/rules` path with input built from a custom request header `\"X-Username\"`\n      ; and with and enforcement function that returns a 401 Unauthorized response on failures \n      ; and forwards the response body from OPA to the caller.\n      (wrap-opa-authorize {:server-addr \"https://opa.example.com\"\n                           :policy-path \"/authz/policy/rules\"\n                           :input-fn (fn [request] {:username (get-in request [:headers :x-username])})\n                           :enforce-fn (fn [opa-response] {:status 401 :body opa-response})})))\n```\n\n#### Other utilities and helper functions\n\nSee the [API reference](https://cljdoc.org/d/clj-opa/clj-opa/0.1.0/doc/readme).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanderseknert%2Fclj-opa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanderseknert%2Fclj-opa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanderseknert%2Fclj-opa/lists"}