# Oso Policy Builder

> **⚠️ Experimental Release**: This is an early experimental library for building Oso's Polar policies in Python. It's far from feature-complete and has many limitations. Not intended for production use. Use at your own discretion.
>
> **💬 We want your feedback!** Have a comment or feature request? Email the Oso engineer who worked on this library directly: **connor@osohq.com**

[![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)

A python library for building [Oso Cloud](https://osohq.com) authorization policies. Write your authorization logic in clean Python syntax and automatically generate Polar code.

## Installation

```bash
pip install oso-policy-builder
```

## Quick Start

```python
from oso_policy_builder import PolicyBuilder, User, Resource

# Define resources with permissions and roles by creating a subclass of `Resource`
# You must declare all permissions, roles, and relations used in your policy as resource fields.
class Document(Resource):
    permissions = ["read", "write", "delete"]
    roles = ["viewer", "editor"]
    relations={"project": Project}


# Define authorization rules using Python
User.with_role("viewer").can("read").on(Document)  # Anyone can read
User.with_role("editor").can("read", "write", "delete").on(Document)  # Editors can read, write or delete

# Generate Polar policy
policy = PolicyBuilder()
print(policy.generate())

```

This generates valid Polar code:

```polar
# Generated by Oso Policy Builder

actor User {}

resource Document {
  permissions = ["read", "write", "delete"];
  roles = ["viewer", "editor"];

  "read" if "viewer";
  "read" if "editor"
  "write" if "editor";
  "delete" if "editor";
}
```

## Complex Conditions

You can build complex authorization logic with conditions:

```python
from oso_policy_builder import and_, or_, not_

# Complex conditional permissions
User.can("publish").on(Document).when(
    and_(
        User.has_role("editor"),
        not_(Document.has_attribute("is_archived")),
        or_(
            Document.has_attribute("is_reviewed"),
            User.has_permission("admin_override")
        )
    )
)
```

## Role Inheritance

Set up role hierarchies and cross-resource permissions:

```python
class Project(Resource):
    permissions = ["read", "manage"]
    roles = ["member", "admin"]

class Document(Resource):
    permissions = ["read", "write"]
    roles = ["viewer", "editor"]
    relations = {"project": Project}

# Role inheritance
User.with_role("editor").inherits_role("viewer").on(Document)

# Cross-resource permissions
User.with_role("admin").on(Project).can("write").on(Document)
```

## Global Permissions

Define application-wide permissions that aren't tied to specific resources:

```python
from oso_policy_builder import BaseGlobal, attribute

# Define global permissions and roles
class Global(BaseGlobal):
    roles = ["admin", "moderator"]
    permissions = [
        "view_analytics",
        "manage_users",
        "system_settings",
        "audit_logs"
    ]

# Create custom global attributes with the @attribute decorator
@attribute(User)
def is_privileged(user: User):
    """
    returns: is_privileged(user: User) if
               role matches String and
               role in ["admin", "moderator"] and
               has_role(user, role);
    """
    return user.has_role_in(["admin", "moderator"])

# Grant global permissions based on custom attributes
Global.can("view_analytics").when(is_privileged(User))

# Or grant multiple permissions at once
for permission in Global.permissions:
    Global.can(permission).when(is_privileged(User))

```

## Save Your Policy

```python
# Save to file for use with Oso Cloud
policy = PolicyBuilder()
policy.save("authorization.polar")  # Saves to current directory
policy.save("my-policy.polar", directory="./policies")  # Custom location
```

## Fluent API Reference

The library provides a fluent, chainable API for building authorization rules:

### User/Actor Methods

```python
# Permission rules
User.can("read")                    # Start a permission rule
User.can("read").on(Resource)       # Grant permission on a resource
User.can("read").on(Resource).when(condition)  # Add conditions

# Role-based rules
User.with_role("editor")            # Start a role-based rule
User.with_role("editor").can("write").on(Resource)  # Grant permissions to a role
User.with_role("editor").inherits_role("viewer").on(Resource)  # Role inheritance

# Condition helpers
User.has_role("admin")              # Check if user has a role
User.has_permission("manage")       # Check if user has a permission
User.has_relation("owner")          # Check if user has a relation
User.has_role_in(["admin", "mod"])  # Check if user has any role from list
```

### Resource Methods

```python
# Attribute conditions
Document.has_attribute("is_public")     # Check resource attributes
```

### Logical Operators

```python
from oso_policy_builder import and_, or_, not_

# Combine conditions
and_(condition1, condition2, condition3)   # All must be true
or_(condition1, condition2)               # Any must be true
not_(condition)                           # Negation
```

### Method Chaining Examples

```python
# Chain role permissions
User.with_role("admin").can("read", "write", "delete").on(Document)

# Chain role inheritance
User.with_role("editor").inherits_role("viewer").on(Document)

# Chain multiple permission grants
(User.with_role("editor").on(Project)
    .can("write").on(Document)
    .can("delete").on(Comment))

# Chain conditions
User.can("publish").on(Document).when(
    or_(
        and_(
            User.has_role("editor"),
            Document.has_attribute("is_reviewed")
        )
        User.has_permission("admin_override"))
)
```

## Integration with Oso Cloud

Use your generated policies with [Oso Cloud](https://osohq.com/docs):

1. Generate your policy with this library
2. Upload the `.polar` file to Oso Cloud
3. Use Oso's SDKs to make authorization decisions in your application

For complete Oso Cloud documentation, visit [osohq.com/docs](https://osohq.com/docs).

## Current Limitations

This is an experimental library with several limitations:

- **Limited Polar feature coverage** - Not all Polar language features are supported
- **Basic validation** - Limited error checking and validation
- **Experimental API** - This library's API may change in future versions
- **No backward compatibility guarantees** - This is an experimental proof-of-concept

## Requirements

- Python 3.13+

## Feedback & Feature Requests

This is an experimental project and we'd love your feedback! If you:

- Find bugs or limitations
- Have ideas for new features
- Want to discuss authorization patterns
- Need help with complex use cases

Please reach out directly to the engineer who worked on this library: **connor@osohq.com**

---

**Built by the team at [Oso](https://osohq.com) - Authorization as a Service**
