Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,6 @@ dmypy.json
.pyre/

.idea/
*.iml
*.iml

.DS_Store
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ install:
- pip install -r requirements.txt
- pip install coveralls
script:
- coverage run -m unittest discover -s tests -t tests
- coverage run -m django test --settings=tests.settings
after_success:
- coveralls
deploy:
Expand Down
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Django ORM Adapter for PyCasbin
====
# Django ORM Adapter for PyCasbin

[![Build Status](https://www.travis-ci.org/pycasbin/django-orm-adapter.svg?branch=master)](https://www.travis-ci.org/pycasbin/django-orm-adapter)
[![Coverage Status](https://coveralls.io/repos/github/pycasbin/django-orm-adapter/badge.svg)](https://coveralls.io/github/pycasbin/django-orm-adapter)
Expand Down Expand Up @@ -29,13 +28,25 @@ Based on [Officially Supported Databases](https://docs.djangoproject.com/en/3.0/
pip install casbin_django_orm_adapter
```

Add `casbin_adapter` to your `INSTALLED_APPS`

```python
INSTALLED_APPS = [
...
'casbin_adapter',
...
]
```

To run schema migration, execute `python manage.py migrate casbin_adapter

## Simple Example

```python
import casbin_django_orm_adapter
import casbin
from casbin_adapter.adapter import Adapter

adapter = casbin_django_orm_adapter.Adapter('sqlite:///test.db')
adapter = Adapter()

e = casbin.Enforcer('path/to/model.conf', adapter, True)

Expand All @@ -51,7 +62,6 @@ else:
pass
```


### Getting Help

- [PyCasbin](https://github.com/casbin/pycasbin)
Expand Down
Empty file added casbin_adapter/__init__.py
Empty file.
73 changes: 73 additions & 0 deletions casbin_adapter/adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from casbin import persist

from .models import CasbinRule


class Adapter(persist.Adapter):
"""the interface for Casbin adapters."""

def load_policy(self, model):
"""loads all policy rules from the storage."""
lines = CasbinRule.objects.all()

for line in lines:
persist.load_policy_line(str(line), model)

def _create_policy_line(self, ptype, rule):
line = CasbinRule(ptype=ptype)
if len(rule) > 0:
line.v0 = rule[0]
if len(rule) > 1:
line.v1 = rule[1]
if len(rule) > 2:
line.v2 = rule[2]
if len(rule) > 3:
line.v3 = rule[3]
if len(rule) > 4:
line.v4 = rule[4]
if len(rule) > 5:
line.v5 = rule[5]
return line

def save_policy(self, model):
"""saves all policy rules to the storage."""
# See https://casbin.org/docs/en/adapters#autosave
# for why this is deleting all rules
CasbinRule.objects.all().delete()

lines = []
for sec in ["p", "g"]:
if sec not in model.model.keys():
continue
for ptype, ast in model.model[sec].items():
for rule in ast.policy:
lines.append(self._create_policy_line(ptype, rule))
CasbinRule.objects.bulk_create(lines)
return True

def add_policy(self, sec, ptype, rule):
"""adds a policy rule to the storage."""
line = self._create_policy_line(ptype, rule)
line.save()

def remove_policy(self, sec, ptype, rule):
"""removes a policy rule from the storage."""
query_params = {'ptype': ptype}
for i, v in enumerate(rule):
query_params['v{}'.format(i)] = v
rows_deleted, _ = CasbinRule.objects.filter(**query_params).delete()
return True if rows_deleted > 0 else False

def remove_filtered_policy(self, sec, ptype, field_index, *field_values):
"""removes policy rules that match the filter from the storage.
This is part of the Auto-Save feature.
"""
query_params = {'ptype': ptype}
if not(0 <= field_index <= 5):
return False
if not (1 <= field_index + len(field_values) <= 6):
return False
for i, v in enumerate(field_values):
query_params['v{}'.format(i + field_index)] = v
rows_deleted, _ = CasbinRule.objects.filter(**query_params).delete()
return True if rows_deleted > 0 else False
5 changes: 5 additions & 0 deletions casbin_adapter/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class CasbinAdapterConfig(AppConfig):
name = 'casbin_adapter'
30 changes: 30 additions & 0 deletions casbin_adapter/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 2.2.6 on 2020-03-02 15:35

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='CasbinRule',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ptype', models.CharField(max_length=255)),
('v0', models.CharField(max_length=255)),
('v1', models.CharField(max_length=255)),
('v2', models.CharField(max_length=255)),
('v3', models.CharField(max_length=255)),
('v4', models.CharField(max_length=255)),
('v5', models.CharField(max_length=255)),
],
options={
'db_table': 'casbin_rule',
},
),
]
Empty file.
36 changes: 36 additions & 0 deletions casbin_adapter/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copied from: https://github.com/pycasbin/django-orm-adapter
# as it lacks a setup.py file atm
from django.db import models


class CasbinRule(models.Model):
ptype = models.CharField(max_length=255)
v0 = models.CharField(max_length=255)
v1 = models.CharField(max_length=255)
v2 = models.CharField(max_length=255)
v3 = models.CharField(max_length=255)
v4 = models.CharField(max_length=255)
v5 = models.CharField(max_length=255)

class Meta:
db_table = 'casbin_rule'

def __str__(self):
text = self.ptype

if self.v0:
text = text + ', ' + self.v0
if self.v1:
text = text + ', ' + self.v1
if self.v2:
text = text + ', ' + self.v2
if self.v3:
text = text + ', ' + self.v3
if self.v4:
text = text + ', ' + self.v4
if self.v5:
text = text + ', ' + self.v5
return text

def __repr__(self):
return '<CasbinRule {}: "{}">'.format(self.id, str(self))
1 change: 0 additions & 1 deletion casbin_django_orm_adapter/__init__.py

This file was deleted.

104 changes: 0 additions & 104 deletions casbin_django_orm_adapter/adapter.py

This file was deleted.

8 changes: 8 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from setuptools import setup, find_packages

setup(
name='casbin-django-orm-adapter',
version="0.0.1",
packages=find_packages(),
license="Apache-2.0",
)
Empty file added tests/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion tests/rbac_model.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ g = _, _
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
2 changes: 1 addition & 1 deletion tests/rbac_policy.csv
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write

g, alice, data2_admin
g, alice, data2_admin
39 changes: 39 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
SECRET_KEY = 'not-a-production-secret'

INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"casbin_adapter",
"tests",
]

MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]

TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
]
},
}
]

DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}}
Loading