Skip to content

Commit

Permalink
dan/per-11149-add-ruff-to-pdp-repo (#226)
Browse files Browse the repository at this point in the history
* Added ruff

* Updated README.md

* Removed black from pre-commit

* Run ruff format

* Run ruff check --fix

* Changed setup.py to pathlib

* Fixed tests

* Fixed state.py

* Fixed offline_mode.py

* Renamed exceptions

* Fixed ruff checks

* Fixed (some) mypy errors

* Changed to annotated dependencies

* Changed to default_factory(dict)

* Fixed README.md and setup.py

* Fixed header type

* Removed extra arg

* Added install dev to README.md

* Fixed enforcer API
  • Loading branch information
danyi1212 authored Dec 22, 2024
1 parent ecc8174 commit b0a75c6
Show file tree
Hide file tree
Showing 43 changed files with 753 additions and 1,065 deletions.
2 changes: 0 additions & 2 deletions .isort.cfg

This file was deleted.

44 changes: 33 additions & 11 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
rev: v5.0.0
hooks:
- id: check-yaml
exclude: "charts/"
- id: end-of-file-fixer
exclude: "charts/"
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.3.0
- id: end-of-file-fixer
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-json
- id: check-toml
- id: check-yaml
exclude: "^charts/.*"
- id: check-xml
- id: check-merge-conflict
- id: mixed-line-ending
args: [ --fix=lf ]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.3
hooks:
- id: black
# - repo: https://github.com/pycqa/isort
# rev: 5.10.1
- id: ruff
args: [--fix]
files: \.py$
types: [ file ]
- id: ruff-format
files: \.py$
types: [ file ]

# - repo: https://github.com/pre-commit/mirrors-mypy
# rev: v1.13.0
# hooks:
# - id: isort
# - id: mypy
# pass_filenames: false
# additional_dependencies:
# - pydantic
# - types-requests
# files: \.py$
# types: [ file ]
45 changes: 26 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,49 @@
# Permit.io PDP
The PDP (Policy decision point) syncs with the authorization service and maintains up-to-date policy cache for open policy agent.

## Running locally (during development)
```
uvicorn horizon.main:app --reload --port=7000
```
## Running a PDP
PDPs are connected to your [Permit.io account](https://docs.permit.io/quickstart) using an API Key.
Check out the [Permit.io documentation](https://docs.permit.io/manage-your-account/projects-and-env#fetching-and-rotating-the-api-key) to learn how to get an Environment API Key.

you can pass environment variables to control the behavior of the sidecar:
e.g, running a local sidecar against production backend:
```
AUTHZ_SERVICE_URL=https://api.permit.io CLIENT_TOKEN=<CLIENT_TOKEN> uvicorn horizon.main:app --reload --port=7000
You can run a PDP in a docker container by running the following command:
```bash
docker run -it -p 7766:7000 -e PDP_API_KEY=<YOUR_API_KEY> -e PDP_DEBUG=True permitio/pdp-v2:latest
```

## Installing and running in production
### Deploying PDP to Production
You can deploy the PDP to production in multiple designs. See the [Permit.io documentation](https://docs.permit.io/concepts/pdp/overview) for more information.

Pull the image from docker hub
```
docker pull permitio/pdp-v2
## Contributing

### Setting up the development environment
1. Clone the repository
2. Install the dependencies
```bash
pip install ".[dev]"
```

Run the image: don't forget to pass your authorization service API KEY:
### Running locally (during development)
```
docker run -it -e "CLIENT_TOKEN=<YOUR API KEY>" -p 7000:7000 permitio/pdp-v2
PDP_API_KEY=<YOUR_API_KEY> uvicorn horizon.main:app --reload --port=7000
```

By default the image exposes port 7000 but you can change it.
You can pass environment variables to control the behavior of the PDP image.
For example, running a local PDP against the Permit API:
```
PDP_CONTROL_PLANE=https://api.permit.io PDP_API_KEY=<YOUR_API_KEY> uvicorn horizon.main:app --reload --port=7000
```

## Building the docker image yourself
on arm architecture:
## Building a Custom PDP Docker image
For ARM architecture:
```
VERSION=<TAG> make build-arm64
```
on amd64 architecture:
For AMD64 architecture:
```
VERSION=<TAG> make build-amd64
```

## Running the image in development mode
### Running the image in development mode
```
VERSION=<TAG> API_KEY=<PDP_API_KEY> make run
```
19 changes: 7 additions & 12 deletions horizon/authentication.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
from typing import Annotated

from fastapi import Header, HTTPException, status

from horizon.config import MOCK_API_KEY, sidecar_config
from horizon.startup.api_keys import get_env_api_key


def enforce_pdp_token(authorization=Header(None)):
def enforce_pdp_token(authorization: Annotated[str | None, Header()]):
if authorization is None:
raise HTTPException(
status.HTTP_401_UNAUTHORIZED, detail="Missing Authorization header"
)
raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="Missing Authorization header")
schema, token = authorization.split(" ")

if schema.strip().lower() != "bearer" or token.strip() != get_env_api_key():
raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="Invalid PDP token")


def enforce_pdp_control_key(authorization=Header(None)):
def enforce_pdp_control_key(authorization: Annotated[str | None, Header()]):
if sidecar_config.CONTAINER_CONTROL_KEY == MOCK_API_KEY:
raise HTTPException(
status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Control API disabled. Set a PDP_CONTAINER_CONTROL_KEY variable to enable.",
)

if authorization is None:
raise HTTPException(
status.HTTP_401_UNAUTHORIZED, detail="Missing Authorization header"
)
raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="Missing Authorization header")
schema, token = authorization.split(" ")

if (
schema.strip().lower() != "bearer"
or token.strip() != sidecar_config.CONTAINER_CONTROL_KEY
):
if schema.strip().lower() != "bearer" or token.strip() != sidecar_config.CONTAINER_CONTROL_KEY:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="Invalid PDP token")
54 changes: 25 additions & 29 deletions horizon/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,17 @@ class ApiKeyLevel(str):


class SidecarConfig(Confi):
def __new__(cls, prefix=None, is_model=True):
def __new__(cls, *, prefix=None, is_model=True): # noqa: ARG003
"""creates a singleton object, if it is not created,
or else returns the previous singleton object"""
if not hasattr(cls, "instance"):
cls.instance = super(SidecarConfig, cls).__new__(cls)
cls.instance = super().__new__(cls)
return cls.instance

FACTDB_ENABLED = confi.bool(
"FACTDB_ENABLED",
False,
description="if true, the sidecar will enable the FactDB service to manage the PDP data in "
"FactDB",
description="if true, the sidecar will enable the FactDB service to manage the PDP data in " "FactDB",
)

FACTDB_BINARY_PATH = confi.str(
Expand Down Expand Up @@ -79,12 +78,8 @@ def __new__(cls, prefix=None, is_model=True):
)

# backend api url, where proxy requests go
BACKEND_SERVICE_URL = confi.str(
"BACKEND_SERVICE_URL", confi.delay("{CONTROL_PLANE}/v1")
)
BACKEND_LEGACY_URL = confi.str(
"BACKEND_LEGACY_URL", confi.delay("{CONTROL_PLANE}/sdk")
)
BACKEND_SERVICE_URL = confi.str("BACKEND_SERVICE_URL", confi.delay("{CONTROL_PLANE}/v1"))
BACKEND_LEGACY_URL = confi.str("BACKEND_LEGACY_URL", confi.delay("{CONTROL_PLANE}/sdk"))

# backend route to fetch policy data topics
REMOTE_CONFIG_ENDPOINT = confi.str("REMOTE_CONFIG_ENDPOINT", "/v2/pdps/me/config")
Expand All @@ -103,25 +98,23 @@ def __new__(cls, prefix=None, is_model=True):
ORG_API_KEY = confi.str(
"ORG_API_KEY",
None,
description="set this to your organization's API key if you prefer to use the organization level API key. If not set, the PDP will use the project level API key",
description="set this to your organization's API key if you prefer to use the organization level API key. "
"By default, the PDP will use the project level API key",
)

# access token to your project
PROJECT_API_KEY = confi.str(
"PROJECT_API_KEY",
None,
description="set this to your project's API key if you prefer to use the project level API key. If not set, the PDP will use the default project API key",
description="set this to your project's API key if you prefer to use the project level API key. "
"By default, the PDP will use the default project API key",
)

# chosen project id/key to use for the PDP
ACTIVE_PROJECT = confi.str(
"ACTIVE_PROJECT", None, description="the project id/key to use for the PDP"
)
ACTIVE_PROJECT = confi.str("ACTIVE_PROJECT", None, description="the project id/key to use for the PDP")

# chosen environment id/key to use for the PDP
ACTIVE_ENV = confi.str(
"ACTIVE_ENV", None, description="the environment id/key to use for the PDP"
)
ACTIVE_ENV = confi.str("ACTIVE_ENV", None, description="the environment id/key to use for the PDP")

# access token to perform system control operations
CONTAINER_CONTROL_KEY = confi.str("CONTAINER_CONTROL_KEY", MOCK_API_KEY)
Expand All @@ -138,7 +131,8 @@ def __new__(cls, prefix=None, is_model=True):
ENABLE_OFFLINE_MODE = confi.bool(
"ENABLE_OFFLINE_MODE",
False,
description="if true, sidecar will use a file backup to restore configuration and policy data when cloud services are unavailable",
description="When true, sidecar will use a file backup to restore configuration and policy data when "
"cloud services are unavailable",
)

OFFLINE_MODE_BACKUP_DIR = confi.str(
Expand All @@ -164,9 +158,7 @@ def __new__(cls, prefix=None, is_model=True):
)

# centralized logging
CENTRAL_LOG_DRAIN_URL = confi.str(
"CENTRAL_LOG_DRAIN_URL", "https://listener.logz.io:8071"
)
CENTRAL_LOG_DRAIN_URL = confi.str("CENTRAL_LOG_DRAIN_URL", "https://listener.logz.io:8071")
CENTRAL_LOG_DRAIN_TIMEOUT = confi.int("CENTRAL_LOG_DRAIN_TIMEOUT", 5)
CENTRAL_LOG_TOKEN = confi.str("CENTRAL_LOG_TOKEN", None)
CENTRAL_LOG_ENABLED = confi.bool("CENTRAL_LOG_ENABLED", False)
Expand Down Expand Up @@ -216,7 +208,8 @@ def __new__(cls, prefix=None, is_model=True):
OPA_DECISION_LOG_CONSOLE = confi.bool(
"OPA_DECISION_LOG_CONSOLE",
False,
description="if true, OPA decision logs will also be printed to console (only relevant if `OPA_DECISION_LOG_ENABLED` is true)",
description="if true, OPA decision logs will also be printed to console "
"(only relevant if `OPA_DECISION_LOG_ENABLED` is true)",
)
OPA_DECISION_LOG_INGRESS_ROUTE = confi.str(
"OPA_DECISION_LOG_INGRESS_ROUTE",
Expand Down Expand Up @@ -293,7 +286,7 @@ def parse_callbacks(value: Any) -> list[CallbackEntry]:
# non configurable values -------------------------------------------------

# redoc configuration (openapi schema)
OPENAPI_TAGS_METADATA = [
OPENAPI_TAGS_METADATA = [ # noqa: RUF012
{
"name": "Authorization API",
"description": "Authorization queries to OPA. These queries are answered locally by OPA "
Expand All @@ -311,11 +304,14 @@ def parse_callbacks(value: Any) -> list[CallbackEntry]:
},
{
"name": "Cloud API Proxy",
"description": "These endpoints proxy the Permit.io cloud api, and therefore **incur high-latency**. "
+ "You should not use the cloud API in the standard request flow of users, i.e in places where the incurred "
+ "added latency will affect your entire api. A good place to call the cloud API will be in one-time user events "
+ "such as user registration (i.e: calling sync user, assigning initial user roles, etc.). "
+ "The sidecar will proxy to the cloud every request prefixed with '/sdk'.",
"description": (
"These endpoints proxy the Permit.io cloud api, and therefore **incur high-latency**. "
"You should not use the cloud API in the standard request flow of users, i.e in places "
"where the incurred added latency will affect your entire api. "
"A good place to call the cloud API will be in one-time user events such as user registration "
"(i.e: calling sync user, assigning initial user roles, etc.). "
"The sidecar will proxy to the cloud every request prefixed with '/sdk'."
),
"externalDocs": {
"description": "The cloud api complete docs are located here:",
"url": "https://api.permit.io/redoc",
Expand Down
Loading

0 comments on commit b0a75c6

Please sign in to comment.