├── .docker ├── Dockerfile-alpine ├── Dockerfile-build └── Dockerfile-distroless-static ├── .docker_compose ├── config.yaml ├── jwks.json └── rules.json ├── .dockerignore ├── .gitattributes ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── BUG-REPORT.yml │ ├── DESIGN-DOC.yml │ ├── FEATURE-REQUEST.yml │ └── config.yml ├── auto_assign.yml ├── config.yml ├── labels.json ├── pull_request_template.md └── workflows │ ├── ci.yml │ ├── closed_references.yml │ ├── codeql-analysis.yml │ ├── conventional_commits.yml │ ├── cve-scan.yaml │ ├── format.yml │ ├── labels.yml │ ├── licenses.yml │ ├── milestone.yml │ ├── pm.yml │ └── stale.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .mailmap ├── .nancy-ignore ├── .orycli.yml ├── .prettierignore ├── .reference-ignore ├── .reports └── dep-licenses.csv ├── .schema ├── README.md ├── config.schema.json ├── openapi │ ├── gen.go.yml │ ├── gen.typescript.yml │ ├── patches │ │ ├── health.yaml │ │ └── meta.yaml │ └── templates │ │ └── go │ │ ├── .travis.yml │ │ ├── README.mustache │ │ ├── api.mustache │ │ ├── api_doc.mustache │ │ ├── client.mustache │ │ ├── configuration.mustache │ │ ├── git_push.sh.mustache │ │ ├── gitignore.mustache │ │ ├── go.mod.mustache │ │ ├── go.sum │ │ ├── model.mustache │ │ ├── model_anyof.mustache │ │ ├── model_doc.mustache │ │ ├── model_enum.mustache │ │ ├── model_oneof.mustache │ │ ├── model_simple.mustache │ │ ├── nullable_model.mustache │ │ ├── openapi.mustache │ │ ├── partial_header.mustache │ │ ├── response.mustache │ │ ├── signing.mustache │ │ └── utils.mustache └── version.schema.json ├── .schemas ├── README.md ├── authenticators.anonymous.schema.json ├── authenticators.cookie_session.schema.json ├── authenticators.jwt.schema.json ├── authenticators.noop.schema.json ├── authenticators.oauth2_client_credentials.schema.json ├── authenticators.oauth2_introspection.schema.json ├── authenticators.unauthorized.schema.json ├── authorizers.allow.schema.json ├── authorizers.deny.schema.json ├── authorizers.keto_engine_acp_ory.schema.json ├── authorizers.remote_json.schema.json ├── mutators.cookie.schema.json ├── mutators.header.schema.json ├── mutators.hydrator.schema.json ├── mutators.id_token.schema.json ├── mutators.noop.schema.json └── scope_strategy.schema.json ├── AUTHORS ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── UPGRADE.md ├── api ├── credential.go ├── credential_doc.go ├── credential_test.go ├── decision.go ├── decision_test.go ├── health.go ├── health_test.go ├── rule.go ├── rule_doc.go └── rule_test.go ├── cmd ├── clidoc │ ├── generate.go │ └── main.go ├── credentials.go ├── credentials_generate.go ├── health.go ├── health_alive.go ├── health_ready.go ├── helpers.go ├── root.go ├── root_test.go ├── rules.go ├── rules_get.go ├── rules_list.go ├── serve.go ├── server │ ├── banner.go │ └── server.go └── version.go ├── codecov.yml ├── contrib └── grafana │ └── Oathkeeper-Dashboard.json ├── credentials ├── fetcher.go ├── fetcher_default.go ├── fetcher_default_test.go ├── signer.go ├── signer_default.go ├── signer_default_integration_test.go ├── signer_default_test.go ├── testdata │ └── TestFetcherDefault │ │ ├── name=should_fetch_from_azure_object_storage-azblob.replay │ │ ├── name=should_fetch_from_gs_object_storage-gs.replay │ │ └── name=should_fetch_from_s3_object_storage-s3.replay ├── verifier.go ├── verifier_default.go └── verifier_default_test.go ├── doc.go ├── doc_swagger.go ├── docker-compose.yml ├── docs ├── README.md └── sidebar.json ├── driver ├── configuration │ ├── config_keys.go │ ├── provider.go │ ├── provider_koanf.go │ ├── provider_koanf_private_test.go │ └── provider_koanf_public_test.go ├── driver.go ├── driver_default.go ├── registry.go ├── registry_memory.go └── registry_memory_test.go ├── go.mod ├── go.sum ├── go_mod_shadow.go ├── helper ├── bearer.go ├── bearer_test.go └── errors.go ├── install.sh ├── internal ├── cloudstorage │ ├── setup.go │ └── useragent.go ├── config │ └── .oathkeeper.yaml ├── driver.go └── httpclient │ ├── client │ ├── api │ │ ├── api_client.go │ │ ├── decisions_parameters.go │ │ ├── decisions_responses.go │ │ ├── get_rule_parameters.go │ │ ├── get_rule_responses.go │ │ ├── get_well_known_json_web_keys_parameters.go │ │ ├── get_well_known_json_web_keys_responses.go │ │ ├── list_rules_parameters.go │ │ └── list_rules_responses.go │ ├── health │ │ ├── health_client.go │ │ ├── is_instance_alive_parameters.go │ │ ├── is_instance_alive_responses.go │ │ ├── is_instance_ready_parameters.go │ │ └── is_instance_ready_responses.go │ ├── ory_oathkeeper_client.go │ └── version │ │ ├── get_version_parameters.go │ │ ├── get_version_responses.go │ │ └── version_client.go │ └── models │ ├── generic_error.go │ ├── health_not_ready_status.go │ ├── health_status.go │ ├── json_web_key.go │ ├── json_web_key_set.go │ ├── rule.go │ ├── rule_handler.go │ ├── rule_match.go │ ├── unexpected_error.go │ ├── upstream.go │ ├── uuid.go │ └── version.go ├── main.go ├── metrics ├── middleware.go ├── middleware_test.go └── prometheus.go ├── middleware ├── definitions.go ├── grpc_middleware.go ├── grpc_middleware_test.go ├── grpc_mock_server_test.go └── helpers_test.go ├── openapitools.json ├── package-lock.json ├── package.json ├── pipeline ├── authn │ ├── authenticator.go │ ├── authenticator_anonymous.go │ ├── authenticator_anonymous_test.go │ ├── authenticator_bearer_token.go │ ├── authenticator_bearer_token_test.go │ ├── authenticator_cookie_session.go │ ├── authenticator_cookie_session_test.go │ ├── authenticator_jwt.go │ ├── authenticator_jwt_test.go │ ├── authenticator_noop.go │ ├── authenticator_noop_test.go │ ├── authenticator_oauth2_client_credentials.go │ ├── authenticator_oauth2_client_credentials_cache_test.go │ ├── authenticator_oauth2_client_credentials_test.go │ ├── authenticator_oauth2_introspection.go │ ├── authenticator_oauth2_introspection_cache_test.go │ ├── authenticator_oauth2_introspection_test.go │ ├── authenticator_test.go │ ├── authenticator_unauthorized.go │ ├── authenticator_unauthorized_test.go │ └── registry.go ├── authz │ ├── authorizer.go │ ├── authorizer_allow.go │ ├── authorizer_allow_test.go │ ├── authorizer_deny.go │ ├── authorizer_deny_test.go │ ├── keto_engine_acp_ory.go │ ├── keto_engine_acp_ory_test.go │ ├── registry.go │ ├── remote.go │ ├── remote_json.go │ ├── remote_json_test.go │ ├── remote_test.go │ └── utils.go ├── error.go ├── errors │ ├── error.go │ ├── error_json.go │ ├── error_json_test.go │ ├── error_redirect.go │ ├── error_redirect_test.go │ ├── error_www_authenticate.go │ ├── error_www_authenticate_test.go │ ├── registry.go │ ├── when.go │ └── when_test.go ├── mutate │ ├── mutator.go │ ├── mutator_broken.go │ ├── mutator_broken_test.go │ ├── mutator_cookie.go │ ├── mutator_cookie_test.go │ ├── mutator_header.go │ ├── mutator_header_test.go │ ├── mutator_hydrator.go │ ├── mutator_hydrator_test.go │ ├── mutator_id_token.go │ ├── mutator_id_token_test.go │ ├── mutator_noop.go │ ├── mutator_noop_test.go │ └── registry.go └── rule.go ├── proxy ├── proxy.go ├── proxy_test.go ├── request_handler.go ├── request_handler_test.go └── response_writer.go ├── rule ├── doc.go ├── engine_glob.go ├── engine_glob_test.go ├── engine_regexp.go ├── engine_regexp_test.go ├── fetcher.go ├── fetcher_default.go ├── fetcher_default_test.go ├── matcher.go ├── matcher_test.go ├── matching_engine.go ├── registry.go ├── repository.go ├── repository_memory.go ├── repository_test.go ├── rule.go ├── rule_migrator.go ├── rule_migrator_test.go ├── rule_test.go ├── testdata │ ├── TestFetchRulesFromObjectStorage-azblob.replay │ ├── TestFetchRulesFromObjectStorage-gs.replay │ └── TestFetchRulesFromObjectStorage-s3.replay ├── validator.go └── validator_test.go ├── scripts ├── .gitattributes ├── render-schemas.sh ├── run-format.sh ├── run-genswag.sh ├── run-mock.sh └── test-format.sh ├── spec ├── api.json ├── config.schema.json ├── embed.go ├── pipeline │ ├── authenticators.anonymous.schema.json │ ├── authenticators.bearer_token.schema.json │ ├── authenticators.cookie_session.schema.json │ ├── authenticators.jwt.schema.json │ ├── authenticators.noop.schema.json │ ├── authenticators.oauth2_client_credentials.schema.json │ ├── authenticators.oauth2_introspection.schema.json │ ├── authenticators.unauthorized.schema.json │ ├── authorizers.allow.schema.json │ ├── authorizers.deny.schema.json │ ├── authorizers.keto_engine_acp_ory.schema.json │ ├── authorizers.remote.schema.json │ ├── authorizers.remote_json.schema.json │ ├── errors.json.schema.json │ ├── errors.redirect.schema.json │ ├── errors.www_authenticate.schema.json │ ├── mutators.cookie.schema.json │ ├── mutators.header.schema.json │ ├── mutators.hydrator.schema.json │ ├── mutators.id_token.schema.json │ └── mutators.noop.schema.json └── swagger.json ├── test ├── bearer-token │ ├── .gitignore │ ├── config.yaml │ ├── okapi │ │ └── main.go │ ├── rules.1.json │ └── run.sh ├── e2e │ ├── .gitignore │ ├── config.yml │ ├── e2e-rules.json │ ├── jwks-authn.json │ ├── jwks-idt.json │ ├── okapi │ │ └── main.go │ ├── okclient │ │ └── main.go │ └── run.sh ├── forwarded-header │ ├── .gitignore │ ├── config.yaml │ ├── rules.1.json │ └── run.sh ├── reload │ ├── .gitignore │ ├── config.1.yaml │ ├── config.2.yaml │ ├── config.3.yaml │ ├── config.4.yaml │ ├── config.5.yaml │ ├── config.6.yaml │ ├── rules.1.json │ ├── rules.2.json │ ├── rules.3.1.json │ ├── rules.3.2.json │ └── run.sh ├── stub │ ├── jwks-ecdsa.json │ ├── jwks-hs.json │ ├── jwks-rsa-multiple.json │ ├── jwks-rsa-single.json │ ├── rules.json │ └── rules.yaml └── update │ ├── config_default.yaml │ ├── config_error.yaml │ ├── config_glob.yaml │ ├── config_no_repo.yaml │ ├── config_regexp.yaml │ └── rules_glob.yaml └── x ├── compare.go ├── deepcopy.go ├── deepcopy_test.go ├── header └── header.go ├── registry.go ├── router.go ├── template.go ├── testhelper.go ├── url.go └── version.go /.docker/Dockerfile-alpine: -------------------------------------------------------------------------------- 1 | FROM alpine:3.21.2 2 | 3 | RUN addgroup -S ory; \ 4 | adduser -S ory -G ory -D -H -s /bin/nologin 5 | RUN apk --no-cache --update-cache --upgrade add ca-certificates 6 | 7 | COPY oathkeeper /usr/bin/oathkeeper 8 | 9 | USER ory 10 | 11 | EXPOSE 4455 12 | EXPOSE 4456 13 | 14 | ENTRYPOINT ["oathkeeper"] 15 | CMD ["serve"] 16 | -------------------------------------------------------------------------------- /.docker/Dockerfile-build: -------------------------------------------------------------------------------- 1 | # Workaround for https://github.com/GoogleContainerTools/distroless/issues/1342 2 | FROM golang:1.22-bullseye AS builder 3 | 4 | WORKDIR /go/src/github.com/ory/oathkeeper 5 | 6 | RUN apt-get update && apt-get upgrade -y 7 | 8 | COPY go.mod go.mod 9 | COPY go.sum go.sum 10 | 11 | ENV CGO_ENABLED=0 12 | ENV GO111MODULE=on 13 | 14 | RUN go mod download 15 | 16 | COPY . . 17 | 18 | RUN go build -o /usr/bin/oathkeeper . 19 | 20 | ######################### 21 | 22 | FROM gcr.io/distroless/static-debian12:nonroot AS runner 23 | 24 | COPY --from=builder --chown=nonroot:nonroot /usr/bin/oathkeeper /usr/bin/oathkeeper 25 | 26 | EXPOSE 4455 4456 27 | 28 | ENTRYPOINT ["oathkeeper"] 29 | CMD ["serve"] 30 | -------------------------------------------------------------------------------- /.docker/Dockerfile-distroless-static: -------------------------------------------------------------------------------- 1 | FROM gcr.io/distroless/static-debian12:nonroot 2 | 3 | COPY --chown=nonroot:nonroot oathkeeper /usr/bin/oathkeeper 4 | EXPOSE 4455 4456 5 | 6 | ENTRYPOINT ["oathkeeper"] 7 | CMD ["oathkeeper"] 8 | -------------------------------------------------------------------------------- /.docker_compose/config.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | proxy: 3 | port: 4455 # run the proxy at port 4455 4 | api: 5 | port: 4456 # run the api at port 4456 6 | 7 | access_rules: 8 | repositories: 9 | - file:///etc/config/oathkeeper/rules.json 10 | 11 | errors: 12 | fallback: 13 | - json 14 | handlers: 15 | json: 16 | enabled: true 17 | config: 18 | verbose: true 19 | redirect: 20 | enabled: true 21 | config: 22 | to: https://www.ory.sh/docs 23 | 24 | mutators: 25 | header: 26 | enabled: true 27 | config: 28 | headers: 29 | X-User: "{{ print .Subject }}" 30 | noop: 31 | enabled: true 32 | id_token: 33 | enabled: true 34 | config: 35 | issuer_url: http://localhost:4455/ 36 | jwks_url: file:///etc/config/oathkeeper/jwks.json 37 | 38 | authorizers: 39 | allow: 40 | enabled: true 41 | deny: 42 | enabled: true 43 | 44 | authenticators: 45 | anonymous: 46 | enabled: true 47 | config: 48 | subject: guest 49 | -------------------------------------------------------------------------------- /.docker_compose/jwks.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "use": "sig", 5 | "kty": "RSA", 6 | "kid": "387e4978-078b-4664-afb4-cf9142161610", 7 | "alg": "RS256", 8 | "n": "qY_EWBYkXX-8RrH_5tBFTfaEf2uloKC0HJePxf1WQ4qIh1IjcjyHCpOK8dvyfBZtNcCKHa_EQZwupWuIuZtuXGzfmVHYkrlTwbDK6juqyqhrtpQHm-wOaSCjD4hvegD50Cpb5qKm59ZssWRPuQ6AxJWJ-D1MlmNntTI0L3pJd_Np0od-A2SwAczk53gg2V7Zhk87h7dQKHvkbd3e86lzW7FjV8BsUvp2tKxg_ULfEJJYpyjxqIwaDFIUY4qsCrSS6XETOETxBcE2lAQtflPqPlqQIrNra8k9wAxB_OBNIeonmHbM3bI2H6KOcJUWzm72ZjbIfVfrTPsijH-ZhNwjbw", 9 | "e": "AQAB", 10 | "d": "FtEs17mrDRXqTQ0Y5YNzQAmDTO48bIATnKFcjIUJva7_rk4ETRQODANMuD0jxUTzTz9olpQXccjFkicFUAy1biSMdkJIRX5A4hibRaff1MOTMw96cqXyTn1A1A9FCQLmmveIRGHw2dPF7p0UCVAFTe7dkRUWoEoiI4Ts9tKa3lQf8SyWmTuXAMkwTmOXH1ARCCJ0CXgITg5t9_pajn1mX3yCnnrbqQTlYvQ2pkJIqbmxYCAzm6LZpj9u1XFmPLk8IkbPDahdI28bqv7PJgaVRxchlJ8JaP_YzQW1m9xme6PJNhVOcKYlF8L-PgB6gfLUdwNCT1v1MA-WPHLSZbqvQQ", 11 | "p": "xy25H7yYa1m1H09fm9msoaPaCY3cTvF89sJqUzoGCpMHUgPBI276kZgVHPcfp8Fzbq-a_c9BnU11vJ_MV7_kYHt3JM1RTjsK1-JMvDSqNTKWy3qSAmoN2gKKUp4fRG1BskG45QLyj-smesW_7oJoEtPoe_AQ3U47mrrarayb_yE", 12 | "q": "2e8S8IankdTvmBcRetd8kGo1cblpKZ6a9FWbAjkS0ts3fXzTeiBGa_sw5QemIrpPy2fRp3OBMn0NIw2ONbFipgjBqj72Oa-87WTdb7IsH0x8XkgHdUFxBmsU4vzQAKno58WdWY8zP0YLVL-u2ricmsX0gV2EsdvkTPpINaXEoI8", 13 | "dp": "XHLhkVSFXpZ11kGOPBWN5jzaUELzNgUqnpJgrZ6p_TB_Xlb1x4-UaA2yBw7BN6k3_fEuPI59gxjYBCQbwcMEqq_D_mX6ThhjkQ6t1VGQiz6e9XU_3jUBluZE89IG60jXDHkq68kxcxGPe77btkX7LnoDV7t26HGOguQl6iTLB2E", 14 | "dq": "dfQmzRYkdhLJBwldRZ6B5ewGNyJCH-ufNKVsu1xGqudJdlrsXwo-80zGXv-v1NYAQDhVygsDH199j75TfQ4gNXtBzrI7NGfAmsBf9Yd6yAnuulzD5Jvh37ZvXJe2wNU1oNRdYM7XzuRLV7hTnEAVStPfjXEfU-CPBXblRFwPO1M", 15 | "qi": "IfUeLw9BK-4Oby0kPOk3u51D-6GfCAqc9rrYI118vi9deKH3lJUn6G9bZYh6kSA8qjK6gRfSxrjICz-IrGfanYQ-E1zm_Dx6vaa6OXVBMYtpduDaeliGiivQW1HnSWS67UQSV9qK0stUa1epcbpsQBtwhUldSPZezfCpPiUjIPw" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.docker_compose/rules.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "allow-anonymous-with-header-mutator", 4 | "upstream": { 5 | "url": "https://httpbin.org/anything/header" 6 | }, 7 | "match": { 8 | "url": "http://<127.0.0.1|localhost>:4455/anything/header", 9 | "methods": ["GET"] 10 | }, 11 | "authenticators": [ 12 | { 13 | "handler": "anonymous" 14 | } 15 | ], 16 | "authorizer": { 17 | "handler": "allow" 18 | }, 19 | "mutators": [ 20 | { 21 | "handler": "header", 22 | "config": { 23 | "headers": { 24 | "X-User": "{{ print .Subject }}" 25 | } 26 | } 27 | } 28 | ] 29 | }, 30 | { 31 | "id": "deny-anonymous", 32 | "upstream": { 33 | "url": "https://httpbin.org/anything/deny" 34 | }, 35 | "match": { 36 | "url": "http://<127.0.0.1|localhost>:4455/anything/deny", 37 | "methods": ["GET"] 38 | }, 39 | "authenticators": [ 40 | { 41 | "handler": "anonymous" 42 | } 43 | ], 44 | "authorizer": { 45 | "handler": "deny" 46 | }, 47 | "mutators": [ 48 | { 49 | "handler": "noop" 50 | } 51 | ], 52 | "errors": [ 53 | { 54 | "handler": "json", 55 | "config": { 56 | "when": [ 57 | { 58 | "request": { 59 | "header": { 60 | "accept": ["application/json"] 61 | } 62 | } 63 | } 64 | ] 65 | } 66 | }, 67 | { 68 | "handler": "redirect", 69 | "config": { 70 | "when": [ 71 | { 72 | "request": { 73 | "header": { 74 | "accept": ["text/*"] 75 | } 76 | } 77 | } 78 | ] 79 | } 80 | } 81 | ] 82 | }, 83 | { 84 | "id": "allow-anonymous-with-id-token-mutator", 85 | "upstream": { 86 | "url": "https://httpbin.org/anything/id_token" 87 | }, 88 | "match": { 89 | "url": "http://<127.0.0.1|localhost>:4455/anything/id_token", 90 | "methods": ["GET"] 91 | }, 92 | "authenticators": [ 93 | { 94 | "handler": "anonymous" 95 | } 96 | ], 97 | "authorizer": { 98 | "handler": "allow" 99 | }, 100 | "mutators": [ 101 | { 102 | "handler": "id_token" 103 | } 104 | ] 105 | } 106 | ] 107 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | node_modules 3 | dist/ 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | sdk/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @aeneasr @ory/product-development 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/FUNDING.yml 3 | 4 | # These are supported funding model platforms 5 | 6 | # github: 7 | patreon: _ory 8 | open_collective: ory 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/config.yml 3 | 4 | blank_issues_enabled: false 5 | contact_links: 6 | - name: Ory Oathkeeper Forum 7 | url: https://github.com/ory/oathkeeper/discussions 8 | about: 9 | Please ask and answer questions here, show your implementations and 10 | discuss ideas. 11 | - name: Ory Chat 12 | url: https://www.ory.sh/chat 13 | about: 14 | Hang out with other Ory community members to ask and answer questions. 15 | -------------------------------------------------------------------------------- /.github/auto_assign.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/auto_assign.yml 3 | 4 | # Set to true to add reviewers to pull requests 5 | addReviewers: true 6 | 7 | # Set to true to add assignees to pull requests 8 | addAssignees: true 9 | 10 | # A list of reviewers to be added to pull requests (GitHub user name) 11 | assignees: 12 | - ory/maintainers 13 | 14 | # A number of reviewers added to the pull request 15 | # Set 0 to add all the reviewers (default: 0) 16 | numberOfReviewers: 0 17 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/config.yml 3 | 4 | todo: 5 | keyword: "@todo" 6 | label: todo 7 | -------------------------------------------------------------------------------- /.github/labels.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "package/proxy", 4 | "color": "0A28FD", 5 | "aliases": [] 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | ## Related issue(s) 14 | 15 | 29 | 30 | ## Checklist 31 | 32 | 39 | 40 | - [ ] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md). 41 | - [ ] I have referenced an issue containing the design document if my change 42 | introduces a new feature. 43 | - [ ] I am following the 44 | [contributing code guidelines](../blob/master/CONTRIBUTING.md#contributing-code). 45 | - [ ] I have read the [security policy](../security/policy). 46 | - [ ] I confirm that this pull request does not address a security 47 | vulnerability. If this pull request addresses a security vulnerability, I 48 | confirm that I got the approval (please contact 49 | [security@ory.sh](mailto:security@ory.sh)) from the maintainers to push 50 | the changes. 51 | - [ ] I have added tests that prove my fix is effective or that my feature 52 | works. 53 | - [ ] I have added or changed [the documentation](https://github.com/ory/docs). 54 | 55 | ## Further Comments 56 | 57 | 61 | -------------------------------------------------------------------------------- /.github/workflows/closed_references.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/closed_references.yml 3 | 4 | name: Closed Reference Notifier 5 | 6 | on: 7 | schedule: 8 | - cron: "0 0 * * *" 9 | workflow_dispatch: 10 | inputs: 11 | issueLimit: 12 | description: Max. number of issues to create 13 | required: true 14 | default: "5" 15 | 16 | jobs: 17 | find_closed_references: 18 | if: github.repository_owner == 'ory' 19 | runs-on: ubuntu-latest 20 | name: Find closed references 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: actions/setup-node@v2-beta 24 | with: 25 | node-version: "14" 26 | - uses: ory/closed-reference-notifier@v1 27 | with: 28 | token: ${{ secrets.GITHUB_TOKEN }} 29 | issueLabels: upstream,good first issue,help wanted 30 | issueLimit: ${{ github.event.inputs.issueLimit || '5' }} 31 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [master] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [master] 20 | schedule: 21 | - cron: "26 0 * * 2" 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: ["go"] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v2 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v2 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v2 72 | -------------------------------------------------------------------------------- /.github/workflows/conventional_commits.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/conventional_commits.yml 3 | 4 | name: Conventional commits 5 | 6 | # This GitHub CI Action enforces that pull request titles follow conventional commits. 7 | # More info at https://www.conventionalcommits.org. 8 | # 9 | # The Ory-wide defaults for commit titles and scopes are below. 10 | # Your repository can add/replace elements via a configuration file at the path below. 11 | # More info at https://github.com/ory/ci/blob/master/conventional_commit_config/README.md 12 | 13 | on: 14 | pull_request_target: 15 | types: 16 | - edited 17 | - opened 18 | - ready_for_review 19 | - reopened 20 | # pull_request: # for debugging, uses config in local branch but supports only Pull Requests from this repo 21 | 22 | jobs: 23 | main: 24 | name: Validate PR title 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v3 28 | - id: config 29 | uses: ory/ci/conventional_commit_config@master 30 | with: 31 | config_path: .github/conventional_commits.json 32 | default_types: | 33 | feat 34 | fix 35 | revert 36 | docs 37 | style 38 | refactor 39 | test 40 | build 41 | autogen 42 | security 43 | ci 44 | chore 45 | default_scopes: | 46 | deps 47 | docs 48 | default_require_scope: false 49 | - uses: amannn/action-semantic-pull-request@v4 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | with: 53 | types: ${{ steps.config.outputs.types }} 54 | scopes: ${{ steps.config.outputs.scopes }} 55 | requireScope: ${{ steps.config.outputs.requireScope }} 56 | subjectPattern: ^(?![A-Z]).+$ 57 | subjectPatternError: | 58 | The subject should start with a lowercase letter, yours is uppercase: 59 | "{subject}" 60 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | format: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-go@v3 13 | with: 14 | go-version: "1.22" 15 | - run: make format 16 | - name: Indicate formatting issues 17 | run: git diff HEAD --exit-code --color 18 | -------------------------------------------------------------------------------- /.github/workflows/labels.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/labels.yml 3 | 4 | name: Synchronize Issue Labels 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - master 11 | 12 | jobs: 13 | milestone: 14 | if: github.repository_owner == 'ory' 15 | name: Synchronize Issue Labels 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | - name: Synchronize Issue Labels 21 | uses: ory/label-sync-action@v0 22 | with: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | dry: false 25 | forced: true 26 | -------------------------------------------------------------------------------- /.github/workflows/licenses.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/licenses.yml 3 | 4 | name: Licenses 5 | 6 | on: 7 | pull_request: 8 | push: 9 | branches: 10 | - main 11 | - v3 12 | - master 13 | 14 | jobs: 15 | licenses: 16 | name: License compliance 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Install script 20 | uses: ory/ci/licenses/setup@master 21 | with: 22 | token: ${{ secrets.ORY_BOT_PAT || secrets.GITHUB_TOKEN }} 23 | - name: Check licenses 24 | uses: ory/ci/licenses/check@master 25 | - name: Write, commit, push licenses 26 | uses: ory/ci/licenses/write@master 27 | if: 28 | ${{ github.ref == 'refs/heads/main' || github.ref == 29 | 'refs/heads/master' || github.ref == 'refs/heads/v3' }} 30 | with: 31 | author-email: 32 | ${{ secrets.ORY_BOT_PAT && 33 | '60093411+ory-bot@users.noreply.github.com' || 34 | format('{0}@users.noreply.github.com', github.actor) }} 35 | author-name: ${{ secrets.ORY_BOT_PAT && 'ory-bot' || github.actor }} 36 | -------------------------------------------------------------------------------- /.github/workflows/milestone.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/server/.github/workflows/milestone.yml 3 | 4 | name: Generate and Publish Milestone Document 5 | 6 | on: 7 | workflow_dispatch: 8 | schedule: 9 | - cron: "0 0 * * *" 10 | 11 | jobs: 12 | milestone: 13 | if: github.repository_owner == 'ory' 14 | name: Generate and Publish Milestone Document 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | token: ${{ secrets.TOKEN_PRIVILEGED }} 21 | - name: Milestone Documentation Generator 22 | uses: ory/milestone-action@v0 23 | with: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | outputFile: docs/docs/milestones.md 26 | - name: Commit Milestone Documentation 27 | uses: EndBug/add-and-commit@v4.4.0 28 | with: 29 | message: "autogen(docs): update milestone document" 30 | author_name: aeneasr 31 | author_email: "3372410+aeneasr@users.noreply.github.com" 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.TOKEN_PRIVILEGED }} 34 | -------------------------------------------------------------------------------- /.github/workflows/pm.yml: -------------------------------------------------------------------------------- 1 | name: Synchronize with product board 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | pull_request: 8 | types: 9 | - opened 10 | - ready_for_review 11 | 12 | jobs: 13 | automate: 14 | if: github.event.pull_request.head.repo.fork == false 15 | name: Add issue to project 16 | runs-on: ubuntu-latest 17 | timeout-minutes: 5 18 | steps: 19 | - uses: ory-corp/planning-automation-action@v0.1 20 | with: 21 | organization: ory-corp 22 | project: 5 23 | token: ${{ secrets.ORY_BOT_PAT }} 24 | todoLabel: "Needs Triage" 25 | statusName: Status 26 | statusValue: "Needs Triage" 27 | includeEffort: "false" 28 | monthlyMilestoneName: Roadmap Monthly 29 | quarterlyMilestoneName: Roadmap 30 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED, DO NOT EDIT! 2 | # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/stale.yml 3 | 4 | name: "Close Stale Issues" 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 0 * * *" 9 | 10 | jobs: 11 | stale: 12 | if: github.repository_owner == 'ory' 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/stale@v4 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | stale-issue-message: | 19 | Hello contributors! 20 | 21 | I am marking this issue as stale as it has not received any engagement from the community or maintainers for a year. That does not imply that the issue has no merit! If you feel strongly about this issue 22 | 23 | - open a PR referencing and resolving the issue; 24 | - leave a comment on it and discuss ideas on how you could contribute towards resolving it; 25 | - leave a comment and describe in detail why this issue is critical for your use case; 26 | - open a new issue with updated details and a plan for resolving the issue. 27 | 28 | Throughout its lifetime, Ory has received over 10.000 issues and PRs. To sustain that growth, we need to prioritize and focus on issues that are important to the community. A good indication of importance, and thus priority, is activity on a topic. 29 | 30 | Unfortunately, [burnout](https://www.jeffgeerling.com/blog/2016/why-i-close-prs-oss-project-maintainer-notes) has become a [topic](https://opensource.guide/best-practices/#its-okay-to-hit-pause) of [concern](https://docs.brew.sh/Maintainers-Avoiding-Burnout) amongst open-source projects. 31 | 32 | It can lead to severe personal and health issues as well as [opening](https://haacked.com/archive/2019/05/28/maintainer-burnout/) catastrophic [attack vectors](https://www.gradiant.org/en/blog/open-source-maintainer-burnout-as-an-attack-surface/). 33 | 34 | The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone. 35 | 36 | If this issue was marked as stale erroneously you can exempt it by adding the `backlog` label, assigning someone, or setting a milestone for it. 37 | 38 | Thank you for your understanding and to anyone who participated in the conversation! And as written above, please do participate in the conversation if this topic is important to you! 39 | 40 | Thank you 🙏✌️ 41 | stale-issue-label: "stale" 42 | exempt-issue-labels: "bug,blocking,docs,backlog" 43 | days-before-stale: 365 44 | days-before-close: 30 45 | exempt-milestones: true 46 | exempt-assignees: true 47 | only-pr-labels: "stale" 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bin 2 | vendor 3 | .bin/ 4 | .idea 5 | _book 6 | node_modules/ 7 | LICENSE.txt 8 | dev 9 | dist/ 10 | oathkeeper 11 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable-all: false 3 | disable-all: true 4 | enable: 5 | - vet 6 | - goimports 7 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | includes: 4 | - from_url: 5 | url: https://raw.githubusercontent.com/ory/xgoreleaser/master/build.tmpl.yml 6 | 7 | before: 8 | hooks: 9 | - go mod download 10 | - git checkout -- go.sum go.mod 11 | - "curl -Lo /tmp/cosign.key 12 | https://raw.githubusercontent.com/ory/xgoreleaser/master/cosign.key" 13 | - "curl -Lo /tmp/cosign.pub 14 | https://raw.githubusercontent.com/ory/xgoreleaser/master/cosign.pub" 15 | 16 | variables: 17 | brew_name: oathkeeper 18 | brew_description: "The Ory Identity and Access Proxy (Ory Oathkeeper)" 19 | buildinfo_hash: "github.com/ory/oathkeeper/x.Commit" 20 | buildinfo_tag: "github.com/ory/oathkeeper/x.Version" 21 | buildinfo_date: "github.com/ory/oathkeeper/x.Date" 22 | dockerfile_alpine: ".docker/Dockerfile-alpine" 23 | dockerfile_static: ".docker/Dockerfile-distroless-static" 24 | 25 | project_name: oathkeeper 26 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Aeneas Rekkas 2 | Aeneas Rekkas <3372410+aeneasr@users.noreply.github.com> 3 | Aeneas Rekkas 4 | Aeneas Rekkas 5 | Aeneas Rekkas 6 | Patrik Neu 7 | Patrik Neu 8 | -------------------------------------------------------------------------------- /.nancy-ignore: -------------------------------------------------------------------------------- 1 | # etcd issues - can be ignored because etcd is not used. 2 | CVE-2020-15114 3 | CVE-2020-15136 4 | CVE-2020-15115 5 | # end 6 | 7 | # x/net false positives - see https://github.com/sonatype-nexus-community/nancy/issues/189 8 | CVE-2018-17142 9 | CVE-2018-17846 10 | CVE-2018-17143 11 | CVE-2018-17847 12 | CVE-2018-17848 13 | # end 14 | -------------------------------------------------------------------------------- /.orycli.yml: -------------------------------------------------------------------------------- 1 | project: oathkeeper 2 | 3 | pre_release_hooks: 4 | - ./scripts/render-schemas.sh 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .schema/version.schema.json 2 | -------------------------------------------------------------------------------- /.reference-ignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | docs 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /.schema/README.md: -------------------------------------------------------------------------------- 1 | Do not edit files in this directory. They are generated by CI using 2 | render-schemas.sh and other automation. Edit spec/config.schema.json instead. 3 | -------------------------------------------------------------------------------- /.schema/openapi/gen.go.yml: -------------------------------------------------------------------------------- 1 | disallowAdditionalPropertiesIfNotPresent: true 2 | packageName: client 3 | generateInterfaces: true 4 | isGoSubmodule: false 5 | structPrefix: true 6 | enumClassPrefix: true 7 | -------------------------------------------------------------------------------- /.schema/openapi/gen.typescript.yml: -------------------------------------------------------------------------------- 1 | npmName: "@ory/kratos-client" 2 | npmVersion: 0.0.0 3 | # typescriptThreePlus: true 4 | #npmRepository: https://github.com/ory/sdk.git 5 | supportsES6: true 6 | ensureUniqueParams: true 7 | modelPropertyNaming: original 8 | -------------------------------------------------------------------------------- /.schema/openapi/patches/meta.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /info 3 | value: 4 | title: Ory Oathkeeper API 5 | description: | 6 | Documentation for all of Ory Oathkeeper's APIs. 7 | version: >- 8 | {{ getenv "CIRCLE_TAG" }} 9 | license: 10 | name: Apache 2.0 11 | contact: 12 | email: "hi@ory.sh" 13 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | install: 4 | - go get -d -v . 5 | 6 | script: 7 | - go build -v ./ 8 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/git_push.sh.mustache: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ 3 | # 4 | # Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com" 5 | 6 | git_user_id=$1 7 | git_repo_id=$2 8 | release_note=$3 9 | git_host=$4 10 | 11 | if [ "$git_host" = "" ]; then 12 | git_host="{{{gitHost}}}" 13 | echo "[INFO] No command line input provided. Set \$git_host to $git_host" 14 | fi 15 | 16 | if [ "$git_user_id" = "" ]; then 17 | git_user_id="{{{gitUserId}}}" 18 | echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" 19 | fi 20 | 21 | if [ "$git_repo_id" = "" ]; then 22 | git_repo_id="{{{gitRepoId}}}" 23 | echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" 24 | fi 25 | 26 | if [ "$release_note" = "" ]; then 27 | release_note="{{{releaseNote}}}" 28 | echo "[INFO] No command line input provided. Set \$release_note to $release_note" 29 | fi 30 | 31 | # Initialize the local directory as a Git repository 32 | git init 33 | 34 | # Adds the files in the local repository and stages them for commit. 35 | git add . 36 | 37 | # Commits the tracked changes and prepares them to be pushed to a remote repository. 38 | git commit -m "$release_note" 39 | 40 | # Sets the new remote 41 | git_remote=`git remote` 42 | if [ "$git_remote" = "" ]; then # git remote not defined 43 | 44 | if [ "$GIT_TOKEN" = "" ]; then 45 | echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." 46 | git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git 47 | else 48 | git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git 49 | fi 50 | 51 | fi 52 | 53 | git pull origin master 54 | 55 | # Pushes (Forces) the changes in the local repository up to the remote repository 56 | echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" 57 | git push origin master 2>&1 | grep -v 'To https' 58 | 59 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/gitignore.mustache: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/go.mod.mustache: -------------------------------------------------------------------------------- 1 | module {{gitHost}}/{{gitUserId}}/{{gitRepoId}}{{#isGoSubmodule}}/{{packageName}}{{/isGoSubmodule}} 2 | 3 | go 1.13 4 | 5 | require ( 6 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 7 | {{#withAWSV4Signature}} 8 | github.com/aws/aws-sdk-go v1.34.14 9 | {{/withAWSV4Signature}} 10 | ) 11 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 3 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 4 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 5 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= 6 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 7 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 8 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 9 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= 10 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 11 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 12 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 13 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 14 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/model.mustache: -------------------------------------------------------------------------------- 1 | {{>partial_header}} 2 | package {{packageName}} 3 | 4 | {{#models}} 5 | import ( 6 | "encoding/json" 7 | {{#imports}} 8 | "{{import}}" 9 | {{/imports}} 10 | ) 11 | 12 | {{#model}} 13 | {{#isEnum}} 14 | {{>model_enum}} 15 | {{/isEnum}} 16 | {{^isEnum}} 17 | {{#oneOf}}{{#-first}}{{>model_oneof}}{{/-first}}{{/oneOf}}{{^oneOf}}{{#anyOf}}{{#-first}}{{>model_anyof}}{{/-first}}{{/anyOf}}{{^anyOf}}{{>model_simple}}{{/anyOf}}{{/oneOf}} 18 | {{/isEnum}} 19 | {{/model}} 20 | {{/models}} 21 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/model_anyof.mustache: -------------------------------------------------------------------------------- 1 | // {{classname}}{{#description}} {{{description}}}{{/description}}{{^description}} struct for {{{classname}}}{{/description}} 2 | type {{classname}} struct { 3 | {{#anyOf}} 4 | {{{.}}} *{{{.}}} 5 | {{/anyOf}} 6 | } 7 | 8 | // Unmarshal JSON data into any of the pointers in the struct 9 | func (dst *{{classname}}) UnmarshalJSON(data []byte) error { 10 | var err error 11 | {{#isNullable}} 12 | // this object is nullable so check if the payload is null or empty string 13 | if string(data) == "" || string(data) == "{}" { 14 | return nil 15 | } 16 | 17 | {{/isNullable}} 18 | {{#discriminator}} 19 | {{#mappedModels}} 20 | {{#-first}} 21 | // use discriminator value to speed up the lookup 22 | var jsonDict map[string]interface{} 23 | err := json.Unmarshal(data, &jsonDict) 24 | if err != nil { 25 | return fmt.Errorf("Failed to unmarshal JSON into map for the discrimintor lookup.") 26 | } 27 | 28 | {{/-first}} 29 | // check if the discriminator value is '{{{mappingName}}}' 30 | if jsonDict["{{{propertyBaseName}}}"] == "{{{mappingName}}}" { 31 | // try to unmarshal JSON data into {{{modelName}}} 32 | err = json.Unmarshal(data, &dst.{{{modelName}}}); 33 | if err == nil { 34 | json{{{modelName}}}, _ := json.Marshal(dst.{{{modelName}}}) 35 | if string(json{{{modelName}}}) == "{}" { // empty struct 36 | dst.{{{modelName}}} = nil 37 | } else { 38 | return nil // data stored in dst.{{{modelName}}}, return on the first match 39 | } 40 | } else { 41 | dst.{{{modelName}}} = nil 42 | } 43 | } 44 | 45 | {{/mappedModels}} 46 | {{/discriminator}} 47 | {{#anyOf}} 48 | // try to unmarshal JSON data into {{{.}}} 49 | err = json.Unmarshal(data, &dst.{{{.}}}); 50 | if err == nil { 51 | json{{{.}}}, _ := json.Marshal(dst.{{{.}}}) 52 | if string(json{{{.}}}) == "{}" { // empty struct 53 | dst.{{{.}}} = nil 54 | } else { 55 | return nil // data stored in dst.{{{.}}}, return on the first match 56 | } 57 | } else { 58 | dst.{{{.}}} = nil 59 | } 60 | 61 | {{/anyOf}} 62 | return fmt.Errorf("Data failed to match schemas in anyOf({{classname}})") 63 | } 64 | 65 | // Marshal data from the first non-nil pointers in the struct to JSON 66 | func (src *{{classname}}) MarshalJSON() ([]byte, error) { 67 | {{#anyOf}} 68 | if src.{{{.}}} != nil { 69 | return json.Marshal(&src.{{{.}}}) 70 | } 71 | 72 | {{/anyOf}} 73 | return nil, nil // no data in anyOf schemas 74 | } 75 | 76 | {{>nullable_model}} 77 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/model_enum.mustache: -------------------------------------------------------------------------------- 1 | // {{{classname}}} {{#description}}{{{.}}}{{/description}}{{^description}}the model '{{{classname}}}'{{/description}} 2 | type {{{classname}}} {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}} 3 | 4 | // List of {{{name}}} 5 | const ( 6 | {{#allowableValues}} 7 | {{#enumVars}} 8 | {{^-first}} 9 | {{/-first}} 10 | {{#enumClassPrefix}}{{{classname.toUpperCase}}}_{{/enumClassPrefix}}{{name}} {{{classname}}} = {{{value}}} 11 | {{/enumVars}} 12 | {{/allowableValues}} 13 | ) 14 | 15 | func (v *{{{classname}}}) UnmarshalJSON(src []byte) error { 16 | var value {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}} 17 | err := json.Unmarshal(src, &value) 18 | if err != nil { 19 | return err 20 | } 21 | enumTypeValue := {{{classname}}}(value) 22 | for _, existing := range []{{classname}}{ {{#allowableValues}}{{#enumVars}}{{{value}}}, {{/enumVars}} {{/allowableValues}} } { 23 | if existing == enumTypeValue { 24 | *v = enumTypeValue 25 | return nil 26 | } 27 | } 28 | 29 | return fmt.Errorf("%+v is not a valid {{classname}}", value) 30 | } 31 | 32 | // Ptr returns reference to {{{name}}} value 33 | func (v {{{classname}}}) Ptr() *{{{classname}}} { 34 | return &v 35 | } 36 | 37 | type Nullable{{{classname}}} struct { 38 | value *{{{classname}}} 39 | isSet bool 40 | } 41 | 42 | func (v Nullable{{classname}}) Get() *{{classname}} { 43 | return v.value 44 | } 45 | 46 | func (v *Nullable{{classname}}) Set(val *{{classname}}) { 47 | v.value = val 48 | v.isSet = true 49 | } 50 | 51 | func (v Nullable{{classname}}) IsSet() bool { 52 | return v.isSet 53 | } 54 | 55 | func (v *Nullable{{classname}}) Unset() { 56 | v.value = nil 57 | v.isSet = false 58 | } 59 | 60 | func NewNullable{{classname}}(val *{{classname}}) *Nullable{{classname}} { 61 | return &Nullable{{classname}}{value: val, isSet: true} 62 | } 63 | 64 | func (v Nullable{{{classname}}}) MarshalJSON() ([]byte, error) { 65 | return json.Marshal(v.value) 66 | } 67 | 68 | func (v *Nullable{{{classname}}}) UnmarshalJSON(src []byte) error { 69 | v.isSet = true 70 | return json.Unmarshal(src, &v.value) 71 | } 72 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/nullable_model.mustache: -------------------------------------------------------------------------------- 1 | type Nullable{{{classname}}} struct { 2 | value {{^isArray}}{{^isFreeFormObject}}*{{/isFreeFormObject}}{{/isArray}}{{{classname}}} 3 | isSet bool 4 | } 5 | 6 | func (v Nullable{{classname}}) Get() {{^isArray}}{{^isFreeFormObject}}*{{/isFreeFormObject}}{{/isArray}}{{classname}} { 7 | return v.value 8 | } 9 | 10 | func (v *Nullable{{classname}}) Set(val {{^isArray}}{{^isFreeFormObject}}*{{/isFreeFormObject}}{{/isArray}}{{classname}}) { 11 | v.value = val 12 | v.isSet = true 13 | } 14 | 15 | func (v Nullable{{classname}}) IsSet() bool { 16 | return v.isSet 17 | } 18 | 19 | func (v *Nullable{{classname}}) Unset() { 20 | v.value = nil 21 | v.isSet = false 22 | } 23 | 24 | func NewNullable{{classname}}(val {{^isArray}}{{^isFreeFormObject}}*{{/isFreeFormObject}}{{/isArray}}{{classname}}) *Nullable{{classname}} { 25 | return &Nullable{{classname}}{value: val, isSet: true} 26 | } 27 | 28 | func (v Nullable{{{classname}}}) MarshalJSON() ([]byte, error) { 29 | return json.Marshal(v.value) 30 | } 31 | 32 | func (v *Nullable{{{classname}}}) UnmarshalJSON(src []byte) error { 33 | v.isSet = true 34 | return json.Unmarshal(src, &v.value) 35 | } 36 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/openapi.mustache: -------------------------------------------------------------------------------- 1 | {{{openapi-yaml}}} -------------------------------------------------------------------------------- /.schema/openapi/templates/go/partial_header.mustache: -------------------------------------------------------------------------------- 1 | /* 2 | {{#appName}} 3 | * {{{appName}}} 4 | * 5 | {{/appName}} 6 | {{#appDescription}} 7 | * {{{appDescription}}} 8 | * 9 | {{/appDescription}} 10 | {{#version}} 11 | * API version: {{{version}}} 12 | {{/version}} 13 | {{#infoEmail}} 14 | * Contact: {{{infoEmail}}} 15 | {{/infoEmail}} 16 | */ 17 | 18 | // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 19 | -------------------------------------------------------------------------------- /.schema/openapi/templates/go/response.mustache: -------------------------------------------------------------------------------- 1 | {{>partial_header}} 2 | package {{packageName}} 3 | 4 | import ( 5 | "net/http" 6 | ) 7 | 8 | // APIResponse stores the API response returned by the server. 9 | type APIResponse struct { 10 | *http.Response `json:"-"` 11 | Message string `json:"message,omitempty"` 12 | // Operation is the name of the OpenAPI operation. 13 | Operation string `json:"operation,omitempty"` 14 | // RequestURL is the request URL. This value is always available, even if the 15 | // embedded *http.Response is nil. 16 | RequestURL string `json:"url,omitempty"` 17 | // Method is the HTTP method used for the request. This value is always 18 | // available, even if the embedded *http.Response is nil. 19 | Method string `json:"method,omitempty"` 20 | // Payload holds the contents of the response body (which may be nil or empty). 21 | // This is provided here as the raw response.Body() reader will have already 22 | // been drained. 23 | Payload []byte `json:"-"` 24 | } 25 | 26 | // NewAPIResponse returns a new APIResonse object. 27 | func NewAPIResponse(r *http.Response) *APIResponse { 28 | 29 | response := &APIResponse{Response: r} 30 | return response 31 | } 32 | 33 | // NewAPIResponseWithError returns a new APIResponse object with the provided error message. 34 | func NewAPIResponseWithError(errorMessage string) *APIResponse { 35 | 36 | response := &APIResponse{Message: errorMessage} 37 | return response 38 | } 39 | -------------------------------------------------------------------------------- /.schemas/README.md: -------------------------------------------------------------------------------- 1 | Do not edit files in this directory. They are kept for backwards compatibility. 2 | -------------------------------------------------------------------------------- /.schemas/authenticators.anonymous.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authenticators.anonymous.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Anonymous Authenticator Configuration", 6 | "description": "This section is optional when the authenticator is disabled.", 7 | "properties": { 8 | "subject": { 9 | "type": "string", 10 | "title": "Anonymous Subject", 11 | "examples": ["guest", "anon", "anonymous", "unknown"], 12 | "default": "anonymous", 13 | "description": "Sets the anonymous username." 14 | } 15 | }, 16 | "additionalProperties": false 17 | } 18 | -------------------------------------------------------------------------------- /.schemas/authenticators.cookie_session.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authenticators.cookie_session.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Cookie Session Authenticator Configuration", 6 | "description": "This section is optional when the authenticator is disabled.", 7 | "properties": { 8 | "check_session_url": { 9 | "title": "Session Check URL", 10 | "type": "string", 11 | "format": "uri", 12 | "description": "The origin to proxy requests to. If the response is a 200 with body `{ \"subject\": \"...\", \"extra\": {} }`. The request will pass the subject through successfully, otherwise it will be marked as unauthorized.\n\n>If this authenticator is enabled, this value is required.", 13 | "examples": ["https://session-store-host"] 14 | }, 15 | "only": { 16 | "type": "array", 17 | "items": { 18 | "type": "string", 19 | "additionalItems": false 20 | }, 21 | "title": "Only Cookies", 22 | "description": "A list of possible cookies to look for on incoming requests, and will fallthrough to the next authenticator if none of the passed cookies are set on the request." 23 | }, 24 | "preserve_path": { 25 | "title": "Preserve Path", 26 | "type": "boolean", 27 | "description": "When set to true, any path specified in `check_session_url` will be preserved instead of overwriting the path with the path from the original request" 28 | } 29 | }, 30 | "required": ["check_session_url"], 31 | "additionalProperties": false 32 | } 33 | -------------------------------------------------------------------------------- /.schemas/authenticators.noop.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authenticators.noop.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "NoOp Authenticator Configuration", 6 | "description": "This section is optional when the authenticator is disabled.", 7 | "properties": {}, 8 | "additionalProperties": false 9 | } 10 | -------------------------------------------------------------------------------- /.schemas/authenticators.oauth2_client_credentials.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authenticators.oauth2_client_credentials.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "OAuth 2.0 Client Credentials Authenticator Configuration", 6 | "description": "This section is optional when the authenticator is disabled.", 7 | "properties": { 8 | "token_url": { 9 | "type": "string", 10 | "description": "The OAuth 2.0 Token Endpoint that will be used to validate the client credentials.\n\n>If this authenticator is enabled, this value is required.", 11 | "format": "uri", 12 | "examples": ["https://my-website.com/oauth2/token"] 13 | }, 14 | "required_scope": { 15 | "type": "array", 16 | "title": "Request Permissions (Token Scope)", 17 | "description": "Scopes is an array of OAuth 2.0 scopes that are required when accessing an endpoint protected by this rule.\n If the token used in the Authorization header did not request that specific scope, the request is denied.", 18 | "items": { 19 | "type": "string" 20 | } 21 | } 22 | }, 23 | "required": ["token_url"], 24 | "additionalProperties": false 25 | } 26 | -------------------------------------------------------------------------------- /.schemas/authenticators.unauthorized.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authenticators.unauthorized.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Unauthorized Authenticator Configuration", 6 | "description": "This section is optional when the authenticator is disabled.", 7 | "properties": {}, 8 | "additionalProperties": false 9 | } 10 | -------------------------------------------------------------------------------- /.schemas/authorizers.allow.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authorizers.allow.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Allow Authorizer Configuration", 6 | "description": "This section is optional when the authorizer is disabled.", 7 | "properties": {}, 8 | "additionalProperties": false 9 | } 10 | -------------------------------------------------------------------------------- /.schemas/authorizers.deny.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authorizers.deny.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Deny Authorizer Configuration", 6 | "description": "This section is optional when the authorizer is disabled.", 7 | "properties": {}, 8 | "additionalProperties": false 9 | } 10 | -------------------------------------------------------------------------------- /.schemas/authorizers.keto_engine_acp_ory.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authorizers.keto_engine_acp_ory.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "ORY Keto Access Control Policy Authorizer Configuration", 6 | "description": "This section is optional when the authorizer is disabled.", 7 | "properties": { 8 | "base_url": { 9 | "title": "Base URL", 10 | "type": "string", 11 | "format": "uri", 12 | "description": "The base URL of ORY Keto.\n\n>If this authorizer is enabled, this value is required.", 13 | "examples": ["http://my-keto/"] 14 | }, 15 | "required_action": { 16 | "type": "string" 17 | }, 18 | "required_resource": { 19 | "type": "string" 20 | }, 21 | "subject": { 22 | "type": "string" 23 | }, 24 | "flavor": { 25 | "type": "string" 26 | } 27 | }, 28 | "required": ["base_url", "required_action", "required_resource"], 29 | "additionalProperties": false 30 | } 31 | -------------------------------------------------------------------------------- /.schemas/authorizers.remote_json.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/authorizers.remote_json.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Remote JSON Configuration", 6 | "description": "This section is optional when the authorizer is disabled.", 7 | "properties": { 8 | "remote": { 9 | "title": "Remote Authorizer URL", 10 | "type": "string", 11 | "format": "uri", 12 | "description": "The URL of the remote authorizer. The remote authorizer is expected to return either 200 OK or 403 Forbidden to allow/deny access.\n\n>If this authorizer is enabled, this value is required.", 13 | "examples": ["https://host/path"] 14 | }, 15 | "headers": { 16 | "type": "object", 17 | "additionalProperties": { 18 | "type": "string" 19 | } 20 | }, 21 | "payload": { 22 | "title": "JSON Payload", 23 | "type": "string", 24 | "description": "The JSON payload of the request sent to the remote authorizer. The string will be parsed by the Go text/template package and applied to an AuthenticationSession object.\n\n>If this authorizer is enabled, this value is required.", 25 | "examples": ["{\"subject\":\"{{ .Subject }}\"}"] 26 | }, 27 | "forward_response_headers_to_upstream": { 28 | "title": "Allowed Remote HTTP Headers for his Responses", 29 | "type": "array", 30 | "items": { 31 | "type": "string" 32 | }, 33 | "description": "A list of non simple headers the remote is allowed to return to mutate requests.", 34 | "examples": ["X-Foo"] 35 | } 36 | }, 37 | "required": ["remote", "payload"], 38 | "additionalProperties": false 39 | } 40 | -------------------------------------------------------------------------------- /.schemas/mutators.cookie.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/mutators.cookie.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Cookie Mutator Configuration", 6 | "description": "This section is optional when the mutator is disabled.", 7 | "required": ["cookies"], 8 | "properties": { 9 | "cookies": { 10 | "type": "object", 11 | "additionalProperties": { "type": "string" } 12 | } 13 | }, 14 | "additionalProperties": false 15 | } 16 | -------------------------------------------------------------------------------- /.schemas/mutators.header.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/mutators.header.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Header Mutator Configuration", 6 | "description": "This section is optional when the mutator is disabled.", 7 | "required": ["headers"], 8 | "properties": { 9 | "headers": { 10 | "type": "object", 11 | "additionalProperties": { "type": "string" } 12 | } 13 | }, 14 | "additionalProperties": false 15 | } 16 | -------------------------------------------------------------------------------- /.schemas/mutators.hydrator.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/mutators.hydrator.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "Hydrator Mutator Configuration", 6 | "description": "This section is optional when the mutator is disabled.", 7 | "properties": { 8 | "api": { 9 | "additionalProperties": false, 10 | "required": ["url"], 11 | "type": "object", 12 | "properties": { 13 | "url": { 14 | "type": "string", 15 | "format": "uri" 16 | }, 17 | "auth": { 18 | "type": "object", 19 | "additionalProperties": false, 20 | "properties": { 21 | "basic": { 22 | "required": ["username", "password"], 23 | "type": "object", 24 | "additionalProperties": false, 25 | "properties": { 26 | "username": { 27 | "type": "string" 28 | }, 29 | "password": { 30 | "type": "string" 31 | } 32 | } 33 | } 34 | } 35 | }, 36 | "retry": { 37 | "type": "object", 38 | "additionalProperties": false, 39 | "properties": { 40 | "number_of_retries": { 41 | "type": "number", 42 | "minimum": 0, 43 | "default": 100 44 | }, 45 | "delay_in_milliseconds": { 46 | "type": "number", 47 | "minimum": 0, 48 | "default": 3 49 | } 50 | } 51 | } 52 | } 53 | } 54 | }, 55 | "required": ["api"], 56 | "additionalProperties": false 57 | } 58 | -------------------------------------------------------------------------------- /.schemas/mutators.id_token.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/mutators.id_token.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "ID Token Mutator Configuration", 6 | "description": "This section is optional when the mutator is disabled.", 7 | "required": ["jwks_url", "issuer_url"], 8 | "properties": { 9 | "claims": { 10 | "type": "string" 11 | }, 12 | "issuer_url": { 13 | "type": "string", 14 | "title": "Issuer URL", 15 | "description": "Sets the \"iss\" value of the ID Token.\n\n>If this mutator is enabled, this value is required." 16 | }, 17 | "jwks_url": { 18 | "type": "string", 19 | "format": "uri", 20 | "title": "JSON Web Key URL", 21 | "description": "Sets the URL where keys should be fetched from. Supports remote locations (http, https) as well as local filesystem paths.\n\n>If this mutator is enabled, this value is required.", 22 | "examples": [ 23 | "https://fetch-keys/from/this/location.json", 24 | "file:///from/this/absolute/location.json", 25 | "file://../from/this/relative/location.json" 26 | ] 27 | }, 28 | "ttl": { 29 | "type": "string", 30 | "title": "Expire After", 31 | "description": "Sets the time-to-live of the JSON Web Token.", 32 | "pattern": "^[0-9]+(ns|us|ms|s|m|h)$", 33 | "default": "1m", 34 | "examples": ["1h", "1m", "30s"] 35 | } 36 | }, 37 | "additionalProperties": false 38 | } 39 | -------------------------------------------------------------------------------- /.schemas/mutators.noop.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/mutators.noop.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "title": "NoOp Mutator Configuration", 6 | "description": "This section is optional when the mutator is disabled.", 7 | "properties": {}, 8 | "additionalProperties": false 9 | } 10 | -------------------------------------------------------------------------------- /.schemas/scope_strategy.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ory/oathkeeper/master/.schemas/scope_strategy.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "title": "Scope Strategy", 5 | "type": "string", 6 | "enum": ["hierarchic", "exact", "wildcard", "none"], 7 | "default": "none", 8 | "description": "Sets the strategy validation algorithm." 9 | } 10 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Ory Security Policy 5 | 6 | This policy outlines Ory's security commitments and practices for users across 7 | different licensing and deployment models. 8 | 9 | To learn more about Ory's security service level agreements (SLAs) and 10 | processes, please [contact us](https://www.ory.sh/contact/). 11 | 12 | ## Ory Network Users 13 | 14 | - **Security SLA:** Ory addresses vulnerabilities in the Ory Network according 15 | to the following guidelines: 16 | - Critical: Typically addressed within 14 days. 17 | - High: Typically addressed within 30 days. 18 | - Medium: Typically addressed within 90 days. 19 | - Low: Typically addressed within 180 days. 20 | - Informational: Addressed as necessary. 21 | These timelines are targets and may vary based on specific circumstances. 22 | - **Release Schedule:** Updates are deployed to the Ory Network as 23 | vulnerabilities are resolved. 24 | - **Version Support:** The Ory Network always runs the latest version, ensuring 25 | up-to-date security fixes. 26 | 27 | ## Ory Enterprise License Customers 28 | 29 | - **Security SLA:** Ory addresses vulnerabilities based on their severity: 30 | - Critical: Typically addressed within 14 days. 31 | - High: Typically addressed within 30 days. 32 | - Medium: Typically addressed within 90 days. 33 | - Low: Typically addressed within 180 days. 34 | - Informational: Addressed as necessary. 35 | These timelines are targets and may vary based on specific circumstances. 36 | - **Release Schedule:** Updates are made available as vulnerabilities are 37 | resolved. Ory works closely with enterprise customers to ensure timely updates 38 | that align with their operational needs. 39 | - **Version Support:** Ory may provide security support for multiple versions, 40 | depending on the terms of the enterprise agreement. 41 | 42 | ## Apache 2.0 License Users 43 | 44 | - **Security SLA:** Ory does not provide a formal SLA for security issues under 45 | the Apache 2.0 License. 46 | - **Release Schedule:** Releases prioritize new functionality and include fixes 47 | for known security vulnerabilities at the time of release. While major 48 | releases typically occur one to two times per year, Ory does not guarantee a 49 | fixed release schedule. 50 | - **Version Support:** Security patches are only provided for the latest release 51 | version. 52 | 53 | ## Reporting a Vulnerability 54 | 55 | For details on how to report security vulnerabilities, visit our 56 | [security policy documentation](https://www.ory.sh/docs/ecosystem/security). 57 | -------------------------------------------------------------------------------- /api/credential_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package api_test 5 | 6 | import ( 7 | "context" 8 | "crypto/rsa" 9 | "encoding/json" 10 | "net/http" 11 | "net/http/httptest" 12 | "testing" 13 | 14 | "github.com/go-jose/go-jose/v3" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | 18 | "github.com/ory/x/configx" 19 | 20 | "github.com/ory/oathkeeper/driver/configuration" 21 | "github.com/ory/oathkeeper/internal" 22 | "github.com/ory/oathkeeper/rule" 23 | "github.com/ory/oathkeeper/x" 24 | ) 25 | 26 | func TestCredentialsHandler(t *testing.T) { 27 | conf := internal.NewConfigurationWithDefaults(configx.SkipValidation()) 28 | conf.SetForTest(t, configuration.MutatorIDTokenIsEnabled, true) 29 | conf.SetForTest(t, configuration.MutatorIDTokenJWKSURL, "file://../test/stub/jwks-rsa-multiple.json") 30 | conf.SetForTest(t, configuration.MutatorIDTokenIssuerURL, "https://example.com") 31 | conf.SetForTest(t, configuration.AuthenticatorAnonymousIsEnabled, true) 32 | conf.SetForTest(t, configuration.AuthorizerAllowIsEnabled, true) 33 | 34 | r := internal.NewRegistry(conf) 35 | 36 | require.NoError(t, r.RuleRepository().Set( 37 | context.Background(), 38 | []rule.Rule{{ 39 | Match: &rule.Match{URL: "http://example.com/*"}, 40 | Authenticators: []rule.Handler{{Handler: "anonymous"}}, 41 | Authorizer: rule.Handler{Handler: "allow"}, 42 | Mutators: []rule.Handler{{ 43 | Handler: "id_token", 44 | Config: json.RawMessage(` 45 | { 46 | "jwks_url": "file://../test/stub/jwks-rsa-single.json", 47 | "issuer_url": "https://example.com" 48 | } 49 | `)}}}}), 50 | ) 51 | 52 | router := x.NewAPIRouter() 53 | r.CredentialHandler().SetRoutes(router) 54 | server := httptest.NewServer(router) 55 | defer server.Close() 56 | 57 | res, err := server.Client().Get(server.URL + "/.well-known/jwks.json") 58 | require.NoError(t, err) 59 | defer res.Body.Close() 60 | require.Equal(t, http.StatusOK, res.StatusCode) 61 | 62 | var j jose.JSONWebKeySet 63 | require.NoError(t, json.NewDecoder(res.Body).Decode(&j)) 64 | require.Len(t, j.Key("3e0edde4-12ad-425d-a783-135f46eac57e"), 1, "The public key should be broadcasted") 65 | require.Len(t, j.Key("81be3441-5303-4c52-b00d-bbdfadc75633"), 1, "The public key should be broadcasted") 66 | require.Len(t, j.Key("f4190122-ae96-4c29-8b79-56024e459d80"), 1, "The public key generated from the private key should be broadcasted") 67 | assert.IsType(t, new(rsa.PublicKey), j.Key("3e0edde4-12ad-425d-a783-135f46eac57e")[0].Key, "Ensure a public key") 68 | assert.IsType(t, new(rsa.PublicKey), j.Key("f4190122-ae96-4c29-8b79-56024e459d80")[0].Key, "Ensure a public key") 69 | assert.IsType(t, new(rsa.PublicKey), j.Key("81be3441-5303-4c52-b00d-bbdfadc75633")[0].Key, "Ensure a public key") 70 | assert.Len(t, j.Keys, 3, "There should not be any unexpected keys") 71 | } 72 | -------------------------------------------------------------------------------- /api/health.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package api 5 | 6 | //lint:file-ignore U1000 Used to generate Swagger/OpenAPI definitions. 7 | 8 | // Alive returns an ok status if the instance is ready to handle HTTP requests. 9 | // 10 | // swagger:route GET /health/alive api isInstanceAlive 11 | // 12 | // # Check Alive Status 13 | // 14 | // This endpoint returns a 200 status code when the HTTP server is up running. 15 | // This status does currently not include checks whether the database connection is working. 16 | // 17 | // If the service supports TLS Edge Termination, this endpoint does not require the 18 | // `X-Forwarded-Proto` header to be set. 19 | // 20 | // Be aware that if you are running multiple nodes of this service, the health status will never 21 | // refer to the cluster state, only to a single instance. 22 | // 23 | // Produces: 24 | // - application/json 25 | // 26 | // Responses: 27 | // 200: healthStatus 28 | // 500: genericError 29 | func swaggerIsInstanceAlive() {} 30 | 31 | // Ready returns an ok status if the instance is ready to handle HTTP requests and all ReadyCheckers are ok. 32 | // 33 | // swagger:route GET /health/ready api isInstanceReady 34 | // 35 | // # Check Readiness Status 36 | // 37 | // This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g. 38 | // the database) are responsive as well. 39 | // 40 | // If the service supports TLS Edge Termination, this endpoint does not require the 41 | // `X-Forwarded-Proto` header to be set. 42 | // 43 | // Be aware that if you are running multiple nodes of this service, the health status will never 44 | // refer to the cluster state, only to a single instance. 45 | // 46 | // Produces: 47 | // - application/json 48 | // 49 | // Responses: 50 | // 200: healthStatus 51 | // 503: healthNotReadyStatus 52 | func swaggerIsInstanceReady() {} 53 | 54 | // Version returns this service's versions. 55 | // 56 | // swagger:route GET /version api getVersion 57 | // 58 | // # Get Service Version 59 | // 60 | // This endpoint returns the service version typically notated using semantic versioning. 61 | // 62 | // If the service supports TLS Edge Termination, this endpoint does not require the 63 | // `X-Forwarded-Proto` header to be set. 64 | // 65 | // Be aware that if you are running multiple nodes of this service, the health status will never 66 | // refer to the cluster state, only to a single instance. 67 | // 68 | // Produces: 69 | // - application/json 70 | // 71 | // Responses: 72 | // 200: version 73 | func swaggerGetVersion() {} 74 | -------------------------------------------------------------------------------- /api/rule.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package api 5 | 6 | import ( 7 | "net/http" 8 | 9 | "github.com/ory/oathkeeper/rule" 10 | "github.com/ory/oathkeeper/x" 11 | 12 | "github.com/julienschmidt/httprouter" 13 | "github.com/pkg/errors" 14 | 15 | "github.com/ory/oathkeeper/helper" 16 | "github.com/ory/x/pagination" 17 | ) 18 | 19 | const ( 20 | RulesPath = "/rules" 21 | ) 22 | 23 | type RuleHandler struct { 24 | r ruleHandlerRegistry 25 | } 26 | 27 | type ruleHandlerRegistry interface { 28 | x.RegistryWriter 29 | rule.Registry 30 | } 31 | 32 | func NewRuleHandler(r ruleHandlerRegistry) *RuleHandler { 33 | return &RuleHandler{r: r} 34 | } 35 | 36 | func (h *RuleHandler) SetRoutes(r *x.RouterAPI) { 37 | r.GET(RulesPath, h.listRules) 38 | r.GET(RulesPath+"/:id", h.getRules) 39 | } 40 | 41 | // swagger:route GET /rules api listRules 42 | // 43 | // # List All Rules 44 | // 45 | // This method returns an array of all rules that are stored in the backend. This is useful if you want to get a full 46 | // view of what rules you have currently in place. 47 | // 48 | // Consumes: 49 | // - application/json 50 | // 51 | // Produces: 52 | // - application/json 53 | // 54 | // Schemes: http, https 55 | // 56 | // Responses: 57 | // 200: rules 58 | // 500: genericError 59 | func (h *RuleHandler) listRules(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 60 | limit, offset := pagination.Parse(r, 50, 0, 500) 61 | rules, err := h.r.RuleRepository().List(r.Context(), limit, offset) 62 | if err != nil { 63 | h.r.Writer().WriteError(w, r, err) 64 | return 65 | } 66 | 67 | if rules == nil { 68 | rules = make([]rule.Rule, 0) 69 | } 70 | 71 | h.r.Writer().Write(w, r, rules) 72 | } 73 | 74 | // swagger:route GET /rules/{id} api getRule 75 | // 76 | // # Retrieve a Rule 77 | // 78 | // Use this method to retrieve a rule from the storage. If it does not exist you will receive a 404 error. 79 | // 80 | // Consumes: 81 | // - application/json 82 | // 83 | // Produces: 84 | // - application/json 85 | // 86 | // Schemes: http, https 87 | // 88 | // Responses: 89 | // 200: rule 90 | // 404: genericError 91 | // 500: genericError 92 | func (h *RuleHandler) getRules(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 93 | rl, err := h.r.RuleRepository().Get(r.Context(), ps.ByName("id")) 94 | if errors.Cause(err) == helper.ErrResourceNotFound { 95 | h.r.Writer().WriteErrorCode(w, r, http.StatusNotFound, err) 96 | return 97 | } else if err != nil { 98 | h.r.Writer().WriteError(w, r, err) 99 | return 100 | } 101 | 102 | h.r.Writer().Write(w, r, rl) 103 | } 104 | -------------------------------------------------------------------------------- /cmd/clidoc/generate.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "html" 10 | "io" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | 15 | "github.com/pkg/errors" 16 | 17 | "github.com/spf13/cobra" 18 | "github.com/spf13/cobra/doc" 19 | ) 20 | 21 | // Generate generates markdown documentation for a cobra command and its children. 22 | func Generate(cmd *cobra.Command, args []string) error { 23 | if len(args) != 1 { 24 | return errors.New("command expects one argument which is the path to the output directory") 25 | } 26 | 27 | return generate(cmd, args[0]) 28 | } 29 | 30 | func trimExt(s string) string { 31 | return strings.ReplaceAll(strings.TrimSuffix(s, filepath.Ext(s)), "_", "-") 32 | } 33 | 34 | func generate(cmd *cobra.Command, dir string) error { 35 | cmd.DisableAutoGenTag = true 36 | for _, c := range cmd.Commands() { 37 | if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { 38 | continue 39 | } 40 | if err := generate(c, dir); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | basename := strings.Replace(cmd.CommandPath(), " ", "-", -1) 46 | if err := os.MkdirAll(filepath.Join(dir), 0755); err != nil { 47 | return err 48 | } 49 | 50 | filename := filepath.Join(dir, basename) + ".md" 51 | f, err := os.Create(filename) 52 | if err != nil { 53 | return err 54 | } 55 | defer f.Close() 56 | 57 | if _, err := io.WriteString(f, fmt.Sprintf(`--- 58 | id: %s 59 | title: %s 60 | description: %s %s 61 | --- 62 | 63 | 68 | `, 69 | basename, 70 | cmd.CommandPath(), 71 | cmd.CommandPath(), 72 | cmd.Short, 73 | )); err != nil { 74 | return err 75 | } 76 | 77 | var b bytes.Buffer 78 | if err := doc.GenMarkdownCustom(cmd, &b, trimExt); err != nil { 79 | return err 80 | } 81 | 82 | _, err = f.WriteString(html.EscapeString(b.String())) 83 | return err 84 | } 85 | -------------------------------------------------------------------------------- /cmd/clidoc/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | 10 | "github.com/ory/oathkeeper/cmd" 11 | ) 12 | 13 | func main() { 14 | if err := Generate(cmd.RootCmd, os.Args[1:]); err != nil { 15 | _, _ = fmt.Fprintf(os.Stderr, "%+v", err) 16 | os.Exit(1) 17 | } 18 | fmt.Println("All files have been generated and updated.") 19 | } 20 | -------------------------------------------------------------------------------- /cmd/credentials.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | // credentialsCmd represents the credentials command 11 | var credentialsCmd = &cobra.Command{ 12 | Use: "credentials", 13 | Short: "Generate RSA, ECDSA, and other keys and output them as JSON Web Keys", 14 | } 15 | 16 | func init() { 17 | RootCmd.AddCommand(credentialsCmd) 18 | } 19 | -------------------------------------------------------------------------------- /cmd/credentials_generate.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "os" 10 | 11 | "github.com/spf13/cobra" 12 | 13 | "github.com/ory/x/cmdx" 14 | "github.com/ory/x/flagx" 15 | "github.com/ory/x/jwksx" 16 | ) 17 | 18 | // credentialsGenerateCmd represents the generate command 19 | var credentialsGenerateCmd = &cobra.Command{ 20 | Use: "generate", 21 | Short: "Generate a key for the specified algorithm", 22 | Long: `Examples: 23 | 24 | $ oathkeeper credentials generate --alg ES256 > jwks.json 25 | $ oathkeeper credentials generate --alg RS256 > jwks.json 26 | $ oathkeeper credentials generate --alg RS256 --bits 4096 > jwks.json`, 27 | Run: func(cmd *cobra.Command, _ []string) { 28 | key, err := jwksx.GenerateSigningKeys( 29 | flagx.MustGetString(cmd, "kid"), 30 | flagx.MustGetString(cmd, "alg"), 31 | flagx.MustGetInt(cmd, "bits"), 32 | ) 33 | cmdx.Must(err, "Unable to generate key: %s", err) 34 | 35 | d := json.NewEncoder(os.Stdout) 36 | d.SetIndent("", " ") 37 | err = d.Encode(key) 38 | cmdx.Must(err, "Unable to encode key to JSON: %s", err) 39 | }, 40 | } 41 | 42 | func init() { 43 | credentialsCmd.AddCommand(credentialsGenerateCmd) 44 | 45 | credentialsGenerateCmd.Flags().String("alg", "", fmt.Sprintf("Generate a key to be used for one of the following algorithms: %v", jwksx.GenerateSigningKeysAvailableAlgorithms())) 46 | credentialsGenerateCmd.Flags().String("kid", "", "The JSON Web Key ID (kid) to be used. A random value will be used if left empty.") 47 | credentialsGenerateCmd.Flags().Int("bits", 0, "The key size in bits. If left empty will default to a secure value for the selected algorithm.") 48 | 49 | cmdx.Must(credentialsGenerateCmd.MarkFlagRequired("alg"), "") 50 | } 51 | -------------------------------------------------------------------------------- /cmd/health.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // healthCmd represents the health command 13 | var healthCmd = &cobra.Command{ 14 | Use: "health", 15 | Short: "Commands for checking the status of an ORY Oathkeeper deployment", 16 | Long: `Note: 17 | The endpoint URL should point to a single ORY Oathkeeper deployment. 18 | If the endpoint URL points to a Load Balancer, these commands will effective test the Load Balancer. 19 | `, 20 | Run: func(cmd *cobra.Command, _ []string) { 21 | fmt.Println(cmd.UsageString()) 22 | }, 23 | } 24 | 25 | func init() { 26 | RootCmd.AddCommand(healthCmd) 27 | healthCmd.PersistentFlags().StringP("endpoint", "e", "", "The endpoint URL of ORY Oathkeeper's management API") 28 | } 29 | -------------------------------------------------------------------------------- /cmd/health_alive.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | 10 | "github.com/ory/oathkeeper/internal/httpclient/client/health" 11 | 12 | "github.com/spf13/cobra" 13 | 14 | "github.com/ory/x/cmdx" 15 | ) 16 | 17 | // healthCmd represents the health command 18 | var healthAliveCmd = &cobra.Command{ 19 | Use: "alive", 20 | Short: "Checks if an ORY Oathkeeper deployment is alive", 21 | Long: `Usage example: 22 | oathkeeper health --endpoint=http://localhost:4456/ alive 23 | 24 | Note: 25 | The endpoint URL should point to a single ORY Oathkeeper deployment. 26 | If the endpoint URL points to a Load Balancer, these commands will effective test the Load Balancer. 27 | `, 28 | Run: func(cmd *cobra.Command, _ []string) { 29 | client := newClient(cmd) 30 | 31 | r, err := client.Health.IsInstanceAlive(health.NewIsInstanceAliveParams()) 32 | // If err, print err and exit 1 33 | cmdx.Must(err, "%s", err) 34 | // Print payload 35 | fmt.Println(cmdx.FormatResponse(r.Payload)) 36 | // When healthy, ORY Oathkeeper always returns a status of "ok" 37 | if r.Payload.Status != "ok" { 38 | os.Exit(1) 39 | } 40 | }, 41 | } 42 | 43 | func init() { 44 | healthCmd.AddCommand(healthAliveCmd) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/health_ready.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | 10 | "github.com/ory/oathkeeper/internal/httpclient/client/health" 11 | 12 | "github.com/spf13/cobra" 13 | 14 | "github.com/ory/x/cmdx" 15 | ) 16 | 17 | // healthCmd represents the health command 18 | var healthReadyCmd = &cobra.Command{ 19 | Use: "ready", 20 | Short: "Checks if an ORY Oathkeeper deployment is ready", 21 | Long: `Usage example: 22 | oathkeeper health --endpoint=http://localhost:4456/ ready 23 | 24 | Note: 25 | The endpoint URL should point to a single ORY Oathkeeper deployment. 26 | If the endpoint URL points to a Load Balancer, these commands will effective test the Load Balancer. 27 | `, 28 | Run: func(cmd *cobra.Command, args []string) { 29 | client := newClient(cmd) 30 | 31 | r, err := client.Health.IsInstanceReady(health.NewIsInstanceReadyParams()) 32 | // If err, print err and exit 1 33 | cmdx.Must(err, "%s", err) 34 | // Print payload 35 | fmt.Println(cmdx.FormatResponse(r.Payload)) 36 | // When healthy, ORY Oathkeeper always returns a status of "ok" 37 | if r.Payload.Status != "ok" { 38 | os.Exit(1) 39 | } 40 | }, 41 | } 42 | 43 | func init() { 44 | healthCmd.AddCommand(healthReadyCmd) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "net/url" 8 | 9 | "github.com/spf13/cobra" 10 | 11 | "github.com/ory/oathkeeper/internal/httpclient/client" 12 | "github.com/ory/x/cmdx" 13 | "github.com/ory/x/flagx" 14 | ) 15 | 16 | func newClient(cmd *cobra.Command) *client.OryOathkeeper { 17 | endpoint := flagx.MustGetString(cmd, "endpoint") 18 | if endpoint == "" { 19 | cmdx.Fatalf("Please specify the endpoint url using the --endpoint flag, for more information use `oathkeeper help rules`") 20 | } 21 | 22 | u, err := url.ParseRequestURI(endpoint) 23 | cmdx.Must(err, `Unable to parse endpoint URL "%s": %s`, endpoint, err) 24 | 25 | return client.NewHTTPClientWithConfig(nil, &client.TransportConfig{ 26 | Host: u.Host, 27 | BasePath: u.Path, 28 | Schemes: []string{u.Scheme}, 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | 10 | "github.com/pkg/errors" 11 | "github.com/spf13/cobra" 12 | 13 | "github.com/ory/x/cmdx" 14 | 15 | _ "github.com/ory/jsonschema/v3/fileloader" 16 | _ "github.com/ory/jsonschema/v3/httploader" 17 | "github.com/ory/x/configx" 18 | ) 19 | 20 | // RootCmd represents the base command when called without any subcommands 21 | var RootCmd = &cobra.Command{ 22 | Use: "oathkeeper", 23 | Short: "A cloud native Access and Identity Proxy", 24 | } 25 | 26 | // Execute adds all child commands to the root command sets flags appropriately. 27 | // This is called by main.main(). It only needs to happen once to the rootCmd. 28 | func Execute() { 29 | if err := RootCmd.Execute(); err != nil { 30 | if !errors.Is(err, cmdx.ErrNoPrintButFail) { 31 | fmt.Println(err) 32 | } 33 | os.Exit(-1) 34 | } 35 | } 36 | 37 | func init() { 38 | configx.RegisterFlags(RootCmd.PersistentFlags()) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/rules.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // rulesCmd represents the rules command 13 | var rulesCmd = &cobra.Command{ 14 | Use: "rules", 15 | Short: "Commands for managing rules", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | fmt.Println(cmd.UsageString()) 18 | }, 19 | } 20 | 21 | func init() { 22 | RootCmd.AddCommand(rulesCmd) 23 | rulesCmd.PersistentFlags().StringP("endpoint", "e", "", "The endpoint URL of ORY Oathkeeper's management API") 24 | } 25 | -------------------------------------------------------------------------------- /cmd/rules_get.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/spf13/cobra" 10 | 11 | "github.com/ory/oathkeeper/internal/httpclient/client/api" 12 | "github.com/ory/x/cmdx" 13 | ) 14 | 15 | // getCmd represents the get command 16 | var getCmd = &cobra.Command{ 17 | Use: "get ", 18 | Short: "Get access rule", 19 | Long: `Usage example: 20 | 21 | oathkeeper rules --endpoint=http://localhost:4456/ get rule-1 22 | `, 23 | RunE: func(cmd *cobra.Command, args []string) error { 24 | cmdx.ExactArgs(cmd, args, 1) 25 | client := newClient(cmd) 26 | 27 | r, err := client.API.GetRule(api.NewGetRuleParams().WithID(args[0])) 28 | if err != nil { 29 | fmt.Fprintf(cmd.ErrOrStderr(), "Could not get rule: %s", err) 30 | return cmdx.FailSilently(cmd) 31 | } 32 | fmt.Println(cmdx.FormatResponse(r.Payload)) 33 | return nil 34 | }, 35 | } 36 | 37 | func init() { 38 | rulesCmd.AddCommand(getCmd) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/rules_list.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/ory/x/flagx" 10 | 11 | "github.com/spf13/cobra" 12 | 13 | "github.com/ory/oathkeeper/internal/httpclient/client/api" 14 | "github.com/ory/x/cmdx" 15 | ) 16 | 17 | // rulesListCmd represents the list command 18 | var rulesListCmd = &cobra.Command{ 19 | Use: "list", 20 | Short: "List access rules", 21 | Long: `Usage example: 22 | 23 | oathkeeper rules --endpoint=http://localhost:4456/ list 24 | `, 25 | Run: func(cmd *cobra.Command, args []string) { 26 | client := newClient(cmd) 27 | 28 | limit, page := int64(flagx.MustGetInt(cmd, "limit")), int64(flagx.MustGetInt(cmd, "page")) 29 | offset := (limit * page) - limit 30 | 31 | r, err := client.API.ListRules(api.NewListRulesParams().WithLimit(&limit).WithOffset(&offset)) 32 | cmdx.Must(err, "%s", err) 33 | fmt.Println(cmdx.FormatResponse(r.Payload)) 34 | }, 35 | } 36 | 37 | func init() { 38 | rulesCmd.AddCommand(rulesListCmd) 39 | rulesListCmd.Flags().Int("limit", 20, "The maximum amount of policies returned.") 40 | rulesListCmd.Flags().Int("page", 1, "The number of page.") 41 | } 42 | -------------------------------------------------------------------------------- /cmd/serve.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/ory/oathkeeper/cmd/server" 10 | "github.com/ory/oathkeeper/x" 11 | ) 12 | 13 | var serveCmd = &cobra.Command{ 14 | Use: "serve", 15 | Short: "Starts the HTTP/2 REST API and HTTP/2 Reverse Proxy", 16 | Long: `Opens two ports for serving both the HTTP/2 Rest API and the HTTP/2 Reverse Proxy. 17 | 18 | ## Configuration 19 | 20 | ORY Oathkeeper can be configured using environment variables as well as a configuration file. For more information 21 | on configuration options, open the configuration documentation: 22 | 23 | >> https://www.ory.sh/oathkeeper/docs/configuration << 24 | `, 25 | Run: func(cmd *cobra.Command, args []string) { 26 | server.RunServe(x.Version, x.Commit, x.Date)(cmd, args) 27 | }, 28 | } 29 | 30 | func init() { 31 | RootCmd.AddCommand(serveCmd) 32 | 33 | serveCmd.PersistentFlags().Bool("disable-telemetry", false, "Disable anonymized telemetry reports - for more information please visit https://www.ory.sh/docs/ecosystem/sqa") 34 | serveCmd.PersistentFlags().Bool("sqa-opt-out", false, "Disable anonymized telemetry reports - for more information please visit https://www.ory.sh/docs/ecosystem/sqa") 35 | } 36 | -------------------------------------------------------------------------------- /cmd/server/banner.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package server 5 | 6 | func banner(version string) string { 7 | return `Thank you for using ORY Oathkeeper ` + version + `! 8 | 9 | Take security seriously and subscribe to the ORY Security Newsletter. Stay on top of new patches and security insights. 10 | 11 | >> Subscribe now: https://www.ory.sh/l/sign-up-newsletter <<` 12 | } 13 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/spf13/cobra" 10 | 11 | "github.com/ory/oathkeeper/x" 12 | ) 13 | 14 | // versionCmd represents the version command 15 | var versionCmd = &cobra.Command{ 16 | Use: "version", 17 | Short: "Display this binary's version, build time and git hash of this build", 18 | Run: func(cmd *cobra.Command, args []string) { 19 | fmt.Printf("Version: %s\n", x.Version) 20 | fmt.Printf("Git Hash: %s\n", x.Commit) 21 | fmt.Printf("Build Time: %s\n", x.Date) 22 | }, 23 | } 24 | 25 | func init() { 26 | RootCmd.AddCommand(versionCmd) 27 | } 28 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "internal/httpclient" # autogenerated code. 3 | -------------------------------------------------------------------------------- /credentials/fetcher.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package credentials 5 | 6 | import ( 7 | "context" 8 | "net/url" 9 | 10 | "github.com/go-jose/go-jose/v3" 11 | ) 12 | 13 | type Fetcher interface { 14 | ResolveKey(ctx context.Context, locations []url.URL, kid string, use string) (*jose.JSONWebKey, error) 15 | ResolveSets(ctx context.Context, locations []url.URL) ([]jose.JSONWebKeySet, error) 16 | } 17 | 18 | type FetcherRegistry interface { 19 | CredentialsFetcher() Fetcher 20 | } 21 | -------------------------------------------------------------------------------- /credentials/signer.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package credentials 5 | 6 | import ( 7 | "context" 8 | "net/url" 9 | 10 | "github.com/golang-jwt/jwt/v5" 11 | ) 12 | 13 | type Signer interface { 14 | Sign(ctx context.Context, location *url.URL, claims jwt.Claims) (string, error) 15 | } 16 | 17 | type SignerRegistry interface { 18 | CredentialsSigner() Signer 19 | } 20 | -------------------------------------------------------------------------------- /credentials/signer_default.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package credentials 5 | 6 | import ( 7 | "context" 8 | "crypto/ecdsa" 9 | "crypto/rsa" 10 | "net/url" 11 | "reflect" 12 | 13 | "github.com/go-jose/go-jose/v3" 14 | "github.com/golang-jwt/jwt/v5" 15 | "github.com/pkg/errors" 16 | "golang.org/x/crypto/ed25519" 17 | ) 18 | 19 | var _ Signer = new(DefaultSigner) 20 | 21 | type DefaultSigner struct { 22 | r FetcherRegistry 23 | } 24 | 25 | func NewSignerDefault(r FetcherRegistry) *DefaultSigner { 26 | return &DefaultSigner{r: r} 27 | } 28 | 29 | func (s *DefaultSigner) Sign(ctx context.Context, location *url.URL, claims jwt.Claims) (string, error) { 30 | key, id, err := s.key(ctx, location) 31 | if err != nil { 32 | return "", err 33 | } 34 | 35 | method := jwt.GetSigningMethod(key.Algorithm) 36 | if method == nil { 37 | return "", errors.Errorf(`credentials: signing key "%s" declares unsupported algorithm "%s"`, key.KeyID, key.Algorithm) 38 | } 39 | 40 | token := jwt.NewWithClaims(method, claims) 41 | token.Header["kid"] = id 42 | 43 | signed, err := token.SignedString(key.Key) 44 | if err != nil { 45 | return "", errors.WithStack(err) 46 | } 47 | 48 | return signed, nil 49 | } 50 | 51 | func (s *DefaultSigner) key(ctx context.Context, location *url.URL) (*jose.JSONWebKey, string, error) { 52 | keys, err := s.r.CredentialsFetcher().ResolveSets(ctx, []url.URL{*location}) 53 | if err != nil { 54 | return nil, "", err 55 | } 56 | 57 | if len(keys) == 0 { 58 | return nil, "", errors.Errorf("credentials: expected at least one JSON Web Key Set to be returned but got: %d", len(keys)) 59 | } 60 | 61 | var pk jose.JSONWebKey 62 | var kid string 63 | for _, key := range keys[0].Keys { 64 | switch key.Key.(type) { 65 | case ed25519.PrivateKey: 66 | pk = key 67 | case ed25519.PublicKey: 68 | kid = key.KeyID 69 | 70 | case *ecdsa.PrivateKey: 71 | pk = key 72 | case *ecdsa.PublicKey: 73 | kid = key.KeyID 74 | 75 | case *rsa.PrivateKey: 76 | pk = key 77 | case *rsa.PublicKey: 78 | kid = key.KeyID 79 | 80 | case []byte: 81 | pk = key 82 | kid = key.KeyID 83 | 84 | default: 85 | return nil, "", errors.Errorf("credentials: unknown key type '%s'", reflect.TypeOf(key)) 86 | } 87 | 88 | if pk.Key != nil && kid != "" { 89 | break 90 | } 91 | } 92 | 93 | if pk.KeyID == "" { 94 | return nil, "", errors.Errorf("credentials: no suitable key could be found") 95 | } 96 | 97 | if kid == "" { 98 | kid = pk.KeyID 99 | } 100 | 101 | return &pk, kid, nil 102 | } 103 | -------------------------------------------------------------------------------- /credentials/signer_default_integration_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package credentials_test 5 | 6 | import ( 7 | "context" 8 | "net/url" 9 | "testing" 10 | "time" 11 | 12 | "github.com/golang-jwt/jwt/v5" 13 | 14 | "github.com/ory/oathkeeper/internal" 15 | ) 16 | 17 | func BenchmarkDefaultSigner(b *testing.B) { 18 | conf := internal.NewConfigurationWithDefaults() 19 | reg := internal.NewRegistry(conf) 20 | ctx := context.Background() 21 | 22 | for alg, keys := range map[string]string{ 23 | "RS256": "file://../test/stub/jwks-rsa-multiple.json", 24 | "ES256": "file://../test/stub/jwks-ecdsa.json", 25 | "HS256": "file://../test/stub/jwks-hs.json", 26 | } { 27 | b.Run("alg="+alg, func(b *testing.B) { 28 | jwks, _ := url.Parse(keys) 29 | for i := 0; i < b.N; i++ { 30 | if _, err := reg.CredentialsSigner().Sign(ctx, jwks, jwt.MapClaims{ 31 | "custom-claim2": 3.14159, 32 | "custom-claim3": true, 33 | "exp": time.Now().Add(time.Minute).Unix(), 34 | "iat": time.Now().Unix(), 35 | "iss": "issuer", 36 | "nbf": time.Now().Unix(), 37 | "sub": "some subject", 38 | }); err != nil { 39 | b.Fatal(err) 40 | } 41 | } 42 | }) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /credentials/signer_default_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package credentials 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "net/url" 10 | "testing" 11 | "time" 12 | 13 | "github.com/golang-jwt/jwt/v5" 14 | "github.com/pkg/errors" 15 | "github.com/stretchr/testify/require" 16 | 17 | "github.com/ory/oathkeeper/x" 18 | ) 19 | 20 | type defaultSignerMockRegistry struct { 21 | f Fetcher 22 | } 23 | 24 | func newDefaultSignerMockRegistry() *defaultSignerMockRegistry { 25 | return &defaultSignerMockRegistry{f: NewFetcherDefault(®{}, time.Millisecond*100, time.Millisecond*500)} 26 | } 27 | 28 | func (m *defaultSignerMockRegistry) CredentialsFetcher() Fetcher { 29 | return m.f 30 | } 31 | 32 | func TestSignerDefault(t *testing.T) { 33 | signer := NewSignerDefault(newDefaultSignerMockRegistry()) 34 | 35 | for _, src := range []string{ 36 | "file://../test/stub/jwks-hs.json", 37 | "file://../test/stub/jwks-rsa-multiple.json", 38 | "file://../test/stub/jwks-rsa-single.json", 39 | } { 40 | t.Run(fmt.Sprintf("src=%s", src), func(t *testing.T) { 41 | token, err := signer.Sign(context.Background(), x.ParseURLOrPanic(src), jwt.MapClaims{"sub": "foo"}) 42 | require.NoError(t, err) 43 | 44 | fetcher := NewFetcherDefault(®{}, time.Second, time.Second) 45 | 46 | _, err = verify(t, token, fetcher, src) 47 | require.NoError(t, err) 48 | }) 49 | } 50 | 51 | } 52 | 53 | func verify(t *testing.T, token string, f Fetcher, u string) (*jwt.Token, error) { 54 | to, err := jwt.ParseWithClaims(token, jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) { 55 | kid, ok := token.Header["kid"].(string) 56 | if !ok || kid == "" { 57 | return nil, errors.New("kid") 58 | } 59 | 60 | t.Logf("Looking up kid: %s", kid) 61 | 62 | key, err := f.ResolveKey(context.Background(), []url.URL{*x.ParseURLOrPanic(u)}, kid, "sig") 63 | if err != nil { 64 | t.Logf("erri erro: %+v", err) 65 | return nil, errors.WithStack(err) 66 | } 67 | 68 | // transform to public key 69 | if _, ok := key.Key.([]byte); !ok && !key.IsPublic() { 70 | k := key.Public() 71 | key = &k 72 | } 73 | 74 | t.Logf("erri erro: %T", key.Key) 75 | return key.Key, nil 76 | }) 77 | 78 | t.Logf("erri erro: %+v", err) 79 | return to, err 80 | } 81 | -------------------------------------------------------------------------------- /credentials/verifier.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package credentials 5 | 6 | import ( 7 | "context" 8 | "net/url" 9 | 10 | "github.com/golang-jwt/jwt/v5" 11 | 12 | "github.com/ory/fosite" 13 | ) 14 | 15 | type Verifier interface { 16 | Verify( 17 | ctx context.Context, 18 | token string, 19 | r *ValidationContext, 20 | ) (*jwt.Token, error) 21 | } 22 | 23 | type VerifierRegistry interface { 24 | CredentialsVerifier() Verifier 25 | } 26 | 27 | type ValidationContext struct { 28 | Algorithms []string 29 | Issuers []string 30 | Audiences []string 31 | ScopeStrategy fosite.ScopeStrategy 32 | Scope []string 33 | KeyURLs []url.URL 34 | } 35 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Package main ORY Oathkeeper 5 | // 6 | // ORY Oathkeeper is a reverse proxy that checks the HTTP Authorization for validity against a set of rules. This service uses Hydra to validate access tokens and policies. 7 | // 8 | // Schemes: http, https 9 | // Host: 10 | // BasePath: / 11 | // Version: Latest 12 | // Contact: ORY https://www.ory.am 13 | // 14 | // Consumes: 15 | // - application/json 16 | // 17 | // Produces: 18 | // - application/json 19 | // 20 | // Extensions: 21 | // --- 22 | // x-request-id: string 23 | // x-forwarded-proto: string 24 | // --- 25 | // 26 | // swagger:meta 27 | package main 28 | -------------------------------------------------------------------------------- /doc_swagger.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package main 5 | 6 | //lint:file-ignore U1000 Used to generate Swagger/OpenAPI definitions. 7 | 8 | // The standard error format 9 | // swagger:model genericError 10 | type genericError struct { 11 | Code int `json:"code,omitempty"` 12 | 13 | Status string `json:"status,omitempty"` 14 | 15 | Request string `json:"request,omitempty"` 16 | 17 | Reason string `json:"reason,omitempty"` 18 | 19 | Details []map[string]interface{} `json:"details,omitempty"` 20 | 21 | Message string `json:"message"` 22 | } 23 | 24 | // An empty response 25 | // swagger:response emptyResponse 26 | type emptyResponse struct{} 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | oathkeeper: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile-dc 8 | ports: 9 | - "4455:4455" 10 | - "4456:4456" 11 | command: serve --config=/etc/config/oathkeeper/config.yaml 12 | environment: 13 | - TRACING_PROVIDER=jaeger 14 | - TRACING_PROVIDER_JAEGER_SAMPLING_SERVER_URL=http://jaeger:5778/sampling 15 | - TRACING_PROVIDER_JAEGER_LOCAL_AGENT_ADDRESS=jaeger:6831 16 | - TRACING_PROVIDER_JAEGER_SAMPLING_TYPE=const 17 | - TRACING_PROVIDER_JAEGER_SAMPLING_VALUE=1 18 | volumes: 19 | - type: bind 20 | source: ./.docker_compose 21 | target: /etc/config/oathkeeper 22 | restart: on-failure 23 | jaeger: 24 | image: jaegertracing/all-in-one 25 | ports: 26 | - "16686:16686" # The UI port 27 | # These are ports for collecting, sampling, agents, ... 28 | # - "5775:5775/udp" 29 | # - "6831:6831/udp" 30 | # - "6832:6832/udp" 31 | # - "5778:5778" 32 | # - "14268:14268" 33 | # - "9411:9411" 34 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | Please find the documentation at 4 | [www.ory.sh/docs/oathkeeper](https://www.ory.sh/docs/oathkeeper). 5 | 6 | To contribute to the documentation, please head over to: 7 | [github.com/ory/docs/tree/master/docs/oathkeeper](https://github.com/ory/docs/tree/master/docs/oathkeeper) 8 | -------------------------------------------------------------------------------- /docs/sidebar.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /driver/configuration/provider_koanf_private_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package configuration 5 | 6 | import ( 7 | "context" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | 13 | "github.com/ory/x/logrusx" 14 | ) 15 | 16 | func TestGetURL(t *testing.T) { 17 | kp, err := NewKoanfProvider(context.Background(), nil, logrusx.New("", "")) 18 | require.NoError(t, err) 19 | assert.Nil(t, kp.getURL("", "key")) 20 | assert.Nil(t, kp.getURL("a", "key")) 21 | } 22 | -------------------------------------------------------------------------------- /driver/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package driver 5 | 6 | import ( 7 | "github.com/ory/oathkeeper/driver/configuration" 8 | ) 9 | 10 | type Driver interface { 11 | Configuration() configuration.Provider 12 | Registry() Registry 13 | } 14 | -------------------------------------------------------------------------------- /driver/driver_default.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package driver 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/spf13/pflag" 10 | 11 | "github.com/ory/x/configx" 12 | "github.com/ory/x/logrusx" 13 | 14 | "github.com/ory/oathkeeper/driver/configuration" 15 | ) 16 | 17 | type DefaultDriver struct { 18 | c configuration.Provider 19 | r Registry 20 | } 21 | 22 | func NewDefaultDriver(l *logrusx.Logger, version, build, date string, flags *pflag.FlagSet, configOpts ...configx.OptionModifier) Driver { 23 | c, err := configuration.NewKoanfProvider( 24 | context.Background(), flags, l, configOpts...) 25 | if err != nil { 26 | l.WithError(err).Fatal("Failed to initialize configuration") 27 | } 28 | r := NewRegistry(c).WithLogger(l).WithBuildInfo(version, build, date) 29 | return &DefaultDriver{r: r, c: c} 30 | } 31 | 32 | func (r *DefaultDriver) Configuration() configuration.Provider { 33 | return r.c 34 | } 35 | 36 | func (r *DefaultDriver) Registry() Registry { 37 | return r.r 38 | } 39 | -------------------------------------------------------------------------------- /driver/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package driver 5 | 6 | import ( 7 | "go.opentelemetry.io/otel/trace" 8 | 9 | "github.com/ory/x/logrusx" 10 | 11 | "github.com/ory/x/healthx" 12 | 13 | "github.com/ory/oathkeeper/pipeline/errors" 14 | "github.com/ory/oathkeeper/proxy" 15 | 16 | "github.com/ory/oathkeeper/api" 17 | "github.com/ory/oathkeeper/credentials" 18 | "github.com/ory/oathkeeper/driver/configuration" 19 | "github.com/ory/oathkeeper/pipeline/authn" 20 | "github.com/ory/oathkeeper/pipeline/authz" 21 | "github.com/ory/oathkeeper/pipeline/mutate" 22 | "github.com/ory/oathkeeper/rule" 23 | "github.com/ory/oathkeeper/x" 24 | ) 25 | 26 | type Registry interface { 27 | Init() 28 | 29 | WithConfig(c configuration.Provider) Registry 30 | WithLogger(l *logrusx.Logger) Registry 31 | WithBuildInfo(version, hash, date string) Registry 32 | BuildVersion() string 33 | BuildDate() string 34 | BuildHash() string 35 | 36 | ProxyRequestHandler() proxy.RequestHandler 37 | HealthxReadyCheckers() healthx.ReadyCheckers 38 | HealthHandler() *healthx.Handler 39 | RuleHandler() *api.RuleHandler 40 | DecisionHandler() *api.DecisionHandler 41 | CredentialHandler() *api.CredentialsHandler 42 | 43 | Proxy() *proxy.Proxy 44 | Tracer() trace.Tracer 45 | 46 | authn.Registry 47 | authz.Registry 48 | mutate.Registry 49 | errors.Registry 50 | 51 | rule.Registry 52 | credentials.FetcherRegistry 53 | credentials.SignerRegistry 54 | credentials.VerifierRegistry 55 | 56 | x.RegistryWriter 57 | x.RegistryLogger 58 | } 59 | 60 | func NewRegistry(c configuration.Provider) Registry { 61 | return NewRegistryMemory().WithConfig(c) 62 | } 63 | -------------------------------------------------------------------------------- /driver/registry_memory_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package driver 5 | 6 | import ( 7 | "context" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | 13 | "github.com/ory/oathkeeper/driver/configuration" 14 | "github.com/ory/x/logrusx" 15 | ) 16 | 17 | func TestRegistryMemoryAvailablePipelineAuthorizers(t *testing.T) { 18 | c, err := configuration.NewKoanfProvider(context.Background(), nil, logrusx.New("", "")) 19 | require.NoError(t, err) 20 | r := NewRegistry(c) 21 | got := r.AvailablePipelineAuthorizers() 22 | assert.ElementsMatch(t, got, []string{"allow", "deny", "keto_engine_acp_ory", "remote", "remote_json"}) 23 | } 24 | 25 | func TestRegistryMemoryPipelineAuthorizer(t *testing.T) { 26 | tests := []struct { 27 | id string 28 | wantErr bool 29 | }{ 30 | {id: "allow"}, 31 | {id: "deny"}, 32 | {id: "keto_engine_acp_ory"}, 33 | {id: "remote"}, 34 | {id: "remote_json"}, 35 | {id: "unregistered", wantErr: true}, 36 | } 37 | for _, tt := range tests { 38 | t.Run(tt.id, func(t *testing.T) { 39 | c, err := configuration.NewKoanfProvider(context.Background(), nil, logrusx.New("", "")) 40 | require.NoError(t, err) 41 | r := NewRegistry(c) 42 | a, err := r.PipelineAuthorizer(tt.id) 43 | if (err != nil) != tt.wantErr { 44 | t.Errorf("PipelineAuthorizer() error = %v, wantErr %v", err, tt.wantErr) 45 | return 46 | } 47 | if a != nil && a.GetID() != tt.id { 48 | t.Errorf("PipelineAuthorizer() got = %v, want %v", a.GetID(), tt.id) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /go_mod_shadow.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //go:build tools 5 | // +build tools 6 | 7 | package main 8 | 9 | import ( 10 | _ "github.com/go-swagger/go-swagger/cmd/swagger" 11 | 12 | _ "github.com/ory/go-acc" 13 | ) 14 | -------------------------------------------------------------------------------- /helper/bearer.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package helper 5 | 6 | import ( 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | defaultAuthorizationHeader = "Authorization" 13 | ) 14 | 15 | type BearerTokenLocation struct { 16 | Header *string `json:"header"` 17 | QueryParameter *string `json:"query_parameter"` 18 | Cookie *string `json:"cookie"` 19 | } 20 | 21 | func BearerTokenFromRequest(r *http.Request, tokenLocation *BearerTokenLocation) string { 22 | if tokenLocation != nil { 23 | if tokenLocation.Header != nil { 24 | if *tokenLocation.Header == defaultAuthorizationHeader { 25 | return DefaultBearerTokenFromRequest(r) 26 | } 27 | return r.Header.Get(*tokenLocation.Header) 28 | } else if tokenLocation.QueryParameter != nil { 29 | if r.URL == nil { 30 | return "" 31 | } 32 | return r.URL.Query().Get(*tokenLocation.QueryParameter) 33 | } else if tokenLocation.Cookie != nil { 34 | cookie, err := r.Cookie(*tokenLocation.Cookie) 35 | if err != nil { 36 | return "" 37 | } 38 | return cookie.Value 39 | } 40 | } 41 | 42 | return DefaultBearerTokenFromRequest(r) 43 | } 44 | 45 | func DefaultBearerTokenFromRequest(r *http.Request) string { 46 | token := r.Header.Get(defaultAuthorizationHeader) 47 | split := strings.SplitN(token, " ", 2) 48 | if len(split) != 2 || !strings.EqualFold(strings.ToLower(split[0]), "bearer") { 49 | return "" 50 | } 51 | return split[1] 52 | } 53 | -------------------------------------------------------------------------------- /internal/cloudstorage/useragent.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package cloudstorage 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | const ( 11 | prefix = "ory/oathkeeper" 12 | version = "0.1.0" 13 | ) 14 | 15 | // AzureUserAgentPrefix returns a prefix that is used to set Azure SDK User-Agent to help with diagnostics. 16 | func AzureUserAgentPrefix(api string) string { 17 | return userAgentString(api) 18 | } 19 | 20 | func userAgentString(api string) string { 21 | return fmt.Sprintf("%s/%s/%s", prefix, api, version) 22 | } 23 | -------------------------------------------------------------------------------- /internal/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package internal 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/ory/oathkeeper/driver" 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | "github.com/ory/x/configx" 12 | "github.com/ory/x/logrusx" 13 | ) 14 | 15 | func NewConfigurationWithDefaults(opts ...configx.OptionModifier) configuration.Provider { 16 | l := logrusx.New("", "") 17 | c, err := configuration.NewKoanfProvider( 18 | context.Background(), nil, l, opts...) 19 | if err != nil { 20 | l.WithError(err).Fatal("Failed to initialize configuration") 21 | } 22 | return c 23 | } 24 | 25 | func NewRegistry(c configuration.Provider) *driver.RegistryMemory { 26 | return driver.NewRegistryMemory().WithConfig(c).(*driver.RegistryMemory) 27 | } 28 | -------------------------------------------------------------------------------- /internal/httpclient/models/generic_error.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // GenericError The standard error format 16 | // 17 | // swagger:model genericError 18 | type GenericError struct { 19 | 20 | // code 21 | Code int64 `json:"code,omitempty"` 22 | 23 | // details 24 | Details []interface{} `json:"details"` 25 | 26 | // message 27 | Message string `json:"message,omitempty"` 28 | 29 | // reason 30 | Reason string `json:"reason,omitempty"` 31 | 32 | // request 33 | Request string `json:"request,omitempty"` 34 | 35 | // status 36 | Status string `json:"status,omitempty"` 37 | } 38 | 39 | // Validate validates this generic error 40 | func (m *GenericError) Validate(formats strfmt.Registry) error { 41 | return nil 42 | } 43 | 44 | // ContextValidate validates this generic error based on context it is used 45 | func (m *GenericError) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 46 | return nil 47 | } 48 | 49 | // MarshalBinary interface implementation 50 | func (m *GenericError) MarshalBinary() ([]byte, error) { 51 | if m == nil { 52 | return nil, nil 53 | } 54 | return swag.WriteJSON(m) 55 | } 56 | 57 | // UnmarshalBinary interface implementation 58 | func (m *GenericError) UnmarshalBinary(b []byte) error { 59 | var res GenericError 60 | if err := swag.ReadJSON(b, &res); err != nil { 61 | return err 62 | } 63 | *m = res 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /internal/httpclient/models/health_not_ready_status.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // HealthNotReadyStatus The not ready status of the service. 16 | // 17 | // swagger:model healthNotReadyStatus 18 | type HealthNotReadyStatus struct { 19 | 20 | // Errors contains a list of errors that caused the not ready status. 21 | Errors map[string]string `json:"errors,omitempty"` 22 | } 23 | 24 | // Validate validates this health not ready status 25 | func (m *HealthNotReadyStatus) Validate(formats strfmt.Registry) error { 26 | return nil 27 | } 28 | 29 | // ContextValidate validates this health not ready status based on context it is used 30 | func (m *HealthNotReadyStatus) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 31 | return nil 32 | } 33 | 34 | // MarshalBinary interface implementation 35 | func (m *HealthNotReadyStatus) MarshalBinary() ([]byte, error) { 36 | if m == nil { 37 | return nil, nil 38 | } 39 | return swag.WriteJSON(m) 40 | } 41 | 42 | // UnmarshalBinary interface implementation 43 | func (m *HealthNotReadyStatus) UnmarshalBinary(b []byte) error { 44 | var res HealthNotReadyStatus 45 | if err := swag.ReadJSON(b, &res); err != nil { 46 | return err 47 | } 48 | *m = res 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /internal/httpclient/models/health_status.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // HealthStatus The health status of the service. 16 | // 17 | // swagger:model healthStatus 18 | type HealthStatus struct { 19 | 20 | // Status always contains "ok". 21 | Status string `json:"status,omitempty"` 22 | } 23 | 24 | // Validate validates this health status 25 | func (m *HealthStatus) Validate(formats strfmt.Registry) error { 26 | return nil 27 | } 28 | 29 | // ContextValidate validates this health status based on context it is used 30 | func (m *HealthStatus) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 31 | return nil 32 | } 33 | 34 | // MarshalBinary interface implementation 35 | func (m *HealthStatus) MarshalBinary() ([]byte, error) { 36 | if m == nil { 37 | return nil, nil 38 | } 39 | return swag.WriteJSON(m) 40 | } 41 | 42 | // UnmarshalBinary interface implementation 43 | func (m *HealthStatus) UnmarshalBinary(b []byte) error { 44 | var res HealthStatus 45 | if err := swag.ReadJSON(b, &res); err != nil { 46 | return err 47 | } 48 | *m = res 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /internal/httpclient/models/rule_handler.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // RuleHandler rule handler 16 | // 17 | // swagger:model ruleHandler 18 | type RuleHandler struct { 19 | 20 | // Config contains the configuration for the handler. Please read the user 21 | // guide for a complete list of each handler's available settings. 22 | Config interface{} `json:"config,omitempty"` 23 | 24 | // Handler identifies the implementation which will be used to handle this specific request. Please read the user 25 | // guide for a complete list of available handlers. 26 | Handler string `json:"handler,omitempty"` 27 | } 28 | 29 | // Validate validates this rule handler 30 | func (m *RuleHandler) Validate(formats strfmt.Registry) error { 31 | return nil 32 | } 33 | 34 | // ContextValidate validates this rule handler based on context it is used 35 | func (m *RuleHandler) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 36 | return nil 37 | } 38 | 39 | // MarshalBinary interface implementation 40 | func (m *RuleHandler) MarshalBinary() ([]byte, error) { 41 | if m == nil { 42 | return nil, nil 43 | } 44 | return swag.WriteJSON(m) 45 | } 46 | 47 | // UnmarshalBinary interface implementation 48 | func (m *RuleHandler) UnmarshalBinary(b []byte) error { 49 | var res RuleHandler 50 | if err := swag.ReadJSON(b, &res); err != nil { 51 | return err 52 | } 53 | *m = res 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /internal/httpclient/models/rule_match.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // RuleMatch rule match 16 | // 17 | // swagger:model ruleMatch 18 | type RuleMatch struct { 19 | 20 | // An array of HTTP methods (e.g. GET, POST, PUT, DELETE, ...). When ORY Oathkeeper searches for rules 21 | // to decide what to do with an incoming request to the proxy server, it compares the HTTP method of the incoming 22 | // request with the HTTP methods of each rules. If a match is found, the rule is considered a partial match. 23 | // If the matchesUrl field is satisfied as well, the rule is considered a full match. 24 | Methods []string `json:"methods"` 25 | 26 | // This field represents the URL pattern this rule matches. When ORY Oathkeeper searches for rules 27 | // to decide what to do with an incoming request to the proxy server, it compares the full request URL 28 | // (e.g. https://mydomain.com/api/resource) without query parameters of the incoming 29 | // request with this field. If a match is found, the rule is considered a partial match. 30 | // If the matchesMethods field is satisfied as well, the rule is considered a full match. 31 | // 32 | // You can use regular expressions in this field to match more than one url. Regular expressions are encapsulated in 33 | // brackets < and >. The following example matches all paths of the domain `mydomain.com`: `https://mydomain.com/<.*>`. 34 | URL string `json:"url,omitempty"` 35 | } 36 | 37 | // Validate validates this rule match 38 | func (m *RuleMatch) Validate(formats strfmt.Registry) error { 39 | return nil 40 | } 41 | 42 | // ContextValidate validates this rule match based on context it is used 43 | func (m *RuleMatch) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 44 | return nil 45 | } 46 | 47 | // MarshalBinary interface implementation 48 | func (m *RuleMatch) MarshalBinary() ([]byte, error) { 49 | if m == nil { 50 | return nil, nil 51 | } 52 | return swag.WriteJSON(m) 53 | } 54 | 55 | // UnmarshalBinary interface implementation 56 | func (m *RuleMatch) UnmarshalBinary(b []byte) error { 57 | var res RuleMatch 58 | if err := swag.ReadJSON(b, &res); err != nil { 59 | return err 60 | } 61 | *m = res 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /internal/httpclient/models/unexpected_error.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | ) 13 | 14 | // UnexpectedError unexpected error 15 | // 16 | // swagger:model unexpectedError 17 | type UnexpectedError string 18 | 19 | // Validate validates this unexpected error 20 | func (m UnexpectedError) Validate(formats strfmt.Registry) error { 21 | return nil 22 | } 23 | 24 | // ContextValidate validates this unexpected error based on context it is used 25 | func (m UnexpectedError) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /internal/httpclient/models/upstream.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // Upstream upstream 16 | // 17 | // swagger:model Upstream 18 | type Upstream struct { 19 | 20 | // PreserveHost, if false (the default), tells ORY Oathkeeper to set the upstream request's Host header to the 21 | // hostname of the API's upstream's URL. Setting this flag to true instructs ORY Oathkeeper not to do so. 22 | PreserveHost bool `json:"preserve_host,omitempty"` 23 | 24 | // StripPath if set, replaces the provided path prefix when forwarding the requested URL to the upstream URL. 25 | StripPath string `json:"strip_path,omitempty"` 26 | 27 | // URL is the URL the request will be proxied to. 28 | URL string `json:"url,omitempty"` 29 | } 30 | 31 | // Validate validates this upstream 32 | func (m *Upstream) Validate(formats strfmt.Registry) error { 33 | return nil 34 | } 35 | 36 | // ContextValidate validates this upstream based on context it is used 37 | func (m *Upstream) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 38 | return nil 39 | } 40 | 41 | // MarshalBinary interface implementation 42 | func (m *Upstream) MarshalBinary() ([]byte, error) { 43 | if m == nil { 44 | return nil, nil 45 | } 46 | return swag.WriteJSON(m) 47 | } 48 | 49 | // UnmarshalBinary interface implementation 50 | func (m *Upstream) UnmarshalBinary(b []byte) error { 51 | var res Upstream 52 | if err := swag.ReadJSON(b, &res); err != nil { 53 | return err 54 | } 55 | *m = res 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /internal/httpclient/models/uuid.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/errors" 12 | "github.com/go-openapi/strfmt" 13 | "github.com/go-openapi/validate" 14 | ) 15 | 16 | // UUID UUID 17 | // 18 | // swagger:model UUID 19 | type UUID strfmt.UUID4 20 | 21 | // Validate validates this UUID 22 | func (m UUID) Validate(formats strfmt.Registry) error { 23 | var res []error 24 | 25 | if err := validate.FormatOf("", "body", "uuid4", strfmt.UUID4(m).String(), formats); err != nil { 26 | return err 27 | } 28 | 29 | if len(res) > 0 { 30 | return errors.CompositeValidationError(res...) 31 | } 32 | return nil 33 | } 34 | 35 | // ContextValidate validates this UUID based on context it is used 36 | func (m UUID) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /internal/httpclient/models/version.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-swagger; DO NOT EDIT. 2 | 3 | package models 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/go-openapi/strfmt" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | // Version version 16 | // 17 | // swagger:model version 18 | type Version struct { 19 | 20 | // Version is the service's version. 21 | Version string `json:"version,omitempty"` 22 | } 23 | 24 | // Validate validates this version 25 | func (m *Version) Validate(formats strfmt.Registry) error { 26 | return nil 27 | } 28 | 29 | // ContextValidate validates this version based on context it is used 30 | func (m *Version) ContextValidate(ctx context.Context, formats strfmt.Registry) error { 31 | return nil 32 | } 33 | 34 | // MarshalBinary interface implementation 35 | func (m *Version) MarshalBinary() ([]byte, error) { 36 | if m == nil { 37 | return nil, nil 38 | } 39 | return swag.WriteJSON(m) 40 | } 41 | 42 | // UnmarshalBinary interface implementation 43 | func (m *Version) UnmarshalBinary(b []byte) error { 44 | var res Version 45 | if err := swag.ReadJSON(b, &res); err != nil { 46 | return err 47 | } 48 | *m = res 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/ory/oathkeeper/cmd" 8 | "github.com/ory/x/profilex" 9 | ) 10 | 11 | func main() { 12 | defer profilex.Profile().Stop() 13 | 14 | cmd.Execute() 15 | } 16 | -------------------------------------------------------------------------------- /middleware/helpers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package middleware 5 | 6 | import ( 7 | "github.com/ory/oathkeeper/driver" 8 | "github.com/ory/oathkeeper/driver/configuration" 9 | ) 10 | 11 | // WithRegistry is a test option that writes the internal registry used in the 12 | // middleware to the given pointer. The pointer must be allocaded by the caller 13 | // beforehand (e.g., `regPtr := new(driver.Registry)`). 14 | func WithRegistry(r *driver.Registry) Option { 15 | return func(o *options) { o.registryAddr = r } 16 | } 17 | 18 | // WithConfigProvider is a test option that writes the internal config provider used in the 19 | // middleware to the given pointer. The pointer must be allocaded by the caller 20 | // beforehand (e.g., `cfgPtr := new(configuration.Provider)`). 21 | func WithConfigProvider(c *configuration.Provider) Option { 22 | return func(o *options) { o.configProviderAddr = c } 23 | } 24 | -------------------------------------------------------------------------------- /openapitools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", 3 | "spaces": 2, 4 | "generator-cli": { 5 | "version": "5.2.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@oryd/oathkeeper", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "Apache-2.0", 6 | "browser": { 7 | "fs": false 8 | }, 9 | "scripts": { 10 | "doctoc": "doctoc README.md" 11 | }, 12 | "prettier": "ory-prettier-styles", 13 | "dependencies": { 14 | "@openapitools/openapi-generator-cli": "^2.6.0" 15 | }, 16 | "devDependencies": { 17 | "doctoc": "^2.0.0", 18 | "license-checker": "^25.0.1", 19 | "ory-prettier-styles": "1.3.0", 20 | "prettier": "2.7.1", 21 | "prettier-plugin-packagejson": "^2.2.18" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | "net/url" 10 | 11 | "github.com/pkg/errors" 12 | 13 | "github.com/ory/herodot" 14 | 15 | "github.com/mitchellh/copystructure" 16 | 17 | "github.com/ory/oathkeeper/pipeline" 18 | ) 19 | 20 | var ErrAuthenticatorNotResponsible = errors.New("Authenticator not responsible") 21 | var ErrAuthenticatorNotEnabled = herodot.DefaultError{ 22 | ErrorField: "authenticator matching this route is misconfigured or disabled", 23 | CodeField: http.StatusInternalServerError, 24 | StatusField: http.StatusText(http.StatusInternalServerError), 25 | } 26 | 27 | type Authenticator interface { 28 | Authenticate(r *http.Request, session *AuthenticationSession, config json.RawMessage, rule pipeline.Rule) error 29 | GetID() string 30 | Validate(config json.RawMessage) error 31 | } 32 | 33 | func NewErrAuthenticatorNotEnabled(a Authenticator) *herodot.DefaultError { 34 | return ErrAuthenticatorNotEnabled.WithTrace(errors.New("")).WithReasonf(`Authenticator "%s" is disabled per configuration.`, a.GetID()) 35 | } 36 | 37 | func NewErrAuthenticatorMisconfigured(a Authenticator, err error) *herodot.DefaultError { 38 | return ErrAuthenticatorNotEnabled.WithTrace(err).WithReasonf( 39 | `Configuration for authenticator "%s" could not be validated: %s`, 40 | a.GetID(), 41 | err, 42 | ) 43 | } 44 | 45 | type AuthenticationSession struct { 46 | Subject string `json:"subject"` 47 | Extra map[string]interface{} `json:"extra"` 48 | Header http.Header `json:"header"` 49 | MatchContext MatchContext `json:"match_context"` 50 | } 51 | 52 | type MatchContext struct { 53 | RegexpCaptureGroups []string `json:"regexp_capture_groups"` 54 | URL *url.URL `json:"url"` 55 | Method string `json:"method"` 56 | Header http.Header `json:"header"` 57 | } 58 | 59 | type AuthenticatorForwardConfig interface { 60 | GetCheckSessionURL() string 61 | GetPreserveQuery() bool 62 | GetPreservePath() bool 63 | GetPreserveHost() bool 64 | GetForwardHTTPHeaders() []string 65 | GetSetHeaders() map[string]string 66 | GetForceMethod() string 67 | } 68 | 69 | func (a *AuthenticationSession) SetHeader(key, val string) { 70 | if a.Header == nil { 71 | a.Header = map[string][]string{} 72 | } 73 | a.Header.Set(key, val) 74 | } 75 | 76 | func (a *AuthenticationSession) Copy() *AuthenticationSession { 77 | return copystructure.Must(copystructure.Copy(a)).(*AuthenticationSession) 78 | } 79 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator_anonymous.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/ory/x/stringsx" 11 | 12 | "github.com/ory/oathkeeper/driver/configuration" 13 | "github.com/ory/oathkeeper/pipeline" 14 | 15 | "github.com/pkg/errors" 16 | ) 17 | 18 | type AuthenticatorAnonymous struct { 19 | c configuration.Provider 20 | } 21 | 22 | type AuthenticatorAnonymousConfiguration struct { 23 | Subject string `json:"subject"` 24 | } 25 | 26 | func NewAuthenticatorAnonymous(c configuration.Provider) *AuthenticatorAnonymous { 27 | return &AuthenticatorAnonymous{ 28 | c: c, 29 | } 30 | } 31 | 32 | func (a *AuthenticatorAnonymous) Validate(config json.RawMessage) error { 33 | if !a.c.AuthenticatorIsEnabled(a.GetID()) { 34 | return NewErrAuthenticatorNotEnabled(a) 35 | } 36 | 37 | _, err := a.Config(config) 38 | return err 39 | } 40 | 41 | func (a *AuthenticatorAnonymous) GetID() string { 42 | return "anonymous" 43 | } 44 | 45 | func (a *AuthenticatorAnonymous) Config(config json.RawMessage) (*AuthenticatorAnonymousConfiguration, error) { 46 | var c AuthenticatorAnonymousConfiguration 47 | if err := a.c.AuthenticatorConfig(a.GetID(), config, &c); err != nil { 48 | return nil, NewErrAuthenticatorMisconfigured(a, err) 49 | } 50 | 51 | return &c, nil 52 | } 53 | 54 | func (a *AuthenticatorAnonymous) Authenticate(r *http.Request, session *AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error { 55 | if len(r.Header.Get("Authorization")) != 0 { 56 | return errors.WithStack(ErrAuthenticatorNotResponsible) 57 | } 58 | 59 | cf, err := a.Config(config) 60 | if err != nil { 61 | return err 62 | } 63 | 64 | session.Subject = stringsx.Coalesce(cf.Subject, "anonymous") 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator_anonymous_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn_test 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | "testing" 10 | 11 | "github.com/ory/oathkeeper/driver/configuration" 12 | "github.com/ory/oathkeeper/internal" 13 | "github.com/ory/oathkeeper/pipeline/authn" 14 | 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | func TestAuthenticatorAnonymous(t *testing.T) { 20 | t.Parallel() 21 | conf := internal.NewConfigurationWithDefaults() 22 | reg := internal.NewRegistry(conf) 23 | 24 | session := new(authn.AuthenticationSession) 25 | 26 | a, err := reg.PipelineAuthenticator("anonymous") 27 | require.NoError(t, err) 28 | assert.Equal(t, "anonymous", a.GetID()) 29 | 30 | t.Run("method=authenticate/case=is anonymous user", func(t *testing.T) { 31 | err := a.Authenticate( 32 | &http.Request{Header: http.Header{}}, 33 | session, 34 | json.RawMessage(`{"subject":"anon"}`), 35 | nil) 36 | require.NoError(t, err) 37 | assert.Equal(t, "anon", session.Subject) 38 | }) 39 | 40 | t.Run("method=authenticate/case=has credentials", func(t *testing.T) { 41 | err := a.Authenticate( 42 | &http.Request{Header: http.Header{"Authorization": {"foo"}}}, 43 | session, 44 | json.RawMessage(`{"subject":"anon"}`), 45 | nil) 46 | require.Error(t, err) 47 | }) 48 | 49 | t.Run("method=validate", func(t *testing.T) { 50 | conf.SetForTest(t, configuration.AuthenticatorAnonymousIsEnabled, true) 51 | require.NoError(t, a.Validate(json.RawMessage(`{"subject":"foo"}`))) 52 | 53 | conf.SetForTest(t, configuration.AuthenticatorAnonymousIsEnabled, false) 54 | require.Error(t, a.Validate(json.RawMessage(`{"subject":"foo"}`))) 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator_noop.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | "github.com/ory/oathkeeper/pipeline" 12 | ) 13 | 14 | type AuthenticatorNoOp struct { 15 | c configuration.Provider 16 | } 17 | 18 | func NewAuthenticatorNoOp(c configuration.Provider) *AuthenticatorNoOp { 19 | return &AuthenticatorNoOp{c: c} 20 | } 21 | 22 | func (a *AuthenticatorNoOp) GetID() string { 23 | return "noop" 24 | } 25 | 26 | func (a *AuthenticatorNoOp) Validate(config json.RawMessage) error { 27 | if !a.c.AuthenticatorIsEnabled(a.GetID()) { 28 | return NewErrAuthenticatorNotEnabled(a) 29 | } 30 | 31 | if err := a.c.AuthenticatorConfig(a.GetID(), config, nil); err != nil { 32 | return NewErrAuthenticatorMisconfigured(a, err) 33 | } 34 | return nil 35 | } 36 | 37 | func (a *AuthenticatorNoOp) Authenticate(r *http.Request, session *AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error { 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator_noop_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/ory/oathkeeper/driver/configuration" 10 | "github.com/ory/oathkeeper/internal" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestAuthenticatorNoop(t *testing.T) { 17 | t.Parallel() 18 | conf := internal.NewConfigurationWithDefaults() 19 | reg := internal.NewRegistry(conf) 20 | 21 | a, err := reg.PipelineAuthenticator("noop") 22 | require.NoError(t, err) 23 | assert.Equal(t, "noop", a.GetID()) 24 | 25 | t.Run("method=authenticate", func(t *testing.T) { 26 | err := a.Authenticate(nil, nil, nil, nil) 27 | require.NoError(t, err) 28 | }) 29 | 30 | t.Run("method=validate", func(t *testing.T) { 31 | conf.SetForTest(t, configuration.AuthenticatorNoopIsEnabled, true) 32 | require.NoError(t, a.Validate(nil)) 33 | 34 | conf.SetForTest(t, configuration.AuthenticatorNoopIsEnabled, false) 35 | require.Error(t, a.Validate(nil)) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn_test 5 | 6 | import ( 7 | "fmt" 8 | "net/http" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | 13 | "github.com/ory/oathkeeper/pipeline/authn" 14 | "github.com/ory/oathkeeper/x" 15 | ) 16 | 17 | const ( 18 | key = "key" 19 | val = "value" 20 | ) 21 | 22 | func TestSetHeader(t *testing.T) { 23 | t.Parallel() 24 | assert := assert.New(t) 25 | for k, tc := range []struct { 26 | a *authn.AuthenticationSession 27 | desc string 28 | }{ 29 | { 30 | a: &authn.AuthenticationSession{}, 31 | desc: "should initiate Header field if it is nil", 32 | }, 33 | { 34 | a: &authn.AuthenticationSession{Header: map[string][]string{}}, 35 | desc: "should add a header to AuthenticationSession", 36 | }, 37 | } { 38 | t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.desc), func(t *testing.T) { 39 | tc.a.SetHeader(key, val) 40 | 41 | assert.NotNil(tc.a.Header) 42 | assert.Len(tc.a.Header, 1) 43 | assert.Equal(tc.a.Header.Get(key), val) 44 | }) 45 | } 46 | } 47 | 48 | func TestCopy(t *testing.T) { 49 | t.Parallel() 50 | assert := assert.New(t) 51 | original := &authn.AuthenticationSession{ 52 | Subject: "ab", 53 | Extra: map[string]interface{}{"a": "b", "b": map[string]string{"a:": "b"}}, 54 | Header: http.Header{"foo": {"bar", "baz"}}, 55 | MatchContext: authn.MatchContext{ 56 | RegexpCaptureGroups: []string{"a", "b"}, 57 | URL: x.ParseURLOrPanic("https://foo/bar"), 58 | Method: "GET", 59 | }, 60 | } 61 | 62 | copied := original.Copy() 63 | copied.Subject = "ba" 64 | copied.Extra["baz"] = "bar" 65 | copied.Header.Add("bazbar", "bar") 66 | copied.MatchContext.URL.Host = "asdf" 67 | copied.MatchContext.RegexpCaptureGroups[0] = "b" 68 | copied.MatchContext.Method = "PUT" 69 | 70 | assert.NotEqual(original.Subject, copied.Subject) 71 | assert.NotEqual(original.Extra, copied.Extra) 72 | assert.NotEqual(original.Header, copied.Header) 73 | assert.NotEqual(original.MatchContext.URL.Host, copied.MatchContext.URL.Host) 74 | assert.NotEqual(original.MatchContext.RegexpCaptureGroups, copied.MatchContext.RegexpCaptureGroups) 75 | assert.NotEqual(original.MatchContext.Method, copied.MatchContext.Method) 76 | } 77 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator_unauthorized.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | "github.com/ory/oathkeeper/pipeline" 12 | 13 | "github.com/pkg/errors" 14 | 15 | "github.com/ory/oathkeeper/helper" 16 | ) 17 | 18 | type AuthenticatorUnauthorized struct { 19 | c configuration.Provider 20 | } 21 | 22 | func NewAuthenticatorUnauthorized(c configuration.Provider) *AuthenticatorUnauthorized { 23 | return &AuthenticatorUnauthorized{c: c} 24 | } 25 | 26 | func (a *AuthenticatorUnauthorized) Validate(config json.RawMessage) error { 27 | if !a.c.AuthenticatorIsEnabled(a.GetID()) { 28 | return NewErrAuthenticatorNotEnabled(a) 29 | } 30 | 31 | if err := a.c.AuthenticatorConfig(a.GetID(), config, nil); err != nil { 32 | return NewErrAuthenticatorMisconfigured(a, err) 33 | } 34 | return nil 35 | } 36 | 37 | func (a *AuthenticatorUnauthorized) GetID() string { 38 | return "unauthorized" 39 | } 40 | 41 | func (a *AuthenticatorUnauthorized) Authenticate(r *http.Request, session *AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error { 42 | return errors.WithStack(helper.ErrUnauthorized) 43 | } 44 | -------------------------------------------------------------------------------- /pipeline/authn/authenticator_unauthorized_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn_test 5 | 6 | import ( 7 | "net/http" 8 | "testing" 9 | 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | "github.com/ory/oathkeeper/internal" 12 | 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestAuthenticatorBroken(t *testing.T) { 18 | t.Parallel() 19 | conf := internal.NewConfigurationWithDefaults() 20 | reg := internal.NewRegistry(conf) 21 | 22 | a, err := reg.PipelineAuthenticator("unauthorized") 23 | require.NoError(t, err) 24 | assert.Equal(t, "unauthorized", a.GetID()) 25 | 26 | t.Run("method=authenticate", func(t *testing.T) { 27 | err := a.Authenticate(&http.Request{Header: http.Header{}}, nil, nil, nil) 28 | require.Error(t, err) 29 | }) 30 | 31 | t.Run("method=validate", func(t *testing.T) { 32 | conf.SetForTest(t, configuration.AuthenticatorUnauthorizedIsEnabled, true) 33 | require.NoError(t, a.Validate(nil)) 34 | 35 | conf.SetForTest(t, configuration.AuthenticatorUnauthorizedIsEnabled, false) 36 | require.Error(t, a.Validate(nil)) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /pipeline/authn/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authn 5 | 6 | type Registry interface { 7 | AvailablePipelineAuthenticators() []string 8 | PipelineAuthenticator(string) (Authenticator, error) 9 | } 10 | -------------------------------------------------------------------------------- /pipeline/authz/authorizer.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authz 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/pkg/errors" 11 | 12 | "github.com/ory/herodot" 13 | 14 | "github.com/ory/oathkeeper/pipeline" 15 | "github.com/ory/oathkeeper/pipeline/authn" 16 | ) 17 | 18 | var ErrAuthorizerNotEnabled = herodot.DefaultError{ 19 | ErrorField: "authorizer matching this route is misconfigured or disabled", 20 | CodeField: http.StatusInternalServerError, 21 | StatusField: http.StatusText(http.StatusInternalServerError), 22 | } 23 | 24 | func NewErrAuthorizerNotEnabled(a Authorizer) *herodot.DefaultError { 25 | return ErrAuthorizerNotEnabled.WithTrace(errors.New("")).WithReasonf(`Authorizer "%s" is disabled per configuration.`, a.GetID()) 26 | } 27 | 28 | func NewErrAuthorizerMisconfigured(a Authorizer, err error) *herodot.DefaultError { 29 | return ErrAuthorizerNotEnabled.WithTrace(err).WithReasonf( 30 | `Configuration for authorizer "%s" could not be validated: %s`, 31 | a.GetID(), 32 | err, 33 | ) 34 | } 35 | 36 | type Authorizer interface { 37 | Authorize(r *http.Request, session *authn.AuthenticationSession, config json.RawMessage, rule pipeline.Rule) error 38 | GetID() string 39 | Validate(config json.RawMessage) error 40 | } 41 | -------------------------------------------------------------------------------- /pipeline/authz/authorizer_allow.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authz 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | "github.com/ory/oathkeeper/pipeline" 12 | "github.com/ory/oathkeeper/pipeline/authn" 13 | ) 14 | 15 | type AuthorizerAllow struct { 16 | c configuration.Provider 17 | } 18 | 19 | func NewAuthorizerAllow(c configuration.Provider) *AuthorizerAllow { 20 | return &AuthorizerAllow{c: c} 21 | } 22 | 23 | func (a *AuthorizerAllow) GetID() string { 24 | return "allow" 25 | } 26 | 27 | func (a *AuthorizerAllow) Authorize(r *http.Request, session *authn.AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error { 28 | return nil 29 | } 30 | 31 | func (a *AuthorizerAllow) Validate(config json.RawMessage) error { 32 | if !a.c.AuthorizerIsEnabled(a.GetID()) { 33 | return NewErrAuthorizerNotEnabled(a) 34 | } 35 | 36 | if err := a.c.AuthorizerConfig(a.GetID(), config, nil); err != nil { 37 | return NewErrAuthorizerMisconfigured(a, err) 38 | } 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /pipeline/authz/authorizer_allow_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authz_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/ory/oathkeeper/driver/configuration" 10 | "github.com/ory/oathkeeper/internal" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestAuthorizerAllow(t *testing.T) { 17 | t.Parallel() 18 | conf := internal.NewConfigurationWithDefaults() 19 | reg := internal.NewRegistry(conf) 20 | 21 | a, err := reg.PipelineAuthorizer("allow") 22 | require.NoError(t, err) 23 | assert.Equal(t, "allow", a.GetID()) 24 | 25 | t.Run("method=authorize/case=passes always", func(t *testing.T) { 26 | require.NoError(t, a.Authorize(nil, nil, nil, nil)) 27 | }) 28 | 29 | t.Run("method=validate", func(t *testing.T) { 30 | conf.SetForTest(t, configuration.AuthorizerAllowIsEnabled, true) 31 | require.NoError(t, a.Validate(nil)) 32 | 33 | conf.SetForTest(t, configuration.AuthorizerAllowIsEnabled, false) 34 | require.Error(t, a.Validate(nil)) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /pipeline/authz/authorizer_deny.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authz 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | "github.com/ory/oathkeeper/pipeline" 12 | "github.com/ory/oathkeeper/pipeline/authn" 13 | 14 | "github.com/pkg/errors" 15 | 16 | "github.com/ory/oathkeeper/helper" 17 | ) 18 | 19 | type AuthorizerDeny struct { 20 | c configuration.Provider 21 | } 22 | 23 | func NewAuthorizerDeny(c configuration.Provider) *AuthorizerDeny { 24 | return &AuthorizerDeny{c: c} 25 | } 26 | 27 | func (a *AuthorizerDeny) GetID() string { 28 | return "deny" 29 | } 30 | 31 | func (a *AuthorizerDeny) Authorize(r *http.Request, session *authn.AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error { 32 | return errors.WithStack(helper.ErrForbidden) 33 | } 34 | 35 | func (a *AuthorizerDeny) Validate(config json.RawMessage) error { 36 | if !a.c.AuthorizerIsEnabled(a.GetID()) { 37 | return NewErrAuthorizerNotEnabled(a) 38 | } 39 | 40 | if err := a.c.AuthorizerConfig(a.GetID(), config, nil); err != nil { 41 | return NewErrAuthorizerMisconfigured(a, err) 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /pipeline/authz/authorizer_deny_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authz_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/ory/oathkeeper/driver/configuration" 10 | "github.com/ory/oathkeeper/internal" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestAuthorizerDeny(t *testing.T) { 17 | t.Parallel() 18 | conf := internal.NewConfigurationWithDefaults() 19 | reg := internal.NewRegistry(conf) 20 | 21 | a, err := reg.PipelineAuthorizer("deny") 22 | require.NoError(t, err) 23 | assert.Equal(t, "deny", a.GetID()) 24 | 25 | t.Run("method=authorize/case=always returns denied", func(t *testing.T) { 26 | require.Error(t, a.Authorize(nil, nil, nil, nil)) 27 | }) 28 | 29 | t.Run("method=validate", func(t *testing.T) { 30 | conf.SetForTest(t, configuration.AuthorizerDenyIsEnabled, true) 31 | require.NoError(t, a.Validate(nil)) 32 | 33 | conf.SetForTest(t, configuration.AuthorizerDenyIsEnabled, false) 34 | require.Error(t, a.Validate(nil)) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /pipeline/authz/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authz 5 | 6 | type Registry interface { 7 | AvailablePipelineAuthorizers() []string 8 | PipelineAuthorizer(string) (Authorizer, error) 9 | } 10 | -------------------------------------------------------------------------------- /pipeline/authz/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package authz 5 | 6 | import ( 7 | "bytes" 8 | "io" 9 | "net/http" 10 | ) 11 | 12 | func pipeRequestBody(r *http.Request, w io.Writer) error { 13 | if r.Body == nil { 14 | return nil 15 | } 16 | 17 | var body bytes.Buffer 18 | defer r.Body.Close() 19 | _, err := io.Copy(w, io.TeeReader(r.Body, &body)) 20 | if err != nil { 21 | return err 22 | } 23 | r.Body = io.NopCloser(&body) 24 | return err 25 | } 26 | -------------------------------------------------------------------------------- /pipeline/error.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package pipeline 5 | 6 | import "github.com/pkg/errors" 7 | 8 | var ( 9 | ErrPipelineHandlerNotFound = errors.New("requested pipeline handler does not exist") 10 | ) 11 | -------------------------------------------------------------------------------- /pipeline/errors/error.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package errors 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/pkg/errors" 11 | 12 | "github.com/ory/herodot" 13 | 14 | "github.com/ory/oathkeeper/pipeline" 15 | ) 16 | 17 | type Handler interface { 18 | GetID() string 19 | Handle(w http.ResponseWriter, r *http.Request, config json.RawMessage, _ pipeline.Rule, err error) error 20 | Validate(config json.RawMessage) error 21 | } 22 | 23 | var ErrErrorHandlerNotEnabled = herodot.DefaultError{ 24 | ErrorField: "error handler matching this route is misconfigured or disabled", 25 | CodeField: http.StatusInternalServerError, 26 | StatusField: http.StatusText(http.StatusInternalServerError), 27 | } 28 | 29 | var ErrHandlerNotResponsible = errors.New("error handler not responsible for this request") 30 | 31 | func NewErrErrorHandlerNotEnabled(a Handler) *herodot.DefaultError { 32 | return ErrErrorHandlerNotEnabled.WithTrace(errors.New("")).WithReasonf(`Error handler "%s" is disabled per configuration.`, a.GetID()) 33 | } 34 | 35 | func NewErrErrorHandlerMisconfigured(a Handler, err error) *herodot.DefaultError { 36 | return ErrErrorHandlerNotEnabled.WithTrace(err).WithReasonf( 37 | `Configuration for error handler "%s" could not be validated: %s`, 38 | a.GetID(), 39 | err, 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /pipeline/errors/error_json.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package errors 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/ory/herodot" 11 | "github.com/ory/x/errorsx" 12 | 13 | "github.com/ory/oathkeeper/driver/configuration" 14 | "github.com/ory/oathkeeper/pipeline" 15 | "github.com/ory/oathkeeper/x" 16 | ) 17 | 18 | var _ Handler = new(ErrorJSON) 19 | 20 | type ( 21 | ErrorJSONConfig struct { 22 | Verbose bool `json:"verbose"` 23 | } 24 | ErrorJSON struct { 25 | c configuration.Provider 26 | d errorJSONDependencies 27 | } 28 | errorJSONDependencies interface { 29 | x.RegistryWriter 30 | } 31 | ) 32 | 33 | func NewErrorJSON( 34 | c configuration.Provider, 35 | d errorJSONDependencies, 36 | ) *ErrorJSON { 37 | return &ErrorJSON{c: c, d: d} 38 | } 39 | 40 | func (a *ErrorJSON) Handle(w http.ResponseWriter, r *http.Request, config json.RawMessage, _ pipeline.Rule, handleError error) error { 41 | c, err := a.Config(config) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | if !c.Verbose { 47 | if sc, ok := errorsx.Cause(handleError).(statusCoder); ok { 48 | switch sc.StatusCode() { 49 | case http.StatusInternalServerError: 50 | handleError = herodot.ErrInternalServerError.WithTrace(handleError) 51 | case http.StatusForbidden: 52 | handleError = herodot.ErrForbidden.WithTrace(handleError) 53 | case http.StatusNotFound: 54 | handleError = herodot.ErrNotFound.WithTrace(handleError) 55 | case http.StatusUnauthorized: 56 | handleError = herodot.ErrUnauthorized.WithTrace(handleError) 57 | case http.StatusBadRequest: 58 | handleError = herodot.ErrBadRequest.WithTrace(handleError) 59 | case http.StatusUnsupportedMediaType: 60 | handleError = herodot.ErrUnsupportedMediaType.WithTrace(handleError) 61 | case http.StatusConflict: 62 | handleError = herodot.ErrConflict.WithTrace(handleError) 63 | } 64 | } else { 65 | handleError = herodot.ErrInternalServerError.WithTrace(handleError) 66 | } 67 | } 68 | 69 | a.d.Writer().WriteError(w, r, handleError) 70 | return nil 71 | } 72 | 73 | func (a *ErrorJSON) Validate(config json.RawMessage) error { 74 | if !a.c.ErrorHandlerIsEnabled(a.GetID()) { 75 | return NewErrErrorHandlerNotEnabled(a) 76 | } 77 | 78 | _, err := a.Config(config) 79 | return err 80 | } 81 | 82 | func (a *ErrorJSON) Config(config json.RawMessage) (*ErrorJSONConfig, error) { 83 | var c ErrorJSONConfig 84 | if err := a.c.ErrorHandlerConfig(a.GetID(), config, &c); err != nil { 85 | return nil, NewErrErrorHandlerMisconfigured(a, err) 86 | } 87 | 88 | return &c, nil 89 | } 90 | 91 | func (a *ErrorJSON) GetID() string { 92 | return "json" 93 | } 94 | -------------------------------------------------------------------------------- /pipeline/errors/error_redirect.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package errors 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | "net/url" 10 | 11 | "github.com/ory/oathkeeper/driver/configuration" 12 | "github.com/ory/oathkeeper/pipeline" 13 | "github.com/ory/oathkeeper/x" 14 | ) 15 | 16 | var _ Handler = new(ErrorRedirect) 17 | 18 | const ( 19 | xForwardedProto = "X-Forwarded-Proto" 20 | xForwardedHost = "X-Forwarded-Host" 21 | xForwardedUri = "X-Forwarded-Uri" 22 | ) 23 | 24 | type ( 25 | ErrorRedirectConfig struct { 26 | To string `json:"to"` 27 | Code int `json:"code"` 28 | ReturnToQueryParam string `json:"return_to_query_param"` 29 | } 30 | ErrorRedirect struct { 31 | c configuration.Provider 32 | d ErrorRedirectDependencies 33 | } 34 | ErrorRedirectDependencies interface { 35 | x.RegistryWriter 36 | } 37 | ) 38 | 39 | func NewErrorRedirect( 40 | c configuration.Provider, 41 | d ErrorRedirectDependencies, 42 | ) *ErrorRedirect { 43 | return &ErrorRedirect{c: c, d: d} 44 | } 45 | 46 | func (a *ErrorRedirect) Handle(w http.ResponseWriter, r *http.Request, config json.RawMessage, _ pipeline.Rule, _ error) error { 47 | c, err := a.Config(config) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | r.URL.Scheme = x.OrDefaultString(r.Header.Get(xForwardedProto), r.URL.Scheme) 53 | r.URL.Host = x.OrDefaultString(r.Header.Get(xForwardedHost), r.URL.Host) 54 | r.URL.Path = x.OrDefaultString(r.Header.Get(xForwardedUri), r.URL.Path) 55 | 56 | http.Redirect(w, r, a.RedirectURL(r.URL, c), c.Code) 57 | return nil 58 | } 59 | 60 | func (a *ErrorRedirect) Validate(config json.RawMessage) error { 61 | if !a.c.ErrorHandlerIsEnabled(a.GetID()) { 62 | return NewErrErrorHandlerNotEnabled(a) 63 | } 64 | _, err := a.Config(config) 65 | return err 66 | } 67 | 68 | func (a *ErrorRedirect) Config(config json.RawMessage) (*ErrorRedirectConfig, error) { 69 | var c ErrorRedirectConfig 70 | if err := a.c.ErrorHandlerConfig(a.GetID(), config, &c); err != nil { 71 | return nil, NewErrErrorHandlerMisconfigured(a, err) 72 | } 73 | 74 | if c.Code < 301 || c.Code > 302 { 75 | c.Code = http.StatusFound 76 | } 77 | 78 | return &c, nil 79 | } 80 | 81 | func (a *ErrorRedirect) GetID() string { 82 | return "redirect" 83 | } 84 | 85 | func (a *ErrorRedirect) RedirectURL(uri *url.URL, c *ErrorRedirectConfig) string { 86 | if c.ReturnToQueryParam == "" { 87 | return c.To 88 | } 89 | 90 | u, err := url.Parse(c.To) 91 | if err != nil { 92 | return c.To 93 | } 94 | 95 | q := u.Query() 96 | q.Set(c.ReturnToQueryParam, uri.String()) 97 | u.RawQuery = q.Encode() 98 | return u.String() 99 | } 100 | -------------------------------------------------------------------------------- /pipeline/errors/error_www_authenticate.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package errors 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "net/http" 10 | 11 | "github.com/ory/oathkeeper/driver/configuration" 12 | "github.com/ory/oathkeeper/pipeline" 13 | "github.com/ory/oathkeeper/x" 14 | ) 15 | 16 | var _ Handler = new(ErrorWWWAuthenticate) 17 | 18 | type ( 19 | ErrorWWWAuthenticateConfig struct { 20 | Realm string `json:"realm"` 21 | } 22 | ErrorWWWAuthenticate struct { 23 | c configuration.Provider 24 | d ErrorWWWAuthenticateDependencies 25 | } 26 | ErrorWWWAuthenticateDependencies interface { 27 | x.RegistryWriter 28 | } 29 | ) 30 | 31 | func NewErrorWWWAuthenticate( 32 | c configuration.Provider, 33 | d ErrorWWWAuthenticateDependencies, 34 | ) *ErrorWWWAuthenticate { 35 | return &ErrorWWWAuthenticate{c: c, d: d} 36 | } 37 | 38 | func (a *ErrorWWWAuthenticate) Handle(w http.ResponseWriter, r *http.Request, config json.RawMessage, _ pipeline.Rule, _ error) error { 39 | c, err := a.Config(config) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%s`, c.Realm)) 45 | http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) 46 | return nil 47 | } 48 | 49 | func (a *ErrorWWWAuthenticate) Validate(config json.RawMessage) error { 50 | if !a.c.ErrorHandlerIsEnabled(a.GetID()) { 51 | return NewErrErrorHandlerNotEnabled(a) 52 | } 53 | _, err := a.Config(config) 54 | return err 55 | } 56 | 57 | func (a *ErrorWWWAuthenticate) Config(config json.RawMessage) (*ErrorWWWAuthenticateConfig, error) { 58 | var c ErrorWWWAuthenticateConfig 59 | if err := a.c.ErrorHandlerConfig(a.GetID(), config, &c); err != nil { 60 | return nil, NewErrErrorHandlerMisconfigured(a, err) 61 | } 62 | 63 | if c.Realm == "" { 64 | c.Realm = "Please authenticate." 65 | } 66 | 67 | return &c, nil 68 | } 69 | 70 | func (a *ErrorWWWAuthenticate) GetID() string { 71 | return "www_authenticate" 72 | } 73 | -------------------------------------------------------------------------------- /pipeline/errors/error_www_authenticate_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package errors_test 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "net/http" 10 | "net/http/httptest" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | 16 | "github.com/ory/herodot" 17 | 18 | "github.com/ory/oathkeeper/internal" 19 | ) 20 | 21 | func TestErrorWWWAuthenticate(t *testing.T) { 22 | conf := internal.NewConfigurationWithDefaults() 23 | reg := internal.NewRegistry(conf) 24 | 25 | a, err := reg.PipelineErrorHandler("www_authenticate") 26 | require.NoError(t, err) 27 | assert.Equal(t, "www_authenticate", a.GetID()) 28 | 29 | t.Run("method=handle", func(t *testing.T) { 30 | for k, tc := range []struct { 31 | d string 32 | header http.Header 33 | config string 34 | expectError error 35 | givenError error 36 | assert func(t *testing.T, recorder *httptest.ResponseRecorder) 37 | }{ 38 | { 39 | d: "should respond with a 401 realm message", 40 | givenError: &herodot.ErrNotFound, 41 | assert: func(t *testing.T, rw *httptest.ResponseRecorder) { 42 | assert.Equal(t, 401, rw.Code) 43 | assert.Equal(t, "Basic realm=Please authenticate.", rw.Header().Get("WWW-Authenticate")) 44 | }, 45 | }, 46 | { 47 | d: "should respond with a 401 realm message and a custom message", 48 | config: `{"realm": "foobar"}`, 49 | givenError: &herodot.ErrNotFound, 50 | assert: func(t *testing.T, rw *httptest.ResponseRecorder) { 51 | assert.Equal(t, 401, rw.Code) 52 | assert.Equal(t, "Basic realm=foobar", rw.Header().Get("WWW-Authenticate")) 53 | }, 54 | }, 55 | } { 56 | t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) { 57 | w := httptest.NewRecorder() 58 | r := httptest.NewRequest("GET", "/test", nil) 59 | err := a.Handle(w, r, json.RawMessage(tc.config), nil, tc.givenError) 60 | 61 | if tc.expectError != nil { 62 | require.EqualError(t, err, tc.expectError.Error(), "%+v", err) 63 | return 64 | } 65 | 66 | require.NoError(t, err) 67 | if tc.assert != nil { 68 | tc.assert(t, w) 69 | } 70 | }) 71 | } 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /pipeline/errors/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package errors 5 | 6 | type ( 7 | Handlers []Handler 8 | Registry interface { 9 | AvailablePipelineErrorHandlers() Handlers 10 | PipelineErrorHandler(id string) (Handler, error) 11 | } 12 | ) 13 | 14 | func (h Handlers) IDs() (res []string) { 15 | for _, hh := range h { 16 | res = append(res, hh.GetID()) 17 | } 18 | return res 19 | } 20 | -------------------------------------------------------------------------------- /pipeline/mutate/mutator.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package mutate 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/pkg/errors" 11 | 12 | "github.com/ory/herodot" 13 | 14 | "github.com/ory/oathkeeper/pipeline" 15 | "github.com/ory/oathkeeper/pipeline/authn" 16 | ) 17 | 18 | var ErrMutatorNotEnabled = herodot.DefaultError{ 19 | ErrorField: "mutator matching this route is misconfigured or disabled", 20 | CodeField: http.StatusInternalServerError, 21 | StatusField: http.StatusText(http.StatusInternalServerError), 22 | } 23 | 24 | func NewErrMutatorNotEnabled(a Mutator) *herodot.DefaultError { 25 | return ErrMutatorNotEnabled.WithTrace(errors.New("")).WithReasonf(`Mutator "%s" is disabled per configuration.`, a.GetID()) 26 | } 27 | 28 | func NewErrMutatorMisconfigured(a Mutator, err error) *herodot.DefaultError { 29 | return ErrMutatorNotEnabled.WithTrace(err).WithReasonf( 30 | `Configuration for mutator "%s" could not be validated: %s`, 31 | a.GetID(), 32 | err, 33 | ) 34 | } 35 | 36 | type Mutator interface { 37 | Mutate(r *http.Request, session *authn.AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error 38 | GetID() string 39 | Validate(config json.RawMessage) error 40 | } 41 | -------------------------------------------------------------------------------- /pipeline/mutate/mutator_broken.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package mutate 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/pkg/errors" 11 | 12 | "github.com/ory/oathkeeper/pipeline" 13 | "github.com/ory/oathkeeper/pipeline/authn" 14 | ) 15 | 16 | type MutatorBroken struct { 17 | enabled bool 18 | } 19 | 20 | func NewMutatorBroken(enabled bool) *MutatorBroken { 21 | return &MutatorBroken{ 22 | enabled: enabled, 23 | } 24 | } 25 | 26 | func (a *MutatorBroken) GetID() string { 27 | return "broken" 28 | } 29 | 30 | func (a *MutatorBroken) Mutate(r *http.Request, session *authn.AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error { 31 | return errors.New("forced denial of credentials") 32 | } 33 | 34 | func (a *MutatorBroken) Validate(_ json.RawMessage) error { 35 | if !a.enabled { 36 | return errors.WithStack(ErrMutatorNotEnabled.WithReasonf(`Mutator "%s" is disabled per configuration.`, a.GetID())) 37 | } 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /pipeline/mutate/mutator_broken_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package mutate_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/ory/oathkeeper/pipeline/mutate" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestCredentialsIssuerBroken(t *testing.T) { 16 | t.Parallel() 17 | a := mutate.NewMutatorBroken(false) 18 | assert.Equal(t, "broken", a.GetID()) 19 | 20 | err := a.Mutate(nil, nil, nil, nil) 21 | require.Error(t, err) 22 | 23 | t.Run("method=new/case=should not be declared in registry", func(t *testing.T) { 24 | err := a.Mutate(nil, nil, nil, nil) 25 | require.Error(t, err) 26 | }) 27 | 28 | t.Run("method=validate", func(t *testing.T) { 29 | require.Error(t, mutate.NewMutatorBroken(false).Validate(nil)) 30 | require.NoError(t, mutate.NewMutatorBroken(true).Validate(nil)) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /pipeline/mutate/mutator_header.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package mutate 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | "fmt" 10 | "net/http" 11 | "text/template" 12 | 13 | "github.com/ory/oathkeeper/driver/configuration" 14 | "github.com/ory/oathkeeper/pipeline" 15 | "github.com/ory/oathkeeper/pipeline/authn" 16 | "github.com/ory/oathkeeper/x" 17 | 18 | "github.com/pkg/errors" 19 | ) 20 | 21 | type MutatorHeaderConfig struct { 22 | Headers map[string]string `json:"headers"` 23 | } 24 | 25 | type MutatorHeader struct { 26 | c configuration.Provider 27 | t *template.Template 28 | } 29 | 30 | func NewMutatorHeader(c configuration.Provider) *MutatorHeader { 31 | return &MutatorHeader{c: c, t: x.NewTemplate("header")} 32 | } 33 | 34 | func (a *MutatorHeader) GetID() string { 35 | return "header" 36 | } 37 | 38 | func (a *MutatorHeader) WithCache(t *template.Template) { 39 | a.t = t 40 | } 41 | 42 | func (a *MutatorHeader) Mutate(_ *http.Request, session *authn.AuthenticationSession, config json.RawMessage, rl pipeline.Rule) error { 43 | cfg, err := a.config(config) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | for hdr, templateString := range cfg.Headers { 49 | var tmpl *template.Template 50 | var err error 51 | 52 | templateId := fmt.Sprintf("%s:%s", rl.GetID(), hdr) 53 | tmpl = a.t.Lookup(templateId) 54 | if tmpl == nil { 55 | tmpl, err = a.t.New(templateId).Parse(templateString) 56 | if err != nil { 57 | return errors.Wrapf(err, `error parsing headers template "%s" in rule "%s"`, templateString, rl.GetID()) 58 | } 59 | } 60 | 61 | headerValue := bytes.Buffer{} 62 | err = tmpl.Execute(&headerValue, session) 63 | if err != nil { 64 | return errors.Wrapf(err, `error executing headers template "%s" in rule "%s"`, templateString, rl.GetID()) 65 | } 66 | session.SetHeader(hdr, headerValue.String()) 67 | } 68 | 69 | return nil 70 | } 71 | 72 | func (a *MutatorHeader) Validate(config json.RawMessage) error { 73 | if !a.c.MutatorIsEnabled(a.GetID()) { 74 | return NewErrMutatorNotEnabled(a) 75 | } 76 | 77 | _, err := a.config(config) 78 | return err 79 | } 80 | 81 | func (a *MutatorHeader) config(config json.RawMessage) (*MutatorHeaderConfig, error) { 82 | var c MutatorHeaderConfig 83 | if err := a.c.MutatorConfig(a.GetID(), config, &c); err != nil { 84 | return nil, NewErrMutatorMisconfigured(a, err) 85 | } 86 | 87 | return &c, nil 88 | } 89 | -------------------------------------------------------------------------------- /pipeline/mutate/mutator_noop.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package mutate 5 | 6 | import ( 7 | "encoding/json" 8 | "net/http" 9 | 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | "github.com/ory/oathkeeper/pipeline" 12 | "github.com/ory/oathkeeper/pipeline/authn" 13 | ) 14 | 15 | type MutatorNoop struct{ c configuration.Provider } 16 | 17 | func NewMutatorNoop(c configuration.Provider) *MutatorNoop { 18 | return &MutatorNoop{c: c} 19 | } 20 | 21 | func (a *MutatorNoop) GetID() string { 22 | return "noop" 23 | } 24 | 25 | func (a *MutatorNoop) Mutate(r *http.Request, session *authn.AuthenticationSession, config json.RawMessage, _ pipeline.Rule) error { 26 | currentSessionHeaders := session.Header.Clone() 27 | session.Header = r.Header 28 | if session.Header == nil { 29 | session.Header = make(map[string][]string) 30 | } 31 | 32 | for k, v := range currentSessionHeaders { 33 | var val string 34 | if len(v) == 0 { 35 | val = "" 36 | } else { 37 | val = v[0] 38 | } 39 | session.SetHeader(k, val) 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func (a *MutatorNoop) Validate(config json.RawMessage) error { 46 | if !a.c.MutatorIsEnabled(a.GetID()) { 47 | return NewErrMutatorNotEnabled(a) 48 | } 49 | 50 | if err := a.c.MutatorConfig(a.GetID(), config, nil); err != nil { 51 | return NewErrMutatorMisconfigured(a, err) 52 | } 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /pipeline/mutate/mutator_noop_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package mutate_test 5 | 6 | import ( 7 | "net/http" 8 | "testing" 9 | 10 | "github.com/ory/oathkeeper/pipeline/authn" 11 | 12 | "github.com/ory/oathkeeper/driver/configuration" 13 | "github.com/ory/oathkeeper/internal" 14 | 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | func TestMutatorNoop(t *testing.T) { 20 | t.Parallel() 21 | conf := internal.NewConfigurationWithDefaults() 22 | reg := internal.NewRegistry(conf) 23 | 24 | a, err := reg.PipelineMutator("noop") 25 | require.NoError(t, err) 26 | assert.Equal(t, "noop", a.GetID()) 27 | 28 | t.Run("method=mutate/case=passes always", func(t *testing.T) { 29 | r := &http.Request{Header: http.Header{"foo": {"foo"}}} 30 | s := &authn.AuthenticationSession{} 31 | err := a.Mutate(r, s, nil, nil) 32 | require.NoError(t, err) 33 | assert.EqualValues(t, r.Header, s.Header) 34 | }) 35 | 36 | t.Run("method=mutate/case=ensure authentication session headers are kept", func(t *testing.T) { 37 | r := &http.Request{Header: http.Header{"foo": {"foo"}}} 38 | s := &authn.AuthenticationSession{Header: http.Header{"bar": {"bar"}}} 39 | combinedHeaders := http.Header{"foo": {"foo"}} 40 | combinedHeaders.Set("bar", "bar") 41 | err := a.Mutate(r, s, nil, nil) 42 | require.NoError(t, err) 43 | assert.EqualValues(t, r.Header, combinedHeaders) 44 | }) 45 | 46 | t.Run("method=validate", func(t *testing.T) { 47 | conf.SetForTest(t, configuration.MutatorNoopIsEnabled, true) 48 | require.NoError(t, a.Validate(nil)) 49 | 50 | conf.SetForTest(t, configuration.MutatorNoopIsEnabled, false) 51 | require.Error(t, a.Validate(nil)) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /pipeline/mutate/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package mutate 5 | 6 | type Registry interface { 7 | AvailablePipelineMutators() []string 8 | PipelineMutator(string) (Mutator, error) 9 | } 10 | -------------------------------------------------------------------------------- /pipeline/rule.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package pipeline 5 | 6 | import ( 7 | "github.com/ory/oathkeeper/driver/configuration" 8 | ) 9 | 10 | type Rule interface { 11 | GetID() string 12 | // ReplaceAllString searches the input string and replaces each match (with the rule's pattern) 13 | // found with the replacement text. 14 | ReplaceAllString(strategy configuration.MatchingStrategy, input, replacement string) (string, error) 15 | } 16 | -------------------------------------------------------------------------------- /proxy/response_writer.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package proxy 5 | 6 | import ( 7 | "bytes" 8 | "net/http" 9 | ) 10 | 11 | type simpleResponseWriter struct { 12 | header http.Header 13 | code int 14 | buffer *bytes.Buffer 15 | } 16 | 17 | func NewSimpleResponseWriter() *simpleResponseWriter { 18 | return &simpleResponseWriter{ 19 | header: http.Header{}, 20 | buffer: new(bytes.Buffer), 21 | } 22 | } 23 | 24 | func (r *simpleResponseWriter) Header() http.Header { 25 | return r.header 26 | } 27 | 28 | func (r *simpleResponseWriter) Write(b []byte) (int, error) { 29 | return r.buffer.Write(b) 30 | } 31 | 32 | func (r *simpleResponseWriter) WriteHeader(statusCode int) { 33 | r.code = statusCode 34 | } 35 | -------------------------------------------------------------------------------- /rule/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Package rule implements management capabilities for rules 5 | // 6 | // A rule is used to decide what to do with requests that are hitting the ORY Oathkeeper proxy server. A rule must 7 | // define the HTTP methods and the URL under which it will apply. A URL may not have more than one rule. If a URL 8 | // has no rule applied, the proxy server will return a 404 not found error. 9 | // 10 | // ORY Oathkeeper stores as many rules as required and iterates through them on every request. Rules are essential 11 | // to the way ORY Oathkeeper works. 12 | package rule 13 | -------------------------------------------------------------------------------- /rule/engine_regexp.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package rule 5 | 6 | import ( 7 | "errors" 8 | "hash/crc64" 9 | 10 | "github.com/dlclark/regexp2" 11 | 12 | "github.com/ory/ladon/compiler" 13 | ) 14 | 15 | type regexpMatchingEngine struct { 16 | compiled *regexp2.Regexp 17 | checksum uint64 18 | table *crc64.Table 19 | } 20 | 21 | func (re *regexpMatchingEngine) compile(pattern string) error { 22 | if re.table == nil { 23 | re.table = crc64.MakeTable(polynomial) 24 | } 25 | if checksum := crc64.Checksum([]byte(pattern), re.table); checksum != re.checksum { 26 | compiled, err := compiler.CompileRegex(pattern, '<', '>') 27 | if err != nil { 28 | return err 29 | } 30 | re.compiled = compiled 31 | re.checksum = checksum 32 | } 33 | return nil 34 | } 35 | 36 | // Checksum of a saved pattern. 37 | func (re *regexpMatchingEngine) Checksum() uint64 { 38 | return re.checksum 39 | } 40 | 41 | // IsMatching determines whether the input matches the pattern. 42 | func (re *regexpMatchingEngine) IsMatching(pattern, matchAgainst string) (bool, error) { 43 | if err := re.compile(pattern); err != nil { 44 | return false, err 45 | } 46 | return re.compiled.MatchString(matchAgainst) 47 | } 48 | 49 | // ReplaceAllString replaces all matches in `input` with `replacement`. 50 | func (re *regexpMatchingEngine) ReplaceAllString(pattern, input, replacement string) (string, error) { 51 | if err := re.compile(pattern); err != nil { 52 | return "", err 53 | } 54 | return re.compiled.Replace(input, replacement, -1, -1) 55 | } 56 | 57 | // FindStringSubmatch returns all captures in matchAgainst following the pattern 58 | func (re *regexpMatchingEngine) FindStringSubmatch(pattern, matchAgainst string) ([]string, error) { 59 | if err := re.compile(pattern); err != nil { 60 | return nil, err 61 | } 62 | 63 | m, _ := re.compiled.FindStringMatch(matchAgainst) 64 | if m == nil { 65 | return nil, errors.New("not match") 66 | } 67 | 68 | result := []string{} 69 | for _, group := range m.Groups()[1:] { 70 | result = append(result, group.String()) 71 | } 72 | 73 | return result, nil 74 | } 75 | -------------------------------------------------------------------------------- /rule/engine_regexp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package rule 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestFindStringSubmatch(t *testing.T) { 13 | type args struct { 14 | pattern string 15 | matchAgainst string 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | want []string 21 | wantErr bool 22 | }{ 23 | { 24 | name: "bad pattern", 25 | args: args{ 26 | pattern: `urn:foo:<.?>`, 27 | matchAgainst: "urn:foo:user", 28 | }, 29 | want: nil, 30 | wantErr: true, 31 | }, 32 | { 33 | name: "one group", 34 | args: args{ 35 | pattern: `urn:foo:<.*>`, 36 | matchAgainst: "urn:foo:user", 37 | }, 38 | want: []string{"user"}, 39 | wantErr: false, 40 | }, 41 | { 42 | name: "several groups", 43 | args: args{ 44 | pattern: `urn:foo:<.*>:<.*>`, 45 | matchAgainst: "urn:foo:user:one", 46 | }, 47 | want: []string{"user", "one"}, 48 | wantErr: false, 49 | }, 50 | { 51 | name: "classic foo bar", 52 | args: args{ 53 | pattern: `urn:foo:`, 54 | matchAgainst: "urn:foo:bar", 55 | }, 56 | want: []string{"bar"}, 57 | wantErr: false, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | regexpEngine := new(regexpMatchingEngine) 63 | got, err := regexpEngine.FindStringSubmatch(tt.args.pattern, tt.args.matchAgainst) 64 | if (err != nil) != tt.wantErr { 65 | t.Errorf("FindStringSubmatch() error = %v, wantErr %v", err, tt.wantErr) 66 | return 67 | } 68 | 69 | assert.ElementsMatch(t, got, tt.want, "FindStringSubmatch() got = %v, want %v", got, tt.want) 70 | }) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /rule/fetcher.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package rule 5 | 6 | import ( 7 | "context" 8 | 9 | "gocloud.dev/blob" 10 | ) 11 | 12 | type Fetcher interface { 13 | Watch(ctx context.Context) error 14 | } 15 | 16 | type URLMuxSetter interface { 17 | SetURLMux(mux *blob.URLMux) 18 | } 19 | -------------------------------------------------------------------------------- /rule/matcher.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package rule 5 | 6 | import ( 7 | "context" 8 | "net/url" 9 | ) 10 | 11 | type ( 12 | Protocol int 13 | 14 | Matcher interface { 15 | Match(ctx context.Context, method string, u *url.URL, protocol Protocol) (*Rule, error) 16 | } 17 | ) 18 | 19 | const ( 20 | ProtocolHTTP Protocol = iota 21 | ProtocolGRPC 22 | ) 23 | -------------------------------------------------------------------------------- /rule/matching_engine.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package rule 5 | 6 | import ( 7 | "hash/crc64" 8 | 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | // polynomial for crc64 table which is used for checking crc64 checksum 13 | const polynomial = crc64.ECMA 14 | 15 | // common errors for MatchingEngine. 16 | var ( 17 | ErrUnbalancedPattern = errors.New("unbalanced pattern") 18 | ErrMethodNotImplemented = errors.New("the method is not implemented") 19 | ErrUnknownMatchingStrategy = errors.New("unknown matching strategy") 20 | ) 21 | 22 | // MatchingEngine describes an interface of matching engine such as regexp or glob. 23 | type MatchingEngine interface { 24 | IsMatching(pattern, matchAgainst string) (bool, error) 25 | ReplaceAllString(pattern, input, replacement string) (string, error) 26 | FindStringSubmatch(pattern, matchAgainst string) ([]string, error) 27 | Checksum() uint64 28 | } 29 | -------------------------------------------------------------------------------- /rule/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package rule 5 | 6 | type Registry interface { 7 | RuleValidator() Validator 8 | RuleFetcher() Fetcher 9 | RuleRepository() Repository 10 | RuleMatcher() Matcher 11 | } 12 | -------------------------------------------------------------------------------- /rule/repository.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package rule 5 | 6 | import ( 7 | "context" 8 | "net/http" 9 | 10 | "github.com/ory/oathkeeper/driver/configuration" 11 | ) 12 | 13 | type Repository interface { 14 | List(ctx context.Context, limit, offset int) ([]Rule, error) 15 | Set(context.Context, []Rule) error 16 | Get(context.Context, string) (*Rule, error) 17 | Count(context.Context) (int, error) 18 | MatchingStrategy(context.Context) (configuration.MatchingStrategy, error) 19 | SetMatchingStrategy(context.Context, configuration.MatchingStrategy) error 20 | ReadyChecker(*http.Request) error 21 | } 22 | -------------------------------------------------------------------------------- /scripts/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | -------------------------------------------------------------------------------- /scripts/render-schemas.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -euxo pipefail 4 | 5 | ory_x_version="$(go list -f '{{.Version}}' -m github.com/ory/x)" 6 | 7 | sed "s!ory://tracing-config!https://raw.githubusercontent.com/ory/x/$ory_x_version/otelx/config.schema.json!g; 8 | s!ory://logging-config!https://raw.githubusercontent.com/ory/x/$ory_x_version/logrusx/config.schema.json!g; 9 | s!/.schema/config.schema.json!https://github.com/ory/oathkeeper/schema/config.schema.json!g" spec/config.schema.json > .schema/config.schema.json 10 | 11 | git commit --author="ory-bot <60093411+ory-bot@users.noreply.github.com>" -m "autogen: render config schema" .schema/config.schema.json || true 12 | -------------------------------------------------------------------------------- /scripts/run-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 6 | 7 | goimports -w $(go list -f {{.Dir}} ./... | grep -v vendor | grep -v oathkeeper$) 8 | goimports -w *.go 9 | -------------------------------------------------------------------------------- /scripts/run-genswag.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 6 | 7 | swagger generate spec -m -o ./spec/api.json 8 | -------------------------------------------------------------------------------- /scripts/run-mock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 6 | 7 | mockgen -package proxy -destination proxy/keto_sdk_mock.go -source ./vendor/github.com/ory/keto/sdk/go/keto/sdk_warden.go WardenSDK 8 | mockgen -package proxy -destination proxy/authenticator_oauth2_introspection_mock.go -source ./proxy/authenticator_oauth2_introspection.go authenticatorOAuth2IntrospectionHelper 9 | -------------------------------------------------------------------------------- /scripts/test-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 6 | 7 | toformat=$(goimports -l $(go list -f {{.Dir}} ./... | grep -v vendor | grep -v 'oathkeeper$')) 8 | [ -z "$toformat" ] && echo "All files are formatted correctly" 9 | [ -n "$toformat" ] && echo "Please use \`goimports\` to format the following files:" && echo $toformat && exit 1 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /spec/embed.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package spec 5 | 6 | import "embed" 7 | 8 | //go:embed *.json all:pipeline 9 | var FS embed.FS 10 | 11 | //go:embed config.schema.json 12 | var Config []byte 13 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.anonymous.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.anonymous.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsAnonymous" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.bearer_token.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.bearer_token.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsBearerToken" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.cookie_session.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.cookie_session.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsCookieSession" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.jwt.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.jwt.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsJwt" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.noop.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.noop.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsAnonymous" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.oauth2_client_credentials.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.oauth2_client_credentials.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsOauth2ClientCredentials" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.oauth2_introspection.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.oauth2_introspection.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsOauth2Introspection" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authenticators.unauthorized.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authenticators.unauthorized.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthenticatorsAnonymous" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authorizers.allow.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authorizers.allow.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#" 4 | } 5 | -------------------------------------------------------------------------------- /spec/pipeline/authorizers.deny.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authorizers.deny.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#" 4 | } 5 | -------------------------------------------------------------------------------- /spec/pipeline/authorizers.keto_engine_acp_ory.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authorizers.keto_engine_acp_ory.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthorizersKetoEngineAcpOry" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authorizers.remote.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authorizers.remote.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthorizersRemote" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/authorizers.remote_json.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/authorizers.remote_json.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configAuthorizersRemoteJSON" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/errors.json.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.cookie.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configErrorsJSON" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/errors.redirect.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.cookie.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configErrorsRedirect" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/errors.www_authenticate.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.cookie.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configErrorsWWWAuthenticate" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/mutators.cookie.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.cookie.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configMutatorsCookie" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/mutators.header.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.header.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configMutatorsHeader" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/mutators.hydrator.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.hydrator.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configMutatorsHydrator" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/mutators.id_token.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.id_token.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$ref": "/.schema/config.schema.json#/definitions/configMutatorsIdToken" 5 | } 6 | -------------------------------------------------------------------------------- /spec/pipeline/mutators.noop.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "/.schema/mutators.noop.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#" 4 | } 5 | -------------------------------------------------------------------------------- /test/bearer-token/.gitignore: -------------------------------------------------------------------------------- 1 | *.log -------------------------------------------------------------------------------- /test/bearer-token/config.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | access_rules: 8 | repositories: 9 | - file://./rules.1.json 10 | 11 | authenticators: 12 | noop: 13 | enabled: true 14 | bearer_token: 15 | enabled: true 16 | config: 17 | check_session_url: http://localhost:6662/session 18 | 19 | authorizers: 20 | allow: 21 | enabled: true 22 | deny: 23 | enabled: true 24 | 25 | mutators: 26 | noop: 27 | enabled: true 28 | -------------------------------------------------------------------------------- /test/bearer-token/okapi/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | "net/http" 10 | "os" 11 | 12 | "github.com/julienschmidt/httprouter" 13 | ) 14 | 15 | func main() { 16 | router := httprouter.New() 17 | 18 | router.POST("/test", test) 19 | router.GET("/session", session) 20 | 21 | port := os.Getenv("PORT") 22 | if port == "" { 23 | port = "6662" 24 | } 25 | 26 | server := http.Server{ 27 | Addr: fmt.Sprintf(":%s", port), 28 | Handler: router, 29 | } 30 | 31 | if err := server.ListenAndServe(); err != nil { 32 | panic(err) 33 | } 34 | } 35 | 36 | func test(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 37 | b, err := io.ReadAll(r.Body) 38 | if err != nil { 39 | w.WriteHeader(http.StatusInternalServerError) 40 | } 41 | 42 | if len(b) == 0 { 43 | w.WriteHeader(http.StatusBadRequest) 44 | return 45 | } 46 | 47 | w.WriteHeader(http.StatusOK) 48 | } 49 | 50 | func session(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 51 | w.WriteHeader(http.StatusOK) 52 | } 53 | -------------------------------------------------------------------------------- /test/bearer-token/rules.1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "test", 4 | "upstream": { 5 | "url": "http://127.0.0.1:6662" 6 | }, 7 | "match": { 8 | "url": "http://127.0.0.1:6060/test", 9 | "methods": ["POST"] 10 | }, 11 | "authenticators": [ 12 | { 13 | "handler": "bearer_token", 14 | "config": { 15 | "check_session_url": "http://127.0.0.1:6662/session", 16 | "preserve_path": true, 17 | "preserve_query": false, 18 | "force_method": "GET", 19 | "token_from": { 20 | "query_parameter": "token" 21 | } 22 | } 23 | } 24 | ], 25 | "authorizer": { 26 | "handler": "allow" 27 | }, 28 | "mutators": [ 29 | { 30 | "handler": "noop" 31 | } 32 | ] 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /test/bearer-token/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | waitport() { 6 | i=0 7 | while ! nc -z localhost "$1" ; do 8 | sleep 1 9 | if [ $i -gt 10 ]; then 10 | cat ./config.yaml 11 | cat ./oathkeeper.log 12 | exit 1 13 | fi 14 | i=$((i+1)) 15 | done 16 | } 17 | 18 | cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 19 | 20 | 21 | run_oathkekeper() { 22 | killall oathkeeper || true 23 | 24 | export OATHKEEPER_PROXY=http://127.0.0.1:6060 25 | export OATHKEEPER_API=http://127.0.0.1:6061 26 | 27 | go build -o . ../.. 28 | LOG_LEVEL=debug ./oathkeeper --config ./config.yaml serve > ./oathkeeper.log 2>&1 & 29 | 30 | waitport 6060 31 | waitport 6061 32 | } 33 | 34 | run_api(){ 35 | killall okapi || true 36 | 37 | PORT=6662 go run ./okapi > ./api.log 2>&1 & 38 | 39 | waitport 6662 40 | } 41 | 42 | SUCCESS_TEST=() 43 | FAILED_TEST=() 44 | 45 | run_test() { 46 | label=$1 47 | shift 1 48 | 49 | result="0" 50 | "$@" || result="1" 51 | 52 | if [[ "$result" -eq "0" ]]; then 53 | SUCCESS_TEST+=("$label") 54 | else 55 | FAILED_TEST+=("$label") 56 | fi 57 | } 58 | 59 | function finish { 60 | echo ::group::Config 61 | cat ./config.yaml 62 | cat ./rules.1.json 63 | echo ::endgroup:: 64 | echo ::group::Log 65 | cat ./oathkeeper.log 66 | echo ::endgroup:: 67 | } 68 | trap finish EXIT 69 | 70 | run_oathkekeper 71 | run_api 72 | 73 | curl -X POST -f http://127.0.0.1:6060/test?token=token -F fk=fv -H "Content-Type: application/x-www-form-urlencoded" -i 74 | 75 | kill %1 || true 76 | 77 | trap - EXIT 78 | -------------------------------------------------------------------------------- /test/e2e/.gitignore: -------------------------------------------------------------------------------- 1 | *.log -------------------------------------------------------------------------------- /test/e2e/config.yml: -------------------------------------------------------------------------------- 1 | access_rules: 2 | repositories: 3 | - file://e2e-rules.json 4 | 5 | mutators: 6 | id_token: 7 | enabled: true 8 | config: 9 | issuer_url: https://my-oathkeeper/ 10 | jwks_url: file://./jwks-idt.json 11 | 12 | authorizers: 13 | allow: 14 | enabled: true 15 | 16 | authenticators: 17 | jwt: 18 | enabled: true 19 | config: 20 | jwks_urls: 21 | - file://./jwks-authn.json 22 | scope_strategy: none 23 | 24 | serve: 25 | proxy: 26 | port: 6660 27 | api: 28 | port: 6661 29 | 30 | log: 31 | level: debug 32 | -------------------------------------------------------------------------------- /test/e2e/e2e-rules.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "jwt-rule", 4 | "upstream": { 5 | "url": "http://127.0.0.1:6662" 6 | }, 7 | "match": { 8 | "url": "http://127.0.0.1:<6660|6661>/jwt", 9 | "methods": ["GET"] 10 | }, 11 | "authenticators": [ 12 | { 13 | "handler": "jwt" 14 | } 15 | ], 16 | "authorizer": { 17 | "handler": "allow" 18 | }, 19 | "mutators": [ 20 | { 21 | "handler": "id_token" 22 | } 23 | ] 24 | } 25 | ] 26 | -------------------------------------------------------------------------------- /test/e2e/okapi/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "net/http" 9 | "os" 10 | 11 | jwtmiddleware "github.com/auth0/go-jwt-middleware/v2" 12 | "github.com/auth0/go-jwt-middleware/v2/jwks" 13 | "github.com/auth0/go-jwt-middleware/v2/validator" 14 | "github.com/julienschmidt/httprouter" 15 | "github.com/urfave/negroni" 16 | 17 | "github.com/ory/oathkeeper/x" 18 | "github.com/ory/x/urlx" 19 | ) 20 | 21 | var ( 22 | jwksProvider *jwks.Provider 23 | jwtValidator *validator.Validator 24 | ) 25 | 26 | func init() { 27 | var err error 28 | u := x.ParseURLOrPanic(os.Getenv("OATHKEEPER_API")) 29 | jwksProvider = jwks.NewProvider(urlx.AppendPaths(u, "/.well-known/jwks.json")) 30 | jwtValidator, err = validator.New( 31 | jwksProvider.KeyFunc, 32 | validator.RS256, 33 | jwksProvider.IssuerURL.String(), 34 | []string{jwksProvider.IssuerURL.String()}, 35 | ) 36 | if err != nil { 37 | panic(err) 38 | } 39 | } 40 | 41 | var jwtm = jwtmiddleware.New(jwtValidator.ValidateToken) 42 | 43 | func main() { 44 | router := httprouter.New() 45 | 46 | router.GET("/jwt", jwtHandler) 47 | 48 | port := os.Getenv("PORT") 49 | if port == "" { 50 | port = "6677" 51 | } 52 | 53 | n := negroni.Classic() 54 | n.UseHandler(router) 55 | n.UseFunc(func(_ http.ResponseWriter, _ *http.Request, next http.HandlerFunc) { 56 | jwtm.CheckJWT(next) 57 | }) 58 | server := http.Server{ 59 | Addr: fmt.Sprintf(":%s", port), 60 | Handler: n, 61 | } 62 | 63 | if err := server.ListenAndServe(); err != nil { 64 | panic(err) 65 | } 66 | } 67 | 68 | func jwtHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) { 69 | _, _ = w.Write([]byte("ok")) 70 | } 71 | -------------------------------------------------------------------------------- /test/e2e/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | waitport() { 6 | i=0 7 | while ! nc -z localhost "$1" ; do 8 | sleep 1 9 | if [ $i -gt 10 ]; then 10 | cat ./oathkeeper.e2e.log 11 | echo "-----" 12 | cat ./api.e2e.log 13 | exit 1 14 | fi 15 | i=$((i+1)) 16 | done 17 | } 18 | 19 | cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 20 | 21 | killall oathkeeper || true 22 | killall okapi || true 23 | killall okclient || true 24 | 25 | export OATHKEEPER_PROXY=http://127.0.0.1:6660 26 | export OATHKEEPER_API=http://127.0.0.1:6661 27 | export GO111MODULE=on 28 | 29 | go build -o . ../.. 30 | ./oathkeeper --config ./config.yml serve > ./oathkeeper.e2e.log 2>&1 & 31 | PORT=6662 go run ./okapi > ./api.e2e.log 2>&1 & 32 | 33 | waitport 6660 34 | waitport 6661 35 | waitport 6662 36 | 37 | function finish { 38 | echo ::group::Oathkeeper Log 39 | cat ./oathkeeper.e2e.log 40 | echo ::endgroup:: 41 | echo ::group::OK API Log 42 | cat ./api.e2e.log 43 | echo ::endgroup:: 44 | } 45 | trap finish EXIT 46 | 47 | go run ./okclient 48 | 49 | kill %1 || true 50 | kill %2 || true 51 | 52 | trap - EXIT 53 | -------------------------------------------------------------------------------- /test/forwarded-header/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | -------------------------------------------------------------------------------- /test/forwarded-header/config.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | access_rules: 8 | repositories: 9 | - file://./rules.1.json 10 | 11 | authenticators: 12 | noop: 13 | enabled: true 14 | anonymous: 15 | enabled: true 16 | 17 | authorizers: 18 | allow: 19 | enabled: true 20 | deny: 21 | enabled: true 22 | 23 | mutators: 24 | noop: 25 | enabled: true 26 | -------------------------------------------------------------------------------- /test/forwarded-header/rules.1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "test-rule-http", 4 | "upstream": { 5 | "url": "https://example.com/", 6 | "strip_path": "http" 7 | }, 8 | "match": { 9 | "url": "http://127.0.0.1:6060/http", 10 | "methods": ["GET"] 11 | }, 12 | "authenticators": [ 13 | { 14 | "handler": "noop" 15 | } 16 | ], 17 | "authorizer": { 18 | "handler": "allow" 19 | }, 20 | "mutators": [ 21 | { 22 | "handler": "noop" 23 | } 24 | ] 25 | }, 26 | { 27 | "id": "test-rule-https", 28 | "upstream": { 29 | "url": "https://example.com/", 30 | "strip_path": "https" 31 | }, 32 | "match": { 33 | "url": "https://127.0.0.1:6060/https", 34 | "methods": ["GET"] 35 | }, 36 | "authenticators": [ 37 | { 38 | "handler": "noop" 39 | } 40 | ], 41 | "authorizer": { 42 | "handler": "allow" 43 | }, 44 | "mutators": [ 45 | { 46 | "handler": "noop" 47 | } 48 | ] 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /test/reload/.gitignore: -------------------------------------------------------------------------------- 1 | config.yaml 2 | *.log 3 | rules.3.json 4 | -------------------------------------------------------------------------------- /test/reload/config.1.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | # access_rules: 8 | # repositories: [] 9 | 10 | authenticators: 11 | noop: 12 | enabled: true 13 | anonymous: 14 | enabled: true 15 | 16 | authorizers: 17 | allow: 18 | enabled: true 19 | deny: 20 | enabled: true 21 | 22 | mutators: 23 | noop: 24 | enabled: true 25 | -------------------------------------------------------------------------------- /test/reload/config.2.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | access_rules: 8 | repositories: 9 | - file://./rules.1.json 10 | 11 | authenticators: 12 | noop: 13 | enabled: true 14 | anonymous: 15 | enabled: true 16 | 17 | authorizers: 18 | allow: 19 | enabled: true 20 | deny: 21 | enabled: true 22 | 23 | mutators: 24 | noop: 25 | enabled: true 26 | -------------------------------------------------------------------------------- /test/reload/config.3.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | access_rules: 8 | repositories: 9 | - file://./rules.2.json 10 | 11 | authenticators: 12 | noop: 13 | enabled: true 14 | anonymous: 15 | enabled: true 16 | 17 | authorizers: 18 | allow: 19 | enabled: true 20 | deny: 21 | enabled: true 22 | 23 | mutators: 24 | noop: 25 | enabled: true 26 | -------------------------------------------------------------------------------- /test/reload/config.4.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | access_rules: 8 | repositories: 9 | - file://./rules.2.json 10 | 11 | authenticators: 12 | noop: 13 | enabled: true 14 | anonymous: 15 | enabled: false 16 | 17 | authorizers: 18 | allow: 19 | enabled: true 20 | deny: 21 | enabled: true 22 | 23 | mutators: 24 | noop: 25 | enabled: true 26 | -------------------------------------------------------------------------------- /test/reload/config.5.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | access_rules: 8 | repositories: 9 | - file://./rules.1.json 10 | - file://./rules.3.json 11 | 12 | authenticators: 13 | noop: 14 | enabled: true 15 | anonymous: 16 | enabled: true 17 | 18 | authorizers: 19 | allow: 20 | enabled: true 21 | deny: 22 | enabled: true 23 | 24 | mutators: 25 | noop: 26 | enabled: true 27 | -------------------------------------------------------------------------------- /test/reload/config.6.yaml: -------------------------------------------------------------------------------- 1 | serve: 2 | api: 3 | port: 6061 4 | proxy: 5 | port: 6060 6 | 7 | # access_rules: 8 | # repositories: [] 9 | 10 | authenticators: 11 | noop: 12 | enabled: true 13 | anonymous: 14 | enabled: true 15 | 16 | authorizers: 17 | allow: 18 | enabled: true 19 | deny: 20 | enabled: true 21 | 22 | mutators: 23 | noop: 24 | enabled: true 25 | -------------------------------------------------------------------------------- /test/reload/rules.1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "test-rule-1", 4 | "upstream": { 5 | "url": "https://example.com/", 6 | "strip_path": "/rules" 7 | }, 8 | "match": { 9 | "url": "http://127.0.0.1:6060/rules", 10 | "methods": ["GET"] 11 | }, 12 | "authenticators": [ 13 | { 14 | "handler": "noop" 15 | } 16 | ], 17 | "authorizer": { 18 | "handler": "allow" 19 | }, 20 | "mutators": [ 21 | { 22 | "handler": "noop" 23 | } 24 | ] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /test/reload/rules.2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "test-rule-1", 4 | "upstream": { 5 | "url": "https://example.com/", 6 | "strip_path": "/rules" 7 | }, 8 | "match": { 9 | "url": "http://127.0.0.1:6060/rules", 10 | "methods": ["GET"] 11 | }, 12 | "authenticators": [ 13 | { 14 | "handler": "anonymous" 15 | } 16 | ], 17 | "authorizer": { 18 | "handler": "deny" 19 | }, 20 | "mutators": [ 21 | { 22 | "handler": "noop" 23 | } 24 | ] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /test/reload/rules.3.1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "test-rule-2", 4 | "upstream": { 5 | "url": "https://example.com/", 6 | "strip_path": "/other-rules" 7 | }, 8 | "match": { 9 | "url": "http://127.0.0.1:6060/other-rules", 10 | "methods": ["GET"] 11 | }, 12 | "authenticators": [ 13 | { 14 | "handler": "anonymous" 15 | } 16 | ], 17 | "authorizer": { 18 | "handler": "allow" 19 | }, 20 | "mutators": [ 21 | { 22 | "handler": "noop" 23 | } 24 | ] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /test/reload/rules.3.2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "test-rule-2", 4 | "upstream": { 5 | "url": "https://example.com/", 6 | "strip_path": "/other-rules" 7 | }, 8 | "match": { 9 | "url": "http://127.0.0.1:6060/other-rules", 10 | "methods": ["GET"] 11 | }, 12 | "authenticators": [ 13 | { 14 | "handler": "anonymous" 15 | } 16 | ], 17 | "authorizer": { 18 | "handler": "deny" 19 | }, 20 | "mutators": [ 21 | { 22 | "handler": "noop" 23 | } 24 | ] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /test/stub/jwks-ecdsa.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kid": "21d9eefe-34b6-42b3-b643-2b30e0ab59e0", 5 | "use": "sig", 6 | "kty": "EC", 7 | "crv": "P-256", 8 | "alg": "ES256", 9 | "x": "kiqIyeqJSFUSVpXkqFFzs1ZjmNv0zcRVFVwBAxt_g9U", 10 | "y": "0bpeB75l6lJQs6t5tUkQcaa1yNd8W2o50zYWd-xjeFU" 11 | }, 12 | { 13 | "kid": "76a19c1b-5dbe-46cb-b3f3-a8c38c8bb8eb", 14 | "use": "sig", 15 | "kty": "EC", 16 | "crv": "P-256", 17 | "alg": "ES256", 18 | "x": "kiqIyeqJSFUSVpXkqFFzs1ZjmNv0zcRVFVwBAxt_g9U", 19 | "y": "0bpeB75l6lJQs6t5tUkQcaa1yNd8W2o50zYWd-xjeFU", 20 | "d": "6vMo_q1f-OvMBDbnPL7d2cTRIi-izFY-G5j8AhJmZ3M" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /test/stub/jwks-hs.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "oct", 5 | "kid": "64f48a05-7ea5-4aaf-80cf-f06b0033b477", 6 | "k": "-2UH7JcKyPpR4HLOJCmRbsu1-x0gltAhfJaLeFbAaIc", 7 | "alg": "HS256", 8 | "use": "sig" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /test/stub/jwks-rsa-single.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "alg": "RS256", 5 | "p": "0Sd3rPNk_mUxTDFFeuWVRV_aAyfdA_gS_2Zg1lW6Un2UWQcbzBsYu0WoYPh35vEpuX0rS-SdvcVlqt7FITVTCCsT-U3LmPBzyTs3ozurs-3lxy94laHtXsHj9Jo68eAsGgk_yiIwDChNvxAIHkxTTFltTNoRK9MjjDzjMbiBfj0", 6 | "kty": "RSA", 7 | "q": "oZMVhOc-4sl4lyCVqPfBbzLhOyeOrUhvV7VIed95jAeq1rXrDr9w97lpaNX3-joH5pgGHHVQqMjyMBOA07TZloCQuAtUF8XjgK04pfY_1F_ds_L486iuAm2EvA3m0H8zqs6p0iWMrmXC9ukboc_BVVsCfh08ePcz1yAVlX92O-k", 8 | "d": "YwxzQRGOIn_3r7iPC4bLG_Z4oA6-uFSwcko-eJJTtFKEQFbaO23oyWwC1R5yxSSFvv1devRsRtd89QALQ1RmcrZPGQwh-ZEwXNQZbShtkTUWFWYefL0qY1XKbrnnoBlZYsUu92BMn2txnlikbKxxcX6XSAGB-L9umdFTd4zzH-amaqLKEyooKX4tbYsDf78mrP1f5DxFxaSghlS3v4IcaeD4tLFUw2_ezPVV_BW9xG5srGkDPFRuzxoqWDpn0YV7ag98f1lE9ixXDJzjitL-crGhxV4B-FTaBGUYawKpQkeZDkZfA6dwjlQOZqZAje-fbf9pcSI9cH6-VcMBv8gEQQ", 9 | "e": "AQAB", 10 | "use": "sig", 11 | "kid": "81be3441-5303-4c52-b00d-bbdfadc75633", 12 | "qi": "UWcO18lAQtnVbKQDctba9FtdQ9eGVyPmq61SiyefrphSH_f9llVLyc7dYC5ra56NwLOfkkSv2SvjtRjJ1uYrq2nceVamND1bn4F6uC2kqShDQ5OEteFdNz-wItNEk9RwyA-waKUFfqoVuLvKAMOCwtGAZwu7ll1HuWz2gVfl7Kg", 13 | "dp": "tcC9X98W2XcoUtTSUwzlHadA9dMTXhqar7TQ3BEdXAfi_UaPIlw9rqfGvZXN5DOYFaevE0F0zm-WfNkfp08Ge6ersM6o6ZuykqFSJRh6il5meiRHfoh7lQeax3mrVXt_95QoCciRxk7T2P_efi3HiSaBM_KgT1wonPpgWFnEteU", 14 | "dq": "X9ZXiEqF0Y1BWxr3yo6SjPu3_xd6mKwDZxnfUHLOrNGLcuna8Bd70WSH8qgf-6taLlv0HAM2AbeXeA7JdkmqWowNyyzlB2jYheW5WS-UMstePLOdGSHvfP8rIUR5qSgaJHGK51xgcr1nhH9GZZdOjOt7sgRzbyjp9etRAijln6k", 15 | "n": "hAH9gk97KfX-umWitXhTD32qYoPfQoTseOBnTUA2V_jhlKBxC2w2jjDFauLLMtaQmXKQ1Soc0Q0Q5rbmD3hx5xkyMWP7PXqB3nnKYnyFXZDRIK8xG4RiVWZz82c5-Vy6BYLLVB1h9xEcwcIlcswAx9PQw4qlarhPWJMWMUroUS_YgJrGlDRCaGO5649zQpajJOqPZZwJDlX0aunjlJ4U6arcLidGaedzPNA6k2jxxDBZs7Qo5aidZ3BHaQlZQT8LI_AcHgDR_x4ExExYL9HSR4miYfH3BnxH4U3KKASLaZe9iz724rjomyVclFLW_GCAKnC6HKDxV-IUlAGYORH0hQ" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/stub/rules.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "test-rule-1", 4 | "upstream": { 5 | "preserve_host": true, 6 | "strip_path": "/api", 7 | "url": "https://mybackend.com/api" 8 | }, 9 | "match": { 10 | "url": "myproxy.com/api", 11 | "methods": ["GET", "POST"] 12 | }, 13 | "authenticators": [ 14 | { 15 | "handler": "noop" 16 | }, 17 | { 18 | "handler": "anonymous" 19 | } 20 | ], 21 | "authorizer": { 22 | "handler": "allow" 23 | }, 24 | "mutators": [ 25 | { 26 | "handler": "noop" 27 | } 28 | ] 29 | }, 30 | { 31 | "id": "test-rule-2", 32 | "upstream": { 33 | "preserve_host": true, 34 | "strip_path": "/api", 35 | "url": "https://mybackend.com/api" 36 | }, 37 | "match": { 38 | "url": "myproxy.com/api", 39 | "methods": ["GET", "POST"] 40 | }, 41 | "authenticators": [ 42 | { 43 | "handler": "noop" 44 | }, 45 | { 46 | "handler": "anonymous" 47 | } 48 | ], 49 | "authorizer": { 50 | "handler": "deny" 51 | }, 52 | "mutators": [ 53 | { 54 | "handler": "id_token" 55 | }, 56 | { 57 | "handler": "header", 58 | "config": { 59 | "headers": { 60 | "X-User": "{{ print .Subject }}" 61 | } 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | "id": "test-rule-3", 68 | "upstream": { 69 | "preserve_host": true, 70 | "strip_path": "/api", 71 | "url": "https://mybackend.com/api" 72 | }, 73 | "match": { 74 | "url": "myproxy.com/api", 75 | "methods": ["GET", "POST"] 76 | }, 77 | "authenticators": [ 78 | { 79 | "handler": "noop" 80 | }, 81 | { 82 | "handler": "anonymous" 83 | } 84 | ], 85 | "authorizer": { 86 | "handler": "allow" 87 | }, 88 | "mutators": [ 89 | { 90 | "handler": "id_token", 91 | "config": { 92 | "jwks_url": "http://stub/" 93 | } 94 | } 95 | ] 96 | } 97 | ] 98 | -------------------------------------------------------------------------------- /test/stub/rules.yaml: -------------------------------------------------------------------------------- 1 | - id: test-rule-1-yaml 2 | upstream: 3 | preserve_host: true 4 | strip_path: /api 5 | url: https://mybackend.com/api 6 | match: 7 | url: myproxy.com/api 8 | methods: 9 | - GET 10 | - POST 11 | authenticators: 12 | - handler: noop 13 | - handler: anonymous 14 | authorizer: 15 | handler: allow 16 | mutators: 17 | - handler: noop 18 | -------------------------------------------------------------------------------- /test/update/config_default.yaml: -------------------------------------------------------------------------------- 1 | access_rules: 2 | repositories: file://../test/update/rules_glob.yaml 3 | authenticators: 4 | anonymous: 5 | enabled: true 6 | authorizers: 7 | allow: 8 | enabled: true 9 | mutators: 10 | noop: 11 | enabled: true 12 | -------------------------------------------------------------------------------- /test/update/config_error.yaml: -------------------------------------------------------------------------------- 1 | access_rules: 2 | repositories: file://../test/update/rules_glob.yaml 3 | matching_strategy: UNKNOWN 4 | authenticators: 5 | anonymous: 6 | enabled: true 7 | authorizers: 8 | allow: 9 | enabled: true 10 | mutators: 11 | noop: 12 | enabled: true 13 | -------------------------------------------------------------------------------- /test/update/config_glob.yaml: -------------------------------------------------------------------------------- 1 | access_rules: 2 | repositories: file://../test/update/rules_glob.yaml 3 | matching_strategy: glob 4 | authenticators: 5 | anonymous: 6 | enabled: true 7 | authorizers: 8 | allow: 9 | enabled: true 10 | mutators: 11 | noop: 12 | enabled: true 13 | -------------------------------------------------------------------------------- /test/update/config_no_repo.yaml: -------------------------------------------------------------------------------- 1 | access_rules: 2 | repositories: [] 3 | authenticators: 4 | anonymous: 5 | enabled: true 6 | authorizers: 7 | allow: 8 | enabled: true 9 | mutators: 10 | noop: 11 | enabled: true 12 | -------------------------------------------------------------------------------- /test/update/config_regexp.yaml: -------------------------------------------------------------------------------- 1 | access_rules: 2 | repositories: file://../test/update/rules_glob.yaml 3 | matching_strategy: regexp 4 | authenticators: 5 | anonymous: 6 | enabled: true 7 | authorizers: 8 | allow: 9 | enabled: true 10 | mutators: 11 | noop: 12 | enabled: true 13 | -------------------------------------------------------------------------------- /test/update/rules_glob.yaml: -------------------------------------------------------------------------------- 1 | - id: test-rule-1-glob 2 | match: 3 | url: myproxy.com/ 4 | methods: 5 | - GET 6 | - POST 7 | authenticators: 8 | - handler: anonymous 9 | authorizer: 10 | handler: allow 11 | mutators: 12 | - handler: noop 13 | -------------------------------------------------------------------------------- /x/compare.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | func OrDefaultString(val, defaultVal string) string { 7 | if val == "" { 8 | return defaultVal 9 | } 10 | return val 11 | } 12 | 13 | func IfThenElseString(c bool, thenVal, elseVal string) string { 14 | if c { 15 | return thenVal 16 | } 17 | return elseVal 18 | } 19 | -------------------------------------------------------------------------------- /x/deepcopy.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | ) 10 | 11 | // Deepcopy performs a deep copy of the given map m. 12 | func Deepcopy(m map[string]interface{}) (map[string]interface{}, error) { 13 | var buf bytes.Buffer 14 | enc := json.NewEncoder(&buf) 15 | dec := json.NewDecoder(&buf) 16 | err := enc.Encode(m) 17 | if err != nil { 18 | return nil, err 19 | } 20 | var copy map[string]interface{} 21 | err = dec.Decode(©) 22 | if err != nil { 23 | return nil, err 24 | } 25 | return copy, nil 26 | } 27 | -------------------------------------------------------------------------------- /x/header/header.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package header 5 | 6 | import "net/textproto" 7 | 8 | const ( 9 | AcceptEncoding = "Accept-Encoding" 10 | Authorization = "Authorization" 11 | Cookie = "Cookie" 12 | XForwardedHost = "X-Forwarded-Host" 13 | ) 14 | 15 | // Canonical returns the canonical format of the 16 | // MIME header key. The canonicalization converts the first 17 | // letter and any letter following a hyphen to upper case; 18 | // the rest are converted to lowercase. 19 | func Canonical(h string) string { 20 | return textproto.CanonicalMIMEHeaderKey(h) 21 | } 22 | -------------------------------------------------------------------------------- /x/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | import ( 7 | "go.opentelemetry.io/otel/trace" 8 | 9 | "github.com/ory/x/logrusx" 10 | 11 | "github.com/ory/herodot" 12 | ) 13 | 14 | type TestLoggerProvider struct{} 15 | 16 | func (lp *TestLoggerProvider) Logger() *logrusx.Logger { 17 | return logrusx.New("", "") 18 | } 19 | 20 | func (lp *TestLoggerProvider) Tracer() trace.Tracer { 21 | return nil 22 | } 23 | 24 | type RegistryLogger interface { 25 | Logger() *logrusx.Logger 26 | } 27 | 28 | type RegistryWriter interface { 29 | Writer() herodot.Writer 30 | } 31 | -------------------------------------------------------------------------------- /x/router.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | import ( 7 | "github.com/julienschmidt/httprouter" 8 | 9 | "github.com/ory/x/serverx" 10 | ) 11 | 12 | type RouterAPI struct { 13 | *httprouter.Router 14 | } 15 | 16 | func NewAPIRouter() *RouterAPI { 17 | router := httprouter.New() 18 | router.NotFound = serverx.DefaultNotFoundHandler 19 | return &RouterAPI{ 20 | Router: router, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /x/template.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | "text/template" 10 | 11 | "github.com/Masterminds/sprig/v3" 12 | ) 13 | 14 | // NewTemplate creates a template with additional functions 15 | func NewTemplate(id string) *template.Template { 16 | return template.New(id). 17 | // Implies that zero value will be used if a key is missing. 18 | Option("missingkey=zero"). 19 | Funcs(template.FuncMap{ 20 | "print": func(i interface{}) string { 21 | if i == nil { 22 | return "" 23 | } 24 | return fmt.Sprintf("%v", i) 25 | }, 26 | "printIndex": func(element interface{}, i int) string { 27 | if element == nil { 28 | return "" 29 | } 30 | 31 | list := reflect.ValueOf(element) 32 | 33 | if list.Kind() == reflect.Slice && i < list.Len() { 34 | return fmt.Sprintf("%v", list.Index(i)) 35 | } 36 | 37 | return "" 38 | }, 39 | }). 40 | Funcs(sprig.TxtFuncMap()) 41 | } 42 | -------------------------------------------------------------------------------- /x/testhelper.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | import ( 7 | "io" 8 | "os" 9 | "testing" 10 | ) 11 | 12 | // WriteFile writes the content to a new file in a temporary location and 13 | // returns the path. No cleanup is necessary. 14 | func WriteFile(t *testing.T, content string) string { 15 | f, err := os.CreateTemp(t.TempDir(), "config-*.yaml") 16 | if err != nil { 17 | t.Error(err) 18 | return "" 19 | } 20 | defer f.Close() 21 | io.WriteString(f, content) 22 | 23 | return f.Name() 24 | } 25 | -------------------------------------------------------------------------------- /x/url.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | import ( 7 | "net/url" 8 | 9 | "github.com/ory/x/logrusx" 10 | "github.com/ory/x/urlx" 11 | ) 12 | 13 | // ParseURLOrPanic parses a url or panics. 14 | // This is the same function as urlx.ParseOrPanic() except that it uses 15 | // urlx.Parse() instead of url.Parse() 16 | func ParseURLOrPanic(in string) *url.URL { 17 | out, err := urlx.Parse(in) 18 | if err != nil { 19 | panic(err.Error()) 20 | } 21 | return out 22 | } 23 | 24 | // ParseURLOrFatal parses a url or fatals. 25 | // This is the same function as urlx.ParseOrFatal() except that it uses 26 | // urlx.Parse() instead of url.Parse() 27 | func ParseURLOrFatal(l *logrusx.Logger, in string) *url.URL { 28 | out, err := urlx.Parse(in) 29 | if err != nil { 30 | l.WithError(err).Fatalf("Unable to parse url: %s", in) 31 | } 32 | return out 33 | } 34 | -------------------------------------------------------------------------------- /x/version.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Ory Corp 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package x 5 | 6 | const UnknownVersion = "master" 7 | 8 | var ( 9 | Version = "master" 10 | Date = "undefined" 11 | Commit = "undefined" 12 | ) 13 | --------------------------------------------------------------------------------