oxutils 0.4.2__tar.gz → 0.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oxutils-0.4.2 → oxutils-0.4.4}/PKG-INFO +1 -1
- {oxutils-0.4.2 → oxutils-0.4.4}/pyproject.toml +1 -1
- oxutils-0.4.4/src/oxutils/__init__.py +53 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/celery/base.py +6 -1
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/logger/settings.py +3 -1
- oxutils-0.4.4/src/oxutils/permissions/__init__.py +1 -0
- oxutils-0.4.4/src/oxutils/permissions/apps.py +23 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/checks.py +36 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/perms.py +58 -0
- oxutils-0.4.4/src/oxutils/permissions/presets.py +131 -0
- oxutils-0.4.2/src/oxutils/__init__.py +0 -28
- oxutils-0.4.2/src/oxutils/permissions/apps.py +0 -10
- oxutils-0.4.2/src/oxutils/users/migrations/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/README.md +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/apps.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/apps.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/export.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/locale/fr/LC_MESSAGES/django.po +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/masks.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/migrations/0001_initial.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/migrations/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/settings.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/audit/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/adapter.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/admin.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/app_settings.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/apps.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/constants.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/emails/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/emails/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/emails/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/emails/services.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/exceptions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/invitations/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/invitations/backend.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/invitations/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/invitations/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/invitations/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/invitations/tokens.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/adapter.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/base/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/base/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/base/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/recovery_codes/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/recovery_codes/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/recovery_codes/flows.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/recovery_codes/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/recovery_codes/services.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/totp/__ini__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/totp/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/totp/flows.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/totp/permissions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/totp/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/totp/services.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mfa/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/migrations/0001_initial.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/migrations/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/mixins.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/password_reset/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/password_reset/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/password_reset/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/password_reset/permissions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/password_reset/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/registration/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/registration/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/registration/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/sessions/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/sessions/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/sessions/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/signals.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/templatetags/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/templatetags/spa_urls.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/tokens/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/tokens/app_settings.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/tokens/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/tokens/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/tokens/tokens.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/tokens/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/auth/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/celery/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/celery/settings.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/conf.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/constants.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/context/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/context/site_name_processor.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/admin.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/apps.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/caches.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/enums.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/management/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/management/commands/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/management/commands/sync_currency.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/migrations/0001_initial.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/migrations/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/signals.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/tests.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/currency/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/enums/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/enums/audit.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/enums/invoices.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/exceptions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/functions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/jwt/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/jwt/auth.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/jwt/middleware.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/jwt/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/jwt/tokens.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/jwt/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/logger/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/logger/receivers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/mixins/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/mixins/base.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/mixins/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/mixins/services.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/models/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/models/base.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/models/billing.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/models/change_tracker.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/models/fields.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/models/invoice.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/admin.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/apps.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/authorization.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/cacheops.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/caches.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/checks.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/constants.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/context.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/enums.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/exceptions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/locale/fr/LC_MESSAGES/django.po +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/management/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/management/commands/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/management/commands/grant_tenant_owners.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/management/commands/init_oxiliere_system.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/middleware.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/migrations/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/permissions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/services.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/settings.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/signals.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/tests.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/oxiliere/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/pagination/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/pagination/cursor.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/pdf/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/pdf/printer.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/pdf/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/pdf/views.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/actions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/admin.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/caches.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/constants.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/controllers.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/exceptions.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/locale/fr/LC_MESSAGES/django.po +0 -0
- {oxutils-0.4.2/src/oxutils/permissions → oxutils-0.4.4/src/oxutils/permissions/management}/__init__.py +0 -0
- {oxutils-0.4.2/src/oxutils/permissions/management → oxutils-0.4.4/src/oxutils/permissions/management/commands}/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/management/commands/load_permission_preset.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0001_initial.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0002_alter_grant_role.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0003_alter_grant_options_alter_group_options_and_more.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0004_group_app_group_permissions_app_9a9924_idx.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0005_role_app.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0006_alter_rolegrant_options_and_more.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0007_grant_locked_alter_grant_role_and_more.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0008_remove_grant_unique_user_scope_role_and_more.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/migrations/0009_grant_is_active.py +0 -0
- {oxutils-0.4.2/src/oxutils/permissions/management/commands → oxutils-0.4.4/src/oxutils/permissions/migrations}/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/queryset.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/schemas.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/services.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/tests.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/permissions/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/py.typed +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/s3.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/settings.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/templates/account/email/base_message.txt +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/templates/email/password/reset_email_message.html +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/templates/email/password/reset_email_message.txt +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/templates/email/password/reset_email_subject.txt +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/templates/email/signup/email_message.html +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/templates/email/signup/email_subject.txt +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/templates/verified.html +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/types.py +0 -0
- {oxutils-0.4.2/src/oxutils/permissions/migrations → oxutils-0.4.4/src/oxutils/users}/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/admin.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/apps.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/locale/fr/LC_MESSAGES/django.po +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/migrations/0001_initial.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/migrations/0002_alter_user_first_name_alter_user_last_name.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/migrations/0003_user_photo.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/migrations/0004_alter_user_options.py +0 -0
- {oxutils-0.4.2/src/oxutils/users → oxutils-0.4.4/src/oxutils/users/migrations}/__init__.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/models.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/tests.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/users/utils.py +0 -0
- {oxutils-0.4.2 → oxutils-0.4.4}/src/oxutils/utils.py +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""OxUtils — Production-ready Django utilities for multi-tenant SaaS.
|
|
2
|
+
|
|
3
|
+
Authentication & authorization:
|
|
4
|
+
- JWT authentication: Bearer, Cookie, and passive backends with JWKS support
|
|
5
|
+
- RBAC permissions: roles, groups, user grants with scope-based access
|
|
6
|
+
control (``ScopePermission``, ``ScopeAnyPermission``, etc.)
|
|
7
|
+
- Auto-discovery of permission presets from installed apps
|
|
8
|
+
- Complete auth system: registration, login/logout, password reset,
|
|
9
|
+
MFA, invitations, sessions — integrates with django-allauth
|
|
10
|
+
- Api exceptions with error codes and structured responses
|
|
11
|
+
|
|
12
|
+
Multi-tenancy:
|
|
13
|
+
- django-tenants integration with ``X-Organization-ID`` header routing
|
|
14
|
+
- Tenant / tenant-user models, schema-aware middleware and caching
|
|
15
|
+
- Tenant lifecycle signals, authorization, and system-tenant fallback
|
|
16
|
+
|
|
17
|
+
Infrastructure & tooling:
|
|
18
|
+
- Structured logging with correlation IDs, request metadata binding,
|
|
19
|
+
and pre-built ``LOGGING`` configuration (structlog)
|
|
20
|
+
- Audit log export to S3 with export-state tracking and status history
|
|
21
|
+
- Cursor-based pagination for Django Ninja APIs
|
|
22
|
+
- PDF generation via WeasyPrint (``Printer`` class and ``WeasyTemplateView``)
|
|
23
|
+
- Multi-currency support with rate syncing (BCC, Open Exchange Rates)
|
|
24
|
+
- Celery app with task auto-discovery and structlog integration
|
|
25
|
+
- S3 storage backend factory with env-var configuration
|
|
26
|
+
|
|
27
|
+
Model utilities:
|
|
28
|
+
- Reusable mixins: UUID PK, timestamps, user tracking, slug, ordering,
|
|
29
|
+
soft-delete with field masking (``SafeDeleteModelMixin``),
|
|
30
|
+
change tracking, invoicing, and billing
|
|
31
|
+
- Custom user model with email-based auth, ``oxi_id`` linking, and audit
|
|
32
|
+
- Custom Pydantic types (``EmailApiType``, etc.)
|
|
33
|
+
|
|
34
|
+
Other:
|
|
35
|
+
- HTTP header/cookie constants (oxi tokens, JWT tokens)
|
|
36
|
+
- Template context processor (site name & domain)
|
|
37
|
+
- Central pydantic-settings configuration (``oxi_settings``)
|
|
38
|
+
- Shared enums (invoice status, export status)
|
|
39
|
+
- Utility helpers: client IP detection, absolute URL building,
|
|
40
|
+
image validation, bound-request detection
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
__version__ = "0.4.4"
|
|
44
|
+
|
|
45
|
+
from oxutils.conf import AUDIT_MIDDLEWARE, UTILS_APPS
|
|
46
|
+
from oxutils.settings import oxi_settings
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"oxi_settings",
|
|
50
|
+
"UTILS_APPS",
|
|
51
|
+
"AUDIT_MIDDLEWARE",
|
|
52
|
+
"__version__",
|
|
53
|
+
]
|
|
@@ -30,6 +30,9 @@ def receiver_setup_logging(loglevel, logfile, format, colorize, **kwargs): # pr
|
|
|
30
30
|
"json_formatter": {
|
|
31
31
|
"()": structlog.stdlib.ProcessorFormatter,
|
|
32
32
|
"processor": structlog.processors.JSONRenderer(),
|
|
33
|
+
"foreign_pre_chain": [
|
|
34
|
+
structlog.processors.format_exc_info,
|
|
35
|
+
],
|
|
33
36
|
},
|
|
34
37
|
"plain_console": {
|
|
35
38
|
"()": structlog.stdlib.ProcessorFormatter,
|
|
@@ -40,6 +43,9 @@ def receiver_setup_logging(loglevel, logfile, format, colorize, **kwargs): # pr
|
|
|
40
43
|
"processor": structlog.processors.KeyValueRenderer(
|
|
41
44
|
key_order=['timestamp', 'level', 'event', 'logger']
|
|
42
45
|
),
|
|
46
|
+
"foreign_pre_chain": [
|
|
47
|
+
structlog.processors.format_exc_info,
|
|
48
|
+
],
|
|
43
49
|
},
|
|
44
50
|
},
|
|
45
51
|
"handlers": {
|
|
@@ -88,7 +94,6 @@ def receiver_setup_logging(loglevel, logfile, format, colorize, **kwargs): # pr
|
|
|
88
94
|
structlog.stdlib.add_log_level,
|
|
89
95
|
structlog.stdlib.PositionalArgumentsFormatter(),
|
|
90
96
|
structlog.processors.StackInfoRenderer(),
|
|
91
|
-
structlog.processors.format_exc_info,
|
|
92
97
|
structlog.processors.UnicodeDecoder(),
|
|
93
98
|
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
|
94
99
|
],
|
|
@@ -16,6 +16,9 @@ LOGGING = {
|
|
|
16
16
|
"json_formatter": {
|
|
17
17
|
"()": structlog.stdlib.ProcessorFormatter,
|
|
18
18
|
"processor": structlog.processors.JSONRenderer(),
|
|
19
|
+
"foreign_pre_chain": [
|
|
20
|
+
structlog.processors.format_exc_info,
|
|
21
|
+
],
|
|
19
22
|
},
|
|
20
23
|
"plain_console": {
|
|
21
24
|
"()": structlog.stdlib.ProcessorFormatter,
|
|
@@ -54,7 +57,6 @@ structlog.configure(
|
|
|
54
57
|
structlog.stdlib.add_log_level,
|
|
55
58
|
structlog.stdlib.PositionalArgumentsFormatter(),
|
|
56
59
|
structlog.processors.StackInfoRenderer(),
|
|
57
|
-
structlog.processors.format_exc_info,
|
|
58
60
|
structlog.processors.UnicodeDecoder(),
|
|
59
61
|
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
|
60
62
|
],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PermissionsConfig(AppConfig):
|
|
6
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
7
|
+
name = "oxutils.permissions"
|
|
8
|
+
|
|
9
|
+
def ready(self):
|
|
10
|
+
"""Import checks when app is ready."""
|
|
11
|
+
from . import checks # noqa
|
|
12
|
+
|
|
13
|
+
from .presets import (
|
|
14
|
+
register_preset,
|
|
15
|
+
register_access_scopes,
|
|
16
|
+
register_access_applications,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
if hasattr(settings, "PERMISSION_PRESET"):
|
|
20
|
+
register_preset(settings.PERMISSION_PRESET)
|
|
21
|
+
|
|
22
|
+
register_access_applications()
|
|
23
|
+
register_access_scopes()
|
|
@@ -291,4 +291,40 @@ def check_permission_settings(app_configs, **kwargs):
|
|
|
291
291
|
)
|
|
292
292
|
)
|
|
293
293
|
|
|
294
|
+
# Validate EXTRA_PERMISSIONS
|
|
295
|
+
if hasattr(settings, 'EXTRA_PERMISSIONS'):
|
|
296
|
+
extra = settings.EXTRA_PERMISSIONS
|
|
297
|
+
if not isinstance(extra, (list, tuple)):
|
|
298
|
+
errors.append(
|
|
299
|
+
Error(
|
|
300
|
+
'EXTRA_PERMISSIONS must be a list or tuple',
|
|
301
|
+
hint='Set EXTRA_PERMISSIONS = ["dotted.path.ToPermission", ...]',
|
|
302
|
+
id='permissions.E019',
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
else:
|
|
306
|
+
from django.utils.module_loading import import_string
|
|
307
|
+
|
|
308
|
+
for path in extra:
|
|
309
|
+
if not isinstance(path, str):
|
|
310
|
+
errors.append(
|
|
311
|
+
Error(
|
|
312
|
+
f'Each entry in EXTRA_PERMISSIONS must be a string, got {type(path).__name__}',
|
|
313
|
+
hint='Use dotted paths like "myapp.permissions.MyPermission"',
|
|
314
|
+
id='permissions.E020',
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
continue
|
|
318
|
+
try:
|
|
319
|
+
cls = import_string(path)
|
|
320
|
+
except ImportError:
|
|
321
|
+
errors.append(
|
|
322
|
+
Error(
|
|
323
|
+
f'Cannot import "{path}" from EXTRA_PERMISSIONS',
|
|
324
|
+
hint='Check that the module and class exist',
|
|
325
|
+
id='permissions.E021',
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
continue
|
|
329
|
+
|
|
294
330
|
return errors
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from functools import lru_cache
|
|
1
2
|
from typing import Optional
|
|
2
3
|
|
|
3
4
|
from django.conf import settings
|
|
4
5
|
from django.core.exceptions import ImproperlyConfigured
|
|
5
6
|
from django.http import HttpRequest
|
|
7
|
+
from django.utils.module_loading import import_string
|
|
6
8
|
from ninja_extra.controllers import ControllerBase
|
|
7
9
|
from ninja_extra.permissions import BasePermission
|
|
8
10
|
|
|
@@ -199,3 +201,59 @@ def access_manager(actions: str):
|
|
|
199
201
|
)
|
|
200
202
|
|
|
201
203
|
return ScopePermission(perm, context)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@lru_cache(maxsize=1)
|
|
207
|
+
def extra_permissions():
|
|
208
|
+
"""
|
|
209
|
+
Return the list of permission instances defined in
|
|
210
|
+
``EXTRA_PERMISSIONS`` (settings.py).
|
|
211
|
+
|
|
212
|
+
Each entry is a dotted path to an **already instantiated** permission
|
|
213
|
+
object (e.g. a module-level singleton). The object is imported via
|
|
214
|
+
:func:`django.utils.module_loading.import_string` and returned as-is.
|
|
215
|
+
|
|
216
|
+
The result is cached via :func:`functools.lru_cache` so that
|
|
217
|
+
``import_string`` is only called once per process.
|
|
218
|
+
|
|
219
|
+
Example::
|
|
220
|
+
|
|
221
|
+
# myapp/permissions.py
|
|
222
|
+
from ninja_extra.permissions import BasePermission
|
|
223
|
+
|
|
224
|
+
class IsPremium(BasePermission):
|
|
225
|
+
def has_permission(self, request, controller):
|
|
226
|
+
return request.user.is_premium
|
|
227
|
+
|
|
228
|
+
IsPremium = IsPremium() # <-- singleton instance
|
|
229
|
+
|
|
230
|
+
# settings.py
|
|
231
|
+
EXTRA_PERMISSIONS = [
|
|
232
|
+
"myapp.permissions.IsPremium",
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
# controller
|
|
236
|
+
from oxutils.permissions import extra_permissions
|
|
237
|
+
|
|
238
|
+
@api_controller(
|
|
239
|
+
"/api",
|
|
240
|
+
permissions=[*extra_permissions(), ScopePermission("articles:r")],
|
|
241
|
+
)
|
|
242
|
+
class MyController:
|
|
243
|
+
...
|
|
244
|
+
"""
|
|
245
|
+
paths = getattr(settings, "EXTRA_PERMISSIONS", [])
|
|
246
|
+
if not isinstance(paths, (list, tuple)):
|
|
247
|
+
raise ImproperlyConfigured(
|
|
248
|
+
"EXTRA_PERMISSIONS must be a list or tuple of dotted paths."
|
|
249
|
+
)
|
|
250
|
+
instances = []
|
|
251
|
+
for path in paths:
|
|
252
|
+
try:
|
|
253
|
+
instance = import_string(path)
|
|
254
|
+
except ImportError as exc:
|
|
255
|
+
raise ImproperlyConfigured(
|
|
256
|
+
f"Cannot import permission from EXTRA_PERMISSIONS: {path}"
|
|
257
|
+
) from exc
|
|
258
|
+
instances.append(instance)
|
|
259
|
+
return instances
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Permission preset auto-discovery.
|
|
3
|
+
|
|
4
|
+
Walk installed apps, collect their ``PERMISSION_PRESET`` dicts,
|
|
5
|
+
``ACCESS_APPLICATION_NAME`` and ``ACCESS_SCOPES``, and merge them
|
|
6
|
+
into the global configuration.
|
|
7
|
+
|
|
8
|
+
Called from ``PermissionsConfig.ready()`` when the app registry is ready.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import importlib
|
|
12
|
+
|
|
13
|
+
import structlog
|
|
14
|
+
from django.apps import apps
|
|
15
|
+
from django.conf import settings
|
|
16
|
+
|
|
17
|
+
logger = structlog.get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def discover_app_presets() -> list[dict]:
|
|
21
|
+
"""Walk installed apps and collect their ``PERMISSION_PRESET``."""
|
|
22
|
+
presets: list[dict] = []
|
|
23
|
+
for app_config in apps.get_app_configs():
|
|
24
|
+
try:
|
|
25
|
+
mod = importlib.import_module(f"{app_config.name}.permissions")
|
|
26
|
+
except (ModuleNotFoundError, ImportError):
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
preset = getattr(mod, "PERMISSION_PRESET", None)
|
|
30
|
+
if not isinstance(preset, dict):
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
for role in preset.get("roles", []):
|
|
34
|
+
role.setdefault("app", app_config.label)
|
|
35
|
+
for group in preset.get("groups", []):
|
|
36
|
+
group.setdefault("app", app_config.label)
|
|
37
|
+
for grant in preset.get("role_grants", []):
|
|
38
|
+
grant.setdefault("app", app_config.label)
|
|
39
|
+
|
|
40
|
+
presets.append(preset)
|
|
41
|
+
logger.info(
|
|
42
|
+
"permission_preset_discovered",
|
|
43
|
+
app=app_config.label,
|
|
44
|
+
roles=len(preset.get("roles", [])),
|
|
45
|
+
groups=len(preset.get("groups", [])),
|
|
46
|
+
grants=len(preset.get("role_grants", [])),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return presets
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def discover_access_scopes() -> list[str]:
|
|
53
|
+
"""Walk installed apps and collect their ``ACCESS_SCOPES``."""
|
|
54
|
+
scopes: list[str] = []
|
|
55
|
+
for app_config in apps.get_app_configs():
|
|
56
|
+
try:
|
|
57
|
+
mod = importlib.import_module(f"{app_config.name}.permissions")
|
|
58
|
+
except (ModuleNotFoundError, ImportError):
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
app_scopes = getattr(mod, "ACCESS_SCOPES", None)
|
|
62
|
+
if isinstance(app_scopes, list):
|
|
63
|
+
for scope in app_scopes:
|
|
64
|
+
if scope not in scopes:
|
|
65
|
+
scopes.append(scope)
|
|
66
|
+
logger.info(
|
|
67
|
+
"access_scope_discovered",
|
|
68
|
+
app=app_config.label,
|
|
69
|
+
scope=scope,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return scopes
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def discover_access_applications() -> list[str]:
|
|
76
|
+
"""Walk installed apps and collect their ``ACCESS_APPLICATION_NAME``."""
|
|
77
|
+
applications: list[str] = []
|
|
78
|
+
for app_config in apps.get_app_configs():
|
|
79
|
+
try:
|
|
80
|
+
mod = importlib.import_module(f"{app_config.name}.permissions")
|
|
81
|
+
except (ModuleNotFoundError, ImportError):
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
app_name = getattr(mod, "ACCESS_APPLICATION_NAME", None)
|
|
85
|
+
if isinstance(app_name, str) and app_name not in applications:
|
|
86
|
+
applications.append(app_name)
|
|
87
|
+
logger.info(
|
|
88
|
+
"access_application_discovered",
|
|
89
|
+
app=app_config.label,
|
|
90
|
+
application=app_name,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return applications
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def register_preset(base_preset: dict) -> dict:
|
|
97
|
+
"""Extend *base_preset* with presets discovered from installed apps."""
|
|
98
|
+
base_roles = base_preset.setdefault("roles", [])
|
|
99
|
+
base_groups = base_preset.setdefault("groups", [])
|
|
100
|
+
base_grants = base_preset.setdefault("role_grants", [])
|
|
101
|
+
|
|
102
|
+
for preset in discover_app_presets():
|
|
103
|
+
base_roles.extend(preset.get("roles", []))
|
|
104
|
+
base_groups.extend(preset.get("groups", []))
|
|
105
|
+
base_grants.extend(preset.get("role_grants", []))
|
|
106
|
+
|
|
107
|
+
return base_preset
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def register_access_scopes() -> None:
|
|
111
|
+
"""Extend ``settings.ACCESS_SCOPES`` with scopes discovered from installed apps."""
|
|
112
|
+
existing = list(getattr(settings, "ACCESS_SCOPES", []))
|
|
113
|
+
|
|
114
|
+
for scope in discover_access_scopes():
|
|
115
|
+
if scope not in existing:
|
|
116
|
+
existing.append(scope)
|
|
117
|
+
|
|
118
|
+
settings.ACCESS_SCOPES = existing
|
|
119
|
+
logger.info("access_scopes_registered", count=len(existing))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def register_access_applications() -> None:
|
|
123
|
+
"""Extend ``settings.ACCESS_APPLICATIONS`` with apps discovered from installed apps."""
|
|
124
|
+
existing = list(getattr(settings, "ACCESS_APPLICATIONS", []))
|
|
125
|
+
|
|
126
|
+
for app_name in discover_access_applications():
|
|
127
|
+
if app_name not in existing:
|
|
128
|
+
existing.append(app_name)
|
|
129
|
+
|
|
130
|
+
settings.ACCESS_APPLICATIONS = existing
|
|
131
|
+
logger.info("access_applications_registered", count=len(existing))
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""OxUtils - Production-ready utilities for Django applications.
|
|
2
|
-
|
|
3
|
-
This package provides:
|
|
4
|
-
- JWT authentication with JWKS support
|
|
5
|
-
- Multi-tenant architecture (django-tenants integration, middleware, signals)
|
|
6
|
-
- Auth system: invitations, MFA, password reset, registration, sessions
|
|
7
|
-
- Structured logging with correlation IDs
|
|
8
|
-
- Audit system with S3 export
|
|
9
|
-
- Celery integration
|
|
10
|
-
- Django model mixins (ChangeTracker, CookieToken, SafeDelete)
|
|
11
|
-
- Permission management & authorization
|
|
12
|
-
- PDF generation
|
|
13
|
-
- Pagination utilities
|
|
14
|
-
- Currency utilities
|
|
15
|
-
- Custom exceptions, enums, and type definitions
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
__version__ = "0.4.2"
|
|
19
|
-
|
|
20
|
-
from oxutils.conf import AUDIT_MIDDLEWARE, UTILS_APPS
|
|
21
|
-
from oxutils.settings import oxi_settings
|
|
22
|
-
|
|
23
|
-
__all__ = [
|
|
24
|
-
"oxi_settings",
|
|
25
|
-
"UTILS_APPS",
|
|
26
|
-
"AUDIT_MIDDLEWARE",
|
|
27
|
-
"__version__",
|
|
28
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|