
# OWS Policy Decision Point (PDP)

> A centralized authorization service for secure access control decisions

**ows-pdp** is a Policy Decision Point (PDP) service that provides centralized authorization decisions for applications across Sony PDE. It determines whether users have permission to access specific resources or perform certain actions based on their roles, tenant relationships, and organizational policies.

## 🎯 What Does This Service Do?

- **Centralized Authorization**: Makes access control decisions for our various applications
- **Role-Based Access Control**: Evaluates user permissions based on their roles and tenant relationships  
- **Policy Enforcement**: Uses [Cerbos](https://cerbos.dev/) for flexible, policy-driven authorization
- **High Performance**: Includes Redis caching and batch processing for fast decision-making
- **Multi-tenant Support**: Handles complex organizational hierarchies and [tenant relationships](https://www.notion.so/Tenants-and-Tenant-Hierarchy-deep-dive-a7cdab9462984d7e8a549ea41c07ba34?source=copy_link#11b97177520f8074b77cdf018a972296)

## 📞 Need Help?

- **Slack Channel**: [#permissions-platform-public](https://sonymusic.enterprise.slack.com/archives/C059WPM7D2A)
- **Team**: Permissions Platform team

## 🚀 Quick Start

### Prerequisites

- **Python 3.11.11** or higher
- **Docker** and **Docker Compose**
- **AWS CLI** configured (for development)
- **uv** package manager

### 1. Environment Setup

Install Python 3.11 using pyenv (recommended):

```bash
# Install pyenv (macOS)
brew update && brew install pyenv

# Install and set Python version
pyenv install 3.11.11
pyenv local 3.11.11

# Update your shell configuration
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'export PATH="$PYENV_ROOT/shims:$PATH"' >> ~/.zshrc
source ~/.zshrc

# Install uv package manager
pip install --upgrade pip
pip install uv==0.7.18
```

### 2. Install Dependencies

```bash
cd ows-pdp
make pip_dev
```

## 🏃‍♂️ Running the Service

### Option 1: Docker Environment (Recommended)

This runs the complete development environment with all dependencies:

1. **Set up your environment variables:**
   ```bash
   # Get DD_API_KEY from the shared Dashlane vault
   echo "DD_API_KEY=your_datadog_api_key" >> .env
   ```

2. **Start the full development stack:**
   ```bash
   make up
   ```
   
   This starts:
   - ows-pdp service
   - Cerbos policy engine
   - Local DynamoDB
   - Redis cache
   - Integration test container

3. **Debug with Python debugger (optional):**
   ```bash
   # Attach to the container for debugging
   docker attach ows-pdp-ows-pdp-1
   
   # Detach without stopping: Ctrl+p then Ctrl+q
   # (Keep in mind that Ctrl+c as it will stop the container)
   ```

### Option 2: Local Development

Run the service directly on your machine:

```bash
make pip_dev
make dev
```

**Note**: This connects to the `dev_pp_identity` table in the AWS dev account (103233932089).

### 🔍 Verify Everything Works

Check that the service is running:
```bash
curl http://localhost:8000/hello/
```

You should see a health check response.

```bash
{
    "status": "ok"
}
```


## 🧪 Testing & Quality

### Running Tests

| Command | Purpose |
|---------|---------|
| `make test` | Run unit tests |
| `make test_unit` | Run unit tests with coverage reporting |
| `make ci_unit_lint` | Run unit tests and lint testing (Jenkins-style) |
| `make ci_test_integration` | Run integration tests (Jenkins-style) |
| `make local_test_integration` | Run integration tests locally |
| `make test_cerbos` | Run Cerbos policy tests |

### Code Quality

| Command | Purpose |
|---------|---------|
| `make lint` | Check code style and types |
| `make fmt` | Auto-format code |

### View Coverage Report

After running `make test_unit`, view the HTML coverage report:
```bash
make unit_cov_report
# Opens coverage report at http://localhost:8000
```

## 🔧 Development Tools

### Dynamodb

`ows-pdp` uses dynamodb as a backing datastore. We've found success using [`dynamodb-admin`](https://github.com/aaronshaf/dynamodb-admin) as a GUI client for viewing our table and its items. After installing, for interacting with our dockerized DynamoDB service (standardized at `http://localhost:50028`):

```sh
dynamodb-admin --dynamo-endpoint=http://localhost:50028
```

Then open up Chrome and navigate to `http://localhost:8001`.


### PDP CLI

The `pdpcli` tool provides commands for development and operational tasks:

```bash
# View all available commands
./pdpcli --help

# View commands for specific modules
./pdpcli dynamodb --help
./pdpcli derived_roles --help
./pdpcli redis --help
```

### Key CLI Commands

#### Load Data from CSV
Import roles from a CSV file (useful for seeding local databases):
```bash
DYNAMODB_ENDPOINT_URL=http://pdp-dynamodb:8000 ./pdpcli dynamodb load_csv somefile/01_pp.csv
```

#### Generate Tenant Policies
Create derived roles policy files for tenant hierarchies:
```bash
./pdpcli derived_roles by_tenant \
  --filepath cerbos/policies/derived_roles/fansifter.yml \
  --name fansifter_multitenancy_roles \
  --role fansifter_can_connect_ad_accounts \
  --role fansifter_can_share_ad_campaign_audiences \
  --role fansifter_can_view_fan_data
```

#### Cache Management
Clear Redis cache entries by type. See here for [available types](https://github.com/theorchard/ows-pdp/blob/468f5a6034c07784248bdfa6026e2defaa1e0a8f/pdp/constants/constants.py#L50-L56).


```bash
./pdpcli redis cache_bludgeon tenant_hierarchy
```


The above command, for example, would flush the following cached items:

```python
"tenant_hierarchy@tenant_id#123|tenant_type#account": IdExchangeTenantHierarchy
"tenant_hierarchy@tenant_type#account|tenant_uuid#61577c79-d788-4b88-bf6a-2def82e0b4ed": IdExchangeTenantHierarchy
```


## 🎛️ Feature Flags

ows-pdp uses [Split.io](https://split.io) for feature flag management.

### Using Feature Flags in Code

```python
from pdp.connectors.features import SplitioClient, BooleanFeature, Feature, splitio_client_factory

splitio_client: SplitioClient = splitio_client_factory()

def fastapi_handler_with_boolean_feature():
   """Awesome API handler.
   
   Feature Flags: 
     - my_feature_flag
   """
   my_feature = BooleanFeature(client=splitio_client, feature_name="my_feature_flag")
   if my_feature.is_on_for_identity("test_identity_uuid"):
      # perform flag dependent behavior
      pass

def fastapi_handler_with_multivalue_feature():
   """Awesome API handler.
   
   Feature Flags: 
     - another_feature_flag
   """
   # Use the `Feature` feature for other flags types.
   my_feature = Feature(client=splitio_client, feature_name="another_feature_flag")
   if my_feature.get_value_for_identity("test_identity_uuid") == "special_ff_value":
      # perform flag dependent behavior
      pass
```

We may add more convenience classes types in the future (e.g. `EnumFeature`, `NumberFeature`).

### Local Development Setup

1. **Create local feature flag file:**
   ```bash
   cp .split.shadow .split
   ```

2. **Configure feature values in `.split`:**
   ```
   pp_say_bonjour on
   ```

3. **Update `.env` file:**
   ```
   SPLITIO_API_KEY=localhost
   SPLIT_FILE_PATH=.split
   ```

### Best Practices

1. **Document feature flags** in function docstrings
2. **Add new flags** to `.split.shadow`
3. **Communicate changes** in [#permissions-platform-public](https://sonymusic.enterprise.slack.com/archives/C059WPM7D2A):
   ```
   PP-998 Setting FF pp_say_bonjour on for all identities. (cc @oncall-engineer)
   PP-999 Changing FF feature_0 from 5% to 25% enabled. (cc @oncall-engineer)
   ```
4. **Remove unused flags** after rollout completion

## 🗄️ Redis Cache

### Running Redis

Start the Redis server:
```bash
make start_redis
```

Connect to Redis CLI for debugging:
```bash
make redis_cli
```

See the [Redis commands documentation](https://redis.io/commands/) for available operations.

## 📚 Additional Resources

### Related Documentation
- [python-orchard-features](https://github.com/theorchard/python-orchard-features)
- [splitio/python-client](https://github.com/splitio/python-client/blob/master/doc/source/introduction.rst)
- [splitio/python sdk docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
- [AWS Access Setup](https://www.notion.so/AWS-Access-f841b9dd815d4443a80e96a86c92cd2f?pvs=4)

### Future Improvements
1. **Async support** - There's a `splitio-python client` feature branch
   called [Feature/Async](https://github.com/splitio/python-client/tree/Feature/Async/splitio/client). Maybe it's a work
   in progress?
2. **Redis caching optimization** - If we see latency issues, the splitio python
   client [supports redis caching](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK#redis-cache-and-client-setup).


## 🤝 Contributing

1. Follow the testing and linting guidelines above
2. Document any new feature flags in code comments
3. Communicate changes in the PP Slack channel [#permissions-platform-public](https://sonymusic.enterprise.slack.com/archives/C059WPM7D2A)
4. Ensure AWS access is configured for development

