librbac


Namelibrbac JSON
Version 3.0.1 PyPI version JSON
download
home_pagehttps://stash.bars-open.ru/projects/EDUEO/repos/librbac/
SummaryОбщая библиотека контроля доступа для микросервисов
upload_time2024-08-26 16:41:15
maintainerNone
docs_urlNone
authorBARS Group
requires_pythonNone
licenseNone
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Общая библиотека контроля доступа для микросервисов

## Подключение
#### settings.py:

```python
INSTALLED_APPS = [
    # другие приложение
    'testapp.core',
    'librbac',                         # для management команды rbac
    'librbac.contrib.migrations',      # для поддержки миграций
]

# Опционально. 
# Подойдёт любая имплементация, где `request.user` поддерживает `librbac.domain.permissions.UserProtocol` 
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oidc_auth.authentication.JSONWebTokenAuthentication',
    ),
}

OIDC_AUTH = {
    # URL сервиса аутентификации, необходимо указать своё значение
    'OIDC_ENDPOINT': 'http://testserver/oauth',
    
    # Функция преобразующая токен в объект пользователя. Указать как есть.
    'OIDC_RESOLVE_USER_FUNCTION': 'librbac.contrib.oidc.auth.get_user_from_token',
    
    # Заголовок в котором хранится токен. Указать как есть.
    'JWT_AUTH_HEADER_PREFIX': 'Bearer',

    # The Claims Options can now be defined by a static string.
    # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation
    # The old OIDC_AUDIENCES option is removed in favor of this new option.
    # `aud` is only required, when you set it as an essential claim.
    'OIDC_CLAIMS_OPTIONS': {
        'iss': {
            'essential': True,
        }
    },
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```    


#### testapp/__init__.py:
```python
from typing import TYPE_CHECKING

from explicit.contrib.messagebus.event_registry import Registry


if TYPE_CHECKING:
    from explicit.messagebus.messagebus import MessageBus  # noqa


default_app_config = f'{__package__}.apps.AppConfig'


bus: 'MessageBus'

event_registry = Registry()
```



#### testapp/apps.py:

```python
from django.apps.config import AppConfig as AppConfigBase


class AppConfig(AppConfigBase):
    name = __package__

    _rbac_permissions_topic = 'test.rbac.permission'

    def _bootstrap(self):
        """Предоставление общей шины ядра."""
        ...

    def _setup_rbac(self):
        from librbac.infrastructure.rest_framework.collector import (
            AppsPermissionsCollector)
        from librbac.infrastructure.rest_framework.backend import (
            RestFrameworkBackend)
        from librbac.infrastructure.explicit.publisher import (
            ServiceBusPublisher)
        from librbac.manager import RBACManager
        from testapp.core.adapters.messaging import adapter

        RBACManager.bootstrap(
            collector_cls=AppsPermissionsCollector,
            backend_cls=RestFrameworkBackend,
            publisher=ServiceBusPublisher(adapter=adapter, topic=self._rbac_permissions_topic),
            # или если нужно отправлять события в локальную шину
            # publisher=LocalBusPermissionsPublisher(bus=bus),
        )

    # Опционально, если нужны миграции
    def _setup_rbac_migrations(self):
        from librbac.contrib.migrations import config
        from testapp import core
        from testapp.core.adapters import messaging

        class Config(config.IConfig):
            bus = core.bus
            adapter = messaging.adapter
            rbac_topic_permission = self._rbac_permissions_topic

        config.migrations_config = Config()

    def ready(self):
        self._bootstrap()
        self._setup_rbac()
        self._setup_rbac_migrations()

```

#### testapp/rest/persons/permissions/rules.py
```python
from testapp.core.persons.models import Person


def is_own_person(
    context: 'GenericViewSet',
    request: 'Request',
    user: 'UserProtocol',
):
    """Проверка отношения пользователя к ФЛ."""
    person_id = context.get_rbac_rule_data()

    user_id = user.pk
    return Person.objects.filter(id=person_id, user_id=user_id).exists()

```


#### testapp/rest/persons/permissions/__init__.py

```python

from librbac.domain.permissions import PermissionGroup, Permission

from . import rules

PERM_NAMESPACE_TEST = 'test'

PERM_RESOURCE__PERSON = 'person'
PERM__PERSON__READ = Permission(
    # (namespace, resource, action, scope)
    PERM_NAMESPACE_TEST, PERM_RESOURCE__PERSON, 'read'
)
PERM__PERSON__WRITE_OWN = Permission(
    PERM_NAMESPACE_TEST, PERM_RESOURCE__PERSON, 'write', 'own'
)
# Описание разрешений
# -----------------------------------------------------------------------------
permissions = (
    (PERM__PERSON__READ, 'Просмотр ФЛ'),
    (PERM__PERSON__WRITE_OWN, 'Редактирование своего ФЛ'),
)

dependencies = {
    PERM__PERSON__WRITE_OWN: {
        PERM__PERSON__READ,
    },
}
# Описание связей разделов и групп разрешений
# -----------------------------------------------------------------------------
partitions = {
    'Администрирование': (
        PermissionGroup(PERM_NAMESPACE_TEST, PERM_RESOURCE__PERSON),
    ),
}
# Описание правил доступа
# -----------------------------------------------------------------------------
rules = {
    PERM__PERSON__WRITE_OWN: (rules.is_own_person,),
}

# Сопоставление представлений с разрешениями (взаимоисключающее с RBACMixin)
# -----------------------------------------------------------------------------
viewset_permission_mapping = {
    'testapp.rest.persons.views.PersonViewSet': dict(
        create=(PERM__PERSON__WRITE_OWN,),
        partial_update=(PERM__PERSON__WRITE_OWN,),
        destroy=(PERM__PERSON__WRITE_OWN,),
        retrieve=(PERM__PERSON__READ,),
        list=(PERM__PERSON__READ,),
    )
}

```


#### testapp/views.py
```python
from rest_framework.viewsets import ModelViewSet

from librbac.infrastructure.django.rest_framework.viewsets import RBACMixin

from .permissions import PERM__PERSON__READ
from .permissions import PERM__PERSON__WRITE


class PersonViewSet(RBACMixin, ModelViewSet):
    
    # сопоставление действий с разрешениями (взаимоисключающее с permissions.viewset_permission_mapping)
    perm_map = dict(
        create=(PERM__PERSON__WRITE,),
        partial_update=(PERM__PERSON__WRITE,),
        destroy=(PERM__PERSON__WRITE,),
        retrieve=(PERM__PERSON__READ,),
        list=(PERM__PERSON__READ,),
    )

    ...
```

## Миграции разрешений
[Описание](./src/librbac/contrib/migrations/README.md) в модуле миграций

## Запуск тестов
    $ tox

            

Raw data

            {
    "_id": null,
    "home_page": "https://stash.bars-open.ru/projects/EDUEO/repos/librbac/",
    "name": "librbac",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": null,
    "author": "BARS Group",
    "author_email": "education_dev@bars-open.ru",
    "download_url": "https://files.pythonhosted.org/packages/32/b7/4707154f57a2d787081c06c318fd8671b28ec1e48ce532ca4a8a9e016f39/librbac-3.0.1.tar.gz",
    "platform": null,
    "description": "# \u041e\u0431\u0449\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0434\u043b\u044f \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432\n\n## \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\n#### settings.py:\n\n```python\nINSTALLED_APPS = [\n    # \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\n    'testapp.core',\n    'librbac',                         # \u0434\u043b\u044f management \u043a\u043e\u043c\u0430\u043d\u0434\u044b rbac\n    'librbac.contrib.migrations',      # \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439\n]\n\n# \u041e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e. \n# \u041f\u043e\u0434\u043e\u0439\u0434\u0451\u0442 \u043b\u044e\u0431\u0430\u044f \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f, \u0433\u0434\u0435 `request.user` \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 `librbac.domain.permissions.UserProtocol` \n# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nREST_FRAMEWORK = {\n    'DEFAULT_AUTHENTICATION_CLASSES': (\n        'oidc_auth.authentication.JSONWebTokenAuthentication',\n    ),\n}\n\nOIDC_AUTH = {\n    # URL \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u0432\u043e\u0451 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n    'OIDC_ENDPOINT': 'http://testserver/oauth',\n    \n    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0449\u0430\u044f \u0442\u043e\u043a\u0435\u043d \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0423\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u0435\u0441\u0442\u044c.\n    'OIDC_RESOLVE_USER_FUNCTION': 'librbac.contrib.oidc.auth.get_user_from_token',\n    \n    # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0442\u043e\u043a\u0435\u043d. \u0423\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u0435\u0441\u0442\u044c.\n    'JWT_AUTH_HEADER_PREFIX': 'Bearer',\n\n    # The Claims Options can now be defined by a static string.\n    # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation\n    # The old OIDC_AUDIENCES option is removed in favor of this new option.\n    # `aud` is only required, when you set it as an essential claim.\n    'OIDC_CLAIMS_OPTIONS': {\n        'iss': {\n            'essential': True,\n        }\n    },\n}\n# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n```    \n\n\n#### testapp/__init__.py:\n```python\nfrom typing import TYPE_CHECKING\n\nfrom explicit.contrib.messagebus.event_registry import Registry\n\n\nif TYPE_CHECKING:\n    from explicit.messagebus.messagebus import MessageBus  # noqa\n\n\ndefault_app_config = f'{__package__}.apps.AppConfig'\n\n\nbus: 'MessageBus'\n\nevent_registry = Registry()\n```\n\n\n\n#### testapp/apps.py:\n\n```python\nfrom django.apps.config import AppConfig as AppConfigBase\n\n\nclass AppConfig(AppConfigBase):\n    name = __package__\n\n    _rbac_permissions_topic = 'test.rbac.permission'\n\n    def _bootstrap(self):\n        \"\"\"\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0431\u0449\u0435\u0439 \u0448\u0438\u043d\u044b \u044f\u0434\u0440\u0430.\"\"\"\n        ...\n\n    def _setup_rbac(self):\n        from librbac.infrastructure.rest_framework.collector import (\n            AppsPermissionsCollector)\n        from librbac.infrastructure.rest_framework.backend import (\n            RestFrameworkBackend)\n        from librbac.infrastructure.explicit.publisher import (\n            ServiceBusPublisher)\n        from librbac.manager import RBACManager\n        from testapp.core.adapters.messaging import adapter\n\n        RBACManager.bootstrap(\n            collector_cls=AppsPermissionsCollector,\n            backend_cls=RestFrameworkBackend,\n            publisher=ServiceBusPublisher(adapter=adapter, topic=self._rbac_permissions_topic),\n            # \u0438\u043b\u0438 \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0448\u0438\u043d\u0443\n            # publisher=LocalBusPermissionsPublisher(bus=bus),\n        )\n\n    # \u041e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u044b \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438\n    def _setup_rbac_migrations(self):\n        from librbac.contrib.migrations import config\n        from testapp import core\n        from testapp.core.adapters import messaging\n\n        class Config(config.IConfig):\n            bus = core.bus\n            adapter = messaging.adapter\n            rbac_topic_permission = self._rbac_permissions_topic\n\n        config.migrations_config = Config()\n\n    def ready(self):\n        self._bootstrap()\n        self._setup_rbac()\n        self._setup_rbac_migrations()\n\n```\n\n#### testapp/rest/persons/permissions/rules.py\n```python\nfrom testapp.core.persons.models import Person\n\n\ndef is_own_person(\n    context: 'GenericViewSet',\n    request: 'Request',\n    user: 'UserProtocol',\n):\n    \"\"\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a \u0424\u041b.\"\"\"\n    person_id = context.get_rbac_rule_data()\n\n    user_id = user.pk\n    return Person.objects.filter(id=person_id, user_id=user_id).exists()\n\n```\n\n\n#### testapp/rest/persons/permissions/__init__.py\n\n```python\n\nfrom librbac.domain.permissions import PermissionGroup, Permission\n\nfrom . import rules\n\nPERM_NAMESPACE_TEST = 'test'\n\nPERM_RESOURCE__PERSON = 'person'\nPERM__PERSON__READ = Permission(\n    # (namespace, resource, action, scope)\n    PERM_NAMESPACE_TEST, PERM_RESOURCE__PERSON, 'read'\n)\nPERM__PERSON__WRITE_OWN = Permission(\n    PERM_NAMESPACE_TEST, PERM_RESOURCE__PERSON, 'write', 'own'\n)\n# \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0439\n# -----------------------------------------------------------------------------\npermissions = (\n    (PERM__PERSON__READ, '\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0424\u041b'),\n    (PERM__PERSON__WRITE_OWN, '\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0432\u043e\u0435\u0433\u043e \u0424\u041b'),\n)\n\ndependencies = {\n    PERM__PERSON__WRITE_OWN: {\n        PERM__PERSON__READ,\n    },\n}\n# \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0435\u0439 \u0440\u0430\u0437\u0434\u0435\u043b\u043e\u0432 \u0438 \u0433\u0440\u0443\u043f\u043f \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0439\n# -----------------------------------------------------------------------------\npartitions = {\n    '\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435': (\n        PermissionGroup(PERM_NAMESPACE_TEST, PERM_RESOURCE__PERSON),\n    ),\n}\n# \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043e\u0441\u0442\u0443\u043f\u0430\n# -----------------------------------------------------------------------------\nrules = {\n    PERM__PERSON__WRITE_OWN: (rules.is_own_person,),\n}\n\n# \u0421\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u0441 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438 (\u0432\u0437\u0430\u0438\u043c\u043e\u0438\u0441\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0435\u0435 \u0441 RBACMixin)\n# -----------------------------------------------------------------------------\nviewset_permission_mapping = {\n    'testapp.rest.persons.views.PersonViewSet': dict(\n        create=(PERM__PERSON__WRITE_OWN,),\n        partial_update=(PERM__PERSON__WRITE_OWN,),\n        destroy=(PERM__PERSON__WRITE_OWN,),\n        retrieve=(PERM__PERSON__READ,),\n        list=(PERM__PERSON__READ,),\n    )\n}\n\n```\n\n\n#### testapp/views.py\n```python\nfrom rest_framework.viewsets import ModelViewSet\n\nfrom librbac.infrastructure.django.rest_framework.viewsets import RBACMixin\n\nfrom .permissions import PERM__PERSON__READ\nfrom .permissions import PERM__PERSON__WRITE\n\n\nclass PersonViewSet(RBACMixin, ModelViewSet):\n    \n    # \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0441 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438 (\u0432\u0437\u0430\u0438\u043c\u043e\u0438\u0441\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0435\u0435 \u0441 permissions.viewset_permission_mapping)\n    perm_map = dict(\n        create=(PERM__PERSON__WRITE,),\n        partial_update=(PERM__PERSON__WRITE,),\n        destroy=(PERM__PERSON__WRITE,),\n        retrieve=(PERM__PERSON__READ,),\n        list=(PERM__PERSON__READ,),\n    )\n\n    ...\n```\n\n## \u041c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0439\n[\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435](./src/librbac/contrib/migrations/README.md) \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439\n\n## \u0417\u0430\u043f\u0443\u0441\u043a \u0442\u0435\u0441\u0442\u043e\u0432\n    $ tox\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "\u041e\u0431\u0449\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0434\u043b\u044f \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432",
    "version": "3.0.1",
    "project_urls": {
        "Homepage": "https://stash.bars-open.ru/projects/EDUEO/repos/librbac/"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7c50dc22b248fbf9398b46c71f6aa0e9b9b03e2e41c0dd18a1a7c137d9aa889b",
                "md5": "8d695277e8eacade41d79ec85cdbf1b7",
                "sha256": "f03776be6d90f796edd0e59394b5bc47ac211decc058ef1ee259fffa622a9468"
            },
            "downloads": -1,
            "filename": "librbac-3.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8d695277e8eacade41d79ec85cdbf1b7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 37428,
            "upload_time": "2024-08-26T16:41:13",
            "upload_time_iso_8601": "2024-08-26T16:41:13.031827Z",
            "url": "https://files.pythonhosted.org/packages/7c/50/dc22b248fbf9398b46c71f6aa0e9b9b03e2e41c0dd18a1a7c137d9aa889b/librbac-3.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "32b74707154f57a2d787081c06c318fd8671b28ec1e48ce532ca4a8a9e016f39",
                "md5": "650f6bfe3357119088e76e4b8a432115",
                "sha256": "b3ce17120bdab6e105950506c6eb4e036421dcb3696b04c115d35887ac81f0ea"
            },
            "downloads": -1,
            "filename": "librbac-3.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "650f6bfe3357119088e76e4b8a432115",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 26443,
            "upload_time": "2024-08-26T16:41:15",
            "upload_time_iso_8601": "2024-08-26T16:41:15.236517Z",
            "url": "https://files.pythonhosted.org/packages/32/b7/4707154f57a2d787081c06c318fd8671b28ec1e48ce532ca4a8a9e016f39/librbac-3.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-26 16:41:15",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "librbac"
}
        
Elapsed time: 0.38945s