Skip to content

Commit 097b9d5

Browse files
authored
Merge pull request #1 from JWDobken/pypi-package
Pypi package
2 parents bff4ebf + 17ec640 commit 097b9d5

File tree

12 files changed

+146
-143
lines changed

12 files changed

+146
-143
lines changed

ci.yml renamed to .github/workflows/ci.yml

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
push:
55
# Avoid using all the resources/limits available by checking only
66
# relevant branches and tags. Other branches can be checked via PRs.
7-
branches: [main]
7+
branches: [main, pypi-package]
88
tags: ["v[0-9]*", "[0-9]+.[0-9]+*"] # Match tags that resemble a version
99
pull_request: # Run in every PR
1010
workflow_dispatch: # Allow manually triggering the workflow
@@ -32,7 +32,7 @@ jobs:
3232
with: { fetch-depth: 0 } # deep clone for setuptools-scm
3333
- uses: actions/setup-python@v4
3434
id: setup-python
35-
with: { python-version: "3.11" }
35+
with: { python-version: "3.10" }
3636
- name: Run static analysis and format checkers
3737
run: pipx run pre-commit run --all-files --show-diff-on-failure
3838
- name: Build package distribution files
@@ -56,13 +56,25 @@ jobs:
5656
strategy:
5757
matrix:
5858
python:
59-
- "3.7" # oldest Python supported by PSF
60-
- "3.11" # newest Python that is stable
59+
- "3.7"
60+
- "3.10"
6161
platform:
6262
- ubuntu-latest
63-
- macos-latest
64-
- windows-latest
63+
# - macos-latest NO DOCKER
64+
# - windows-latest NO DOCKER
6565
runs-on: ${{ matrix.platform }}
66+
67+
services:
68+
postgres:
69+
image: postgres:latest
70+
env:
71+
POSTGRES_USER: jwdobken
72+
POSTGRES_PASSWORD: postgres
73+
POSTGRES_DB: test_db
74+
ports:
75+
- 5432:5432
76+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
77+
6678
steps:
6779
- uses: actions/checkout@v3
6880
- uses: actions/setup-python@v4
@@ -79,34 +91,34 @@ jobs:
7991
-- -rFEx --durations 10 --color yes # pytest args
8092
- name: Generate coverage report
8193
run: pipx run coverage lcov -o coverage.lcov
82-
- name: Upload partial coverage report
83-
uses: coverallsapp/github-action@master
84-
with:
85-
path-to-lcov: coverage.lcov
86-
github-token: ${{ secrets.GITHUB_TOKEN }}
87-
flag-name: ${{ matrix.platform }} - py${{ matrix.python }}
88-
parallel: true
94+
# - name: Upload partial coverage report
95+
# uses: coverallsapp/github-action@master
96+
# with:
97+
# path-to-lcov: coverage.lcov
98+
# github-token: ${{ secrets.GITHUB_TOKEN }}
99+
# flag-name: ${{ matrix.platform }} - py${{ matrix.python }}
100+
# parallel: true
89101

90-
finalize:
91-
needs: test
92-
runs-on: ubuntu-latest
93-
steps:
94-
- name: Finalize coverage report
95-
uses: coverallsapp/github-action@master
96-
with:
97-
github-token: ${{ secrets.GITHUB_TOKEN }}
98-
parallel-finished: true
102+
# finalize:
103+
# needs: test
104+
# runs-on: ubuntu-latest
105+
# steps:
106+
# - name: Finalize coverage report
107+
# uses: coverallsapp/github-action@master
108+
# with:
109+
# github-token: ${{ secrets.GITHUB_TOKEN }}
110+
# parallel-finished: true
99111

100112
publish:
101-
needs: finalize
113+
needs: test # finalize
102114
if: ${{ github.event_name == 'push' && contains(github.ref, 'refs/tags/') }}
103115
runs-on: ubuntu-latest
104116
permissions:
105117
contents: write
106118
steps:
107119
- uses: actions/checkout@v3
108120
- uses: actions/setup-python@v4
109-
with: { python-version: "3.11" }
121+
with: { python-version: "3.10" }
110122
- name: Retrieve pre-built distribution files
111123
uses: actions/download-artifact@v3
112124
with: { name: python-distribution-files, path: dist/ }

.github/workflows/pytest.yml

Lines changed: 0 additions & 36 deletions
This file was deleted.

.pre-commit-config.yaml

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,62 @@
1-
exclude: '^docs/conf.py'
1+
exclude: "^docs/conf.py"
22

33
repos:
4-
- repo: https://github.com/pre-commit/pre-commit-hooks
5-
rev: v4.4.0
6-
hooks:
7-
- id: trailing-whitespace
8-
- id: check-added-large-files
9-
- id: check-ast
10-
- id: check-json
11-
- id: check-merge-conflict
12-
- id: check-xml
13-
- id: check-yaml
14-
- id: debug-statements
15-
- id: end-of-file-fixer
16-
- id: requirements-txt-fixer
17-
- id: mixed-line-ending
18-
args: ['--fix=auto'] # replace 'auto' with 'lf' to enforce Linux/Mac line endings or 'crlf' for Windows
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v4.4.0
6+
hooks:
7+
- id: trailing-whitespace
8+
- id: check-added-large-files
9+
- id: check-ast
10+
- id: check-json
11+
- id: check-merge-conflict
12+
- id: check-xml
13+
- id: check-yaml
14+
- id: debug-statements
15+
- id: end-of-file-fixer
16+
- id: mixed-line-ending
17+
args: ["--fix=auto"] # replace 'auto' with 'lf' to enforce Linux/Mac line endings or 'crlf' for Windows
1918

20-
## If you want to automatically "modernize" your Python code:
21-
# - repo: https://github.com/asottile/pyupgrade
22-
# rev: v3.7.0
23-
# hooks:
24-
# - id: pyupgrade
25-
# args: ['--py37-plus']
26-
27-
## If you want to avoid flake8 errors due to unused vars or imports:
28-
# - repo: https://github.com/PyCQA/autoflake
29-
# rev: v2.1.1
30-
# hooks:
31-
# - id: autoflake
32-
# args: [
33-
# --in-place,
34-
# --remove-all-unused-imports,
35-
# --remove-unused-variables,
36-
# ]
19+
## If you want to automatically "modernize" your Python code:
20+
# - repo: https://github.com/asottile/pyupgrade
21+
# rev: v3.7.0
22+
# hooks:
23+
# - id: pyupgrade
24+
# args: ['--py37-plus']
3725

38-
- repo: https://github.com/PyCQA/isort
39-
rev: 5.11.5
40-
hooks:
41-
- id: isort
26+
## If you want to avoid flake8 errors due to unused vars or imports:
27+
# - repo: https://github.com/PyCQA/autoflake
28+
# rev: v2.1.1
29+
# hooks:
30+
# - id: autoflake
31+
# args: [
32+
# --in-place,
33+
# --remove-all-unused-imports,
34+
# --remove-unused-variables,
35+
# ]
4236

43-
- repo: https://github.com/psf/black
44-
rev: stable
45-
hooks:
46-
- id: black
47-
language_version: python3
48-
49-
## If like to embrace black styles even in the docs:
50-
# - repo: https://github.com/asottile/blacken-docs
51-
# rev: v1.13.0
52-
# hooks:
53-
# - id: blacken-docs
54-
# additional_dependencies: [black]
37+
- repo: https://github.com/PyCQA/isort
38+
rev: 5.11.5
39+
hooks:
40+
- id: isort
5541

56-
- repo: https://github.com/PyCQA/flake8
57-
rev: 5.0.4
58-
hooks:
59-
- id: flake8
60-
## You can add flake8 plugins via `additional_dependencies`:
61-
# additional_dependencies: [flake8-bugbear]
42+
- repo: https://github.com/psf/black
43+
rev: stable
44+
hooks:
45+
- id: black
46+
language_version: python3
6247

48+
## If like to embrace black styles even in the docs:
49+
# - repo: https://github.com/asottile/blacken-docs
50+
# rev: v1.13.0
51+
# hooks:
52+
# - id: blacken-docs
53+
# additional_dependencies: [black]
54+
- repo: https://github.com/PyCQA/flake8
55+
rev: 5.0.4
56+
hooks:
57+
- id: flake8
58+
## You can add flake8 plugins via `additional_dependencies`:
59+
# additional_dependencies: [flake8-bugbear]
6360
## Check for misspells in documentation files:
6461
# - repo: https://github.com/codespell-project/codespell
6562
# rev: v2.2.5

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414

1515
Row-Level Security (RLS) in SQLAlchemy for PostgreSQL with [Row Security Policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html):
1616

17-
- Restrict access to specific rows 🔒 based on user permissions, minimizing unauthorized data exposure.
18-
- Managing who sees what becomes a breeze 😎, without solely relying on application-level permissions.
17+
- Restrict access to specific rows 🔒 minimizing unauthorized data exposure.
1918
- Perfect for Scalability and Multi-Tenancy: keep the data playground organized 🏢, ensuring each tenant plays in their own sandbox.
2019

2120
> **Warning**
@@ -81,3 +80,8 @@ then ...
8180
- [ ] Support for Alembic
8281
- [ ] How to deal with `BYPASSRLS` such as table owners?
8382
- [ ] When item is tried to delete, no error is raised?
83+
- [ ] Python 3.11
84+
85+
## Final note
86+
87+
At the moment this module is work-in-progress and therefore experimental. All feedback and ideas are 100% welcome! So feel free to contribute or reach out to me!

setup.cfg

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ package_dir =
5050
install_requires =
5151
importlib-metadata; python_version<"3.8"
5252
alembic_utils>=0.8
53-
pydantic>=2.6
53+
pydantic>=2.5
5454

5555

5656
[options.packages.find]
@@ -68,6 +68,10 @@ testing =
6868
setuptools
6969
pytest
7070
pytest-cov
71+
pytest_asyncio
72+
faker
73+
asyncpg
74+
greenlet
7175

7276
[options.entry_points]
7377
# Add here console scripts like:

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
PyScaffold helps you to put up the scaffold of your new Python project.
77
Learn more under: https://pyscaffold.org/
88
"""
9+
910
from setuptools import setup
1011

1112
if __name__ == "__main__":
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
from typing import List
22

33
from alembic_utils.pg_function import PGFunction
4-
from sqlalchemy import Connection
54

65
drop_all_policies_on_table = PGFunction(
76
schema="public",
87
signature="drop_all_policies_on_table(target_schema text,target_table_name text)",
98
definition="""
109
RETURNS void LANGUAGE plpgsql AS $$
11-
DECLARE
10+
DECLARE
1211
policy_name text;
1312
sql_text text;
14-
BEGIN
13+
BEGIN
1514
FOR policy_name IN (
16-
SELECT policyname
17-
FROM pg_policies
15+
SELECT policyname
16+
FROM pg_policies
1817
WHERE schemaname = target_schema AND tablename = target_table_name
1918
)
2019
LOOP
21-
sql_text := format('DROP POLICY "%s" on %I.%I', policy_name, target_schema, target_table_name);
20+
sql_text := format('DROP POLICY "%s" on %I.%I',
21+
policy_name, target_schema, target_table_name);
2222
RAISE NOTICE '%', sql_text;
2323
EXECUTE sql_text;
2424
END LOOP;
@@ -29,4 +29,3 @@
2929

3030
def get_functions() -> List[PGFunction]:
3131
return []
32-
return [drop_all_policies_on_table]

src/fastapi_rowsecurity/main.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
from typing import Type
22

33
from sqlalchemy import event
4-
5-
# from sqlalchemy.dialects.postgresql.asyncpg import ProgrammingError
64
from sqlalchemy.ext.declarative import DeclarativeMeta
75

8-
from .functions import get_functions
96
from .policies import get_policies
107

118

src/fastapi_rowsecurity/policies.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,23 @@ def get_policies(Base) -> List[PGPolicy]:
1313
table_name = mapper.tables[0].fullname
1414
schema_name = mapper.tables[0].schema or "public"
1515
# Set the default row-level security policy
16-
policy_lists.append(EnableRowLevelSecurity(schema=schema_name, on_entity=table_name))
17-
policy_lists.append(ForceRowLevelSecurity(schema=schema_name, on_entity=table_name))
16+
policy_lists.append(
17+
EnableRowLevelSecurity(schema=schema_name, on_entity=table_name)
18+
)
19+
policy_lists.append(
20+
ForceRowLevelSecurity(schema=schema_name, on_entity=table_name)
21+
)
1822
for ix, permission in enumerate(mapper.class_.__rls_policies__()):
19-
table_policies = [permission.policy] if isinstance(permission.policy, str) else permission.policy
23+
table_policies = (
24+
[permission.policy]
25+
if isinstance(permission.policy, str)
26+
else permission.policy
27+
)
2028
for pol in table_policies:
21-
policy_name = f"{table_name}_{permission.__class__.__name__}_{pol}_policy_{ix}".lower()
29+
policy_name = (
30+
f"{table_name}_{permission.__class__.__name__}"
31+
f"_{pol}_policy_{ix}".lower()
32+
)
2233
if pol in ["ALL", "SELECT", "UPDATE", "DELETE"]:
2334
policy_lists.append(
2435
PGPolicy(

0 commit comments

Comments
 (0)