fix: fixed `should_reload` behaviour, close PostgreSQL connections, block until `PostgresqlWatcher` is ready, refactorings (#29)

* chore: updated dev requirements

* chore: format code with black

* chore: updated .gitignore

* fix: type hint, multiprocessing.Pipe is a Callable and not a type

* fix: make Watcher.should_reload return value consistent

* fix: Handle Connection and Process objects consistenly and close them before creating new ones

* feat: Customize the postgres channel name

* chore: Some code reorg
       - Make PostgresqlWatcher.create_subscription_process a private method
       - Rename casbin_subscription to _casbin_channel_subscription

* docs: added doc string for PostgresqlWatcher.update

* refactor: PostgresqlWatcher.set_update_callback

* refactor!: Rename 'start_process' flag to 'start_listening'

* docs: Added doc string to PostgresqlWatcher.__init__

* fix: Added proper destructor for PostgresqlWatcher

* chore: fix type hints and proper handling of the channel_name argument and its default value

* test: fix tests

      decrease select timeout to one second in child Process
      remove infinite timout in PostgresqlWatcher.should_reload
      create a new watcher instance for every test case

* feat: Setup logging module for unit tests

* fix: typo

* feat: channel subscription with proper resource cleanup

      Moved channel subscription function to separate file and added context
      manager for the connection, that handles SIGINT, SIGTERM for proper resource
      cleanup

* chore: removed unnecessary tests

* feat: Wait for Process to be ready to receive messages from PostgreSQL

* test: multiple instances of the watcher

* test: make sure every test case uses its own channel

* test: no update

* refactor: moved code into with block

* feat: automaticall call the update handler if it is provided

* refactor: sorted imports

* docs: updated README

* refactor: improved readibility

* refactor: resolve a potential infinite loop with a custom Exception

* refactor: make timeout configurable by the user

* fix: docs

* fix: ensure type hint compatibility with Python 3.9

* feat: make sure multiple calls of update() get resolved by one call of should_reload() thanks to @pradeepranwa1
8 files changed
tree: 0e3130331394c0ddfd36cb94fd65a76a49308c21
  1. .github/
  2. examples/
  3. postgresql_watcher/
  4. tests/
  5. .gitignore
  6. .releaserc.json
  7. CHANGELOG.md
  8. dev_requirements.txt
  9. LICENSE
  10. README.md
  11. requirements.txt
  12. setup.cfg
  13. setup.py
README.md

postgresql-watcher

Build Status Coverage Status Version PyPI - Wheel Pyversions Download Discord

Casbin watcher based on PostgreSQL for monitoring updates to casbin policies.

Installation

pip install casbin-postgresql-watcher

Basic Usage Example

from flask_authz import CasbinEnforcer
from postgresql_watcher import PostgresqlWatcher
from flask import Flask
from casbin.persist.adapters import FileAdapter

casbin_enforcer = CasbinEnforcer(app, adapter)
watcher = PostgresqlWatcher(host=HOST, port=PORT, user=USER, password=PASSWORD, dbname=DBNAME)
watcher.set_update_callback(casbin_enforcer.load_policy)
casbin_enforcer.set_watcher(watcher)

# Call should_reload before every call of enforce to make sure
# the policy is update to date
watcher.should_reload()
if casbin_enforcer.enforce("alice", "data1", "read"):
    # permit alice to read data1
    pass
else:
    # deny the request, show an error
    pass

alternatively, if you need more control

from flask_authz import CasbinEnforcer
from postgresql_watcher import PostgresqlWatcher
from flask import Flask
from casbin.persist.adapters import FileAdapter

casbin_enforcer = CasbinEnforcer(app, adapter)
watcher = PostgresqlWatcher(host=HOST, port=PORT, user=USER, password=PASSWORD, dbname=DBNAME)
casbin_enforcer.set_watcher(watcher)

# Call should_reload before every call of enforce to make sure
# the policy is update to date
if watcher.should_reload():
    casbin_enforcer.load_policy()

if casbin_enforcer.enforce("alice", "data1", "read"):
    # permit alice to read data1
    pass
else:
    # deny the request, show an error
    pass

Basic Usage Example With SSL Enabled

See PostgresQL documentation for full details of SSL parameters.

...
watcher = PostgresqlWatcher(host=HOST, port=PORT, user=USER, password=PASSWORD, dbname=DBNAME, sslmode="verify_full", sslcert=SSLCERT, sslrootcert=SSLROOTCERT, sslkey=SSLKEY)
...