pycasbin / async-sqlalchemy-adapter

Async SQLAlchemy Adapter for PyCasbin
https://github.com/casbin/pycasbin
Apache License 2.0
7 stars 6 forks source link

The save_policy() function does not seem to work #10

Closed ETCHKILI closed 1 year ago

ETCHKILI commented 1 year ago
import asyncio

from casbin import Enforcer
from casbin_async_sqlalchemy_adapter import Adapter

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.asyncio import create_async_engine

from sqlalchemy.orm import declarative_base

Base = declarative_base()

class CustomCasbinRule(Base):
    __tablename__ = "custom_casbin_rule"

    id = Column(Integer, primary_key=True)
    ptype = Column(String(255))
    v0 = Column(String(255))
    v1 = Column(String(255))
    v2 = Column(String(255))
    v3 = Column(String(255))
    v4 = Column(String(255))
    v5 = Column(String(255))

    def __str__(self):
        arr = [self.ptype]
        for v in (self.v0, self.v1, self.v2, self.v3, self.v4, self.v5):
            if v is None:
                break
            arr.append(v)
        return ", ".join(arr)

    def __repr__(self):
        return '<CasbinRule {}: "{}">'.format(self.id, str(self))

async def run_code():
    engine = create_async_engine("postgresql+asyncpg://postgres:------@localhost:44444/postgres")
    adapter = Adapter(engine, CustomCasbinRule)
    enforcer = Enforcer("./model.conf", adapter)

    await enforcer.load_policy()

    res = await enforcer.add_policy('data_admin', 'data', 'read')
    await enforcer.add_policy('data_admin', 'data', 'write')
    await enforcer.add_policy('group_admin', 'group', 'read')
    await enforcer.add_policy('group_admin', 'group', 'write')
    await enforcer.add_grouping_policy('alice', 'data_admin')
    await enforcer.add_grouping_policy('bob', 'group_admin')

    await enforcer.save_policy()

    print(f'Redundant Policy: {not res}')

    result = enforcer.enforce('alice', 'data', 'read')
    print(f'Enforce Result: {result}')

if __name__ == "__main__":
    asyncio.run(run_code())

after i ran this, i got something wrong with the save_policy() function

Traceback (most recent call last):
  File "/home/abin/prj/test/main.py", line 63, in <module>
    asyncio.run(run_code())
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/home/abin/prj/test/main.py", line 54, in run_code
    await enforcer.save_policy()
  File "/home/abin/prj/test/venv/lib/python3.10/site-packages/casbin/core_enforcer.py", line 214, in save_policy
    await self.adapter.save_policy(self.model)
  File "/home/abin/prj/test/venv/lib/python3.10/site-packages/casbin_async_sqlalchemy_adapter/adapter.py", line 147, in save_policy
    await session.execute(query.delete())
AttributeError: 'Select' object has no attribute 'delete'. Did you mean: 'is_delete'?

The traceback information indicates that this function in adapter.py didn't work:

    async def save_policy(self, model):
        """saves all policy rules to the storage."""
        async with self._session_scope() as session:
            query = select(self._db_class)
            await session.execute(query.delete())
            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:
                        await self._save_policy_line(ptype, rule)
        return True

I'm not skilled with sqlalchemy, can anyone help?

casbin-bot commented 1 year ago

@techoner @Nekotoxin

hsluoyz commented 1 year ago

@BustDot

ChenHaolinOlym commented 1 year ago

@ETCHKILI What's your casbin_async_sqlalchemy_adapter version? Update to 1.2.0 may solve this problem.

ETCHKILI commented 1 year ago

@ChenHaolinOlym After i switch to v1.2.0, it does work.The data was saved as expected, however i still encountered some error. I can't tell the reason, maybe it's because of the behavior of asyncio. I'm working on ubuntu 18.04, python 3.10, if it's needed.

Fatal error on SSL transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x7fb71c797b50>
transport: <_SelectorSocketTransport closing fd=9>
Traceback (most recent call last):
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 924, in write
    n = self._sock.send(data)
OSError: [Errno 9] Bad file descriptor

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.10/asyncio/sslproto.py", line 690, in _process_write_backlog
    self._transport.write(chunk)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 930, in write
    self._fatal_error(exc, 'Fatal write error on socket transport')
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 725, in _fatal_error
    self._force_close(exc)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 737, in _force_close
    self._loop.call_soon(self._call_connection_lost, exc)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 753, in call_soon
    self._check_closed()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
ChenHaolinOlym commented 1 year ago

It seems that this is an asyncio problem, not related to casbin. But you may try

loop = asyncio.get_event_loop()
loop.run_until_complete(run_code())

instead of

asyncio.run(run_code())
ETCHKILI commented 1 year ago

@ChenHaolinOlym Thanks! This solves my problem.