Skip to content

Commit 756f640

Browse files
v0.0.6 :octocat:
1 parent 1ef166b commit 756f640

24 files changed

Lines changed: 552 additions & 466 deletions

.flake8

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[flake8]
2+
exclude = __pycache__,built,build,venv
3+
ignore = E203, E266, W503
4+
max-line-length = 88
5+
max-complexity = 18
6+
select = B,C,E,F,W,T4,B9
7+
per-file-ignores =
8+
guardpost/__init__.py:F401

.github/workflows/build.yml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
name: Build
2+
3+
on:
4+
release:
5+
types: [published]
6+
push:
7+
branches:
8+
- master
9+
- ci
10+
pull_request:
11+
branches:
12+
- "*"
13+
14+
env:
15+
PROJECT_NAME: guardpost
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-18.04
20+
strategy:
21+
matrix:
22+
python-version: [3.6, 3.7, 3.8, 3.9]
23+
24+
steps:
25+
- uses: actions/checkout@v1
26+
with:
27+
fetch-depth: 9
28+
submodules: false
29+
30+
- name: Use Python ${{ matrix.python-version }}
31+
uses: actions/setup-python@v1
32+
with:
33+
python-version: ${{ matrix.python-version }}
34+
35+
- uses: actions/cache@v1
36+
id: depcache
37+
with:
38+
path: deps
39+
key: requirements-pip-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}
40+
41+
- name: Download dependencies
42+
if: steps.depcache.outputs.cache-hit != 'true'
43+
run: |
44+
pip download --dest=deps -r requirements.txt
45+
46+
- name: Install dependencies
47+
run: |
48+
pip install -U --no-index --find-links=deps deps/*
49+
50+
- name: Run tests
51+
run: |
52+
flake8 && pytest --doctest-modules --junitxml=junit/pytest-results-${{ matrix.python-version }}.xml --cov=$PROJECT_NAME --cov-report=xml tests/
53+
54+
- name: Upload pytest test results
55+
uses: actions/upload-artifact@master
56+
with:
57+
name: pytest-results-${{ matrix.python-version }}
58+
path: junit/pytest-results-${{ matrix.python-version }}.xml
59+
if: always()
60+
61+
- name: Codecov
62+
run: |
63+
bash <(curl -s https://codecov.io/bash)
64+
65+
- name: Install distribution dependencies
66+
run: pip install --upgrade twine setuptools wheel
67+
if: matrix.python-version == 3.8 || matrix.python-version == 3.9
68+
69+
- name: Create distribution package
70+
run: python setup.py sdist bdist_wheel
71+
if: matrix.python-version == 3.8 || matrix.python-version == 3.9
72+
73+
- name: Upload distribution package
74+
uses: actions/upload-artifact@master
75+
with:
76+
name: dist-package-${{ matrix.python-version }}
77+
path: dist
78+
if: matrix.python-version == 3.8 || matrix.python-version == 3.9
79+
80+
publish:
81+
runs-on: ubuntu-18.04
82+
needs: build
83+
if: github.event_name == 'release'
84+
steps:
85+
- name: Download a distribution artifact
86+
uses: actions/download-artifact@v2
87+
with:
88+
name: dist-package-3.9
89+
path: dist
90+
- name: Publish distribution 📦 to Test PyPI
91+
uses: pypa/gh-action-pypi-publish@master
92+
with:
93+
skip_existing: true
94+
user: __token__
95+
password: ${{ secrets.test_pypi_password }}
96+
repository_url: https://test.pypi.org/legacy/
97+
- name: Publish distribution 📦 to PyPI
98+
uses: pypa/gh-action-pypi-publish@master
99+
with:
100+
user: __token__
101+
password: ${{ secrets.pypi_password }}

.gitignore

Lines changed: 7 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,8 @@
1-
# Byte-compiled / optimized / DLL files
2-
__pycache__/
3-
*.py[cod]
4-
*$py.class
5-
6-
# C extensions
7-
*.so
8-
9-
# Distribution / packaging
10-
.Python
11-
build/
12-
develop-eggs/
13-
dist/
14-
downloads/
15-
eggs/
16-
.eggs/
17-
lib/
18-
lib64/
19-
parts/
20-
sdist/
21-
var/
22-
wheels/
23-
*.egg-info/
24-
.installed.cfg
25-
*.egg
26-
MANIFEST
27-
28-
# PyInstaller
29-
# Usually these files are written by a python script from a template
30-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
31-
*.manifest
32-
*.spec
33-
34-
# Installer logs
35-
pip-log.txt
36-
pip-delete-this-directory.txt
37-
38-
# Unit test / coverage reports
39-
htmlcov/
40-
.tox/
1+
.vscode
2+
venv
3+
htmlcov
414
.coverage
42-
.coverage.*
43-
.cache
44-
nosetests.xml
45-
coverage.xml
46-
*.cover
47-
.hypothesis/
48-
.pytest_cache/
49-
50-
# Translations
51-
*.mo
52-
*.pot
53-
54-
# Django stuff:
55-
*.log
56-
local_settings.py
57-
db.sqlite3
58-
59-
# Flask stuff:
60-
instance/
61-
.webassets-cache
62-
63-
# Scrapy stuff:
64-
.scrapy
65-
66-
# Sphinx documentation
67-
docs/_build/
68-
69-
# PyBuilder
70-
target/
71-
72-
# Jupyter Notebook
73-
.ipynb_checkpoints
74-
75-
# pyenv
76-
.python-version
77-
78-
# celery beat schedule file
79-
celerybeat-schedule
80-
81-
# SageMath parsed files
82-
*.sage.py
83-
84-
# Environments
85-
.env
86-
.venv
87-
env/
88-
venv/
89-
ENV/
90-
env.bak/
91-
venv.bak/
92-
93-
# Spyder project settings
94-
.spyderproject
95-
.spyproject
96-
97-
# Rope project settings
98-
.ropeproject
99-
100-
# mkdocs documentation
101-
/site
102-
103-
# mypy
104-
.mypy_cache/
105-
106-
.idea/
5+
__pycache__
6+
*.egg-info
7+
*.tar.gz
8+
.mypy_cache

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.0.6] - 2020-12-12 :octocat:
9+
- Completely migrates to GitHub Workflows
10+
- Improves build to test Python 3.6 and 3.9
11+
- Adds a changelog
12+
- Improves badges
13+
- Improves code quality using `flake8` and `black`

README.md

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,55 @@
1-
[![Build status](https://dev.azure.com/robertoprevato/GuardPost/_apis/build/status/GuardPost-CI)](https://dev.azure.com/robertoprevato/GuardPost/_build/latest?definitionId=15) [![pypi](https://img.shields.io/pypi/v/GuardPost.svg?color=blue)](https://pypi.org/project/GuardPost/) [![Test coverage](https://img.shields.io/azure-devops/coverage/robertoprevato/GuardPost/15.svg)](https://robertoprevato.visualstudio.com/GuardPost/_build?definitionId=15)
1+
[![Build](https://github.com/Neoteroi/guardpost/workflows/Build/badge.svg)](https://github.com/Neoteroi/guardpost/actions?query=workflow%3ABuild)
2+
[![pypi](https://img.shields.io/pypi/v/guardpost.svg?color=blue)](https://pypi.org/project/guardpost/)
3+
[![versions](https://img.shields.io/pypi/pyversions/guardpost.svg)](https://github.com/Neoteroi/guardpost)
4+
[![license](https://img.shields.io/github/license/Neoteroi/guardpost.svg)](https://github.com/Neoteroi/guardpost/blob/master/LICENSE)
5+
[![codecov](https://codecov.io/gh/Neoteroi/guardpost/branch/master/graph/badge.svg?token=sBKZG2D1bZ)](https://codecov.io/gh/Neoteroi/guardpost)
26

37
# GuardPost
4-
GuardPost provides a basic framework to handle authentication and authorization in any kind of Python application.
8+
GuardPost provides a basic framework to handle authentication and authorization
9+
in any kind of Python application.
510

611
```bash
712
pip install guardpost
813
```
914

10-
This library is freely inspired by [authorization in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2); although its implementation is extremely different.
15+
This library is freely inspired by [authorization in ASP.NET
16+
Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2);
17+
although its implementation is extremely different.
1118

1219
Notable differences are:
13-
1. GuardPost is abstracted from the code that executes it, so it's not bound to the context of a web framework.
14-
1. GuardPost implements both classes for use with synchronous code (not necessarily I/O bound), and classes using `async/await` syntax (optimized for authentication and authorization rules that involve I/O bound operations such as web requests and communications with databases).
15-
1. GuardPost leverages Python function decorators for the authorization part, so any function can be wrapped to be executed after handling authorization.
20+
1. GuardPost is abstracted from the code that executes it, so it's not bound to
21+
the context of a web framework.
22+
1. GuardPost implements both classes for use with synchronous code (not
23+
necessarily I/O bound), and classes using `async/await` syntax (optimized
24+
for authentication and authorization rules that involve I/O bound operations
25+
such as web requests and communications with databases).
26+
1. GuardPost leverages Python function decorators for the authorization part,
27+
so any function can be wrapped to be executed after handling authorization.
1628
1. The code API is simpler.
1729

1830
## More documentation and examples
19-
For documentation and [examples](https://github.com/RobertoPrevato/GuardPost/wiki/Examples), refer to the project [Wiki](https://github.com/RobertoPrevato/GuardPost/wiki).
31+
For documentation and
32+
[examples](https://github.com/RobertoPrevato/GuardPost/wiki/Examples), refer to
33+
the project [Wiki](https://github.com/RobertoPrevato/GuardPost/wiki).
34+
35+
To see how `guardpost` is used in `blacksheep` web framework, read the
36+
documentation here:
37+
38+
* [Authentication](https://www.neoteroi.dev/blacksheep/authentication/)
39+
* [Authorization](https://www.neoteroi.dev/blacksheep/authorization/)
2040

2141
## Both for async/await and synchronous code
22-
GuardPost can be used both with async/await code and with synchronous code, according to use cases and users' preference.
42+
GuardPost can be used both with async/await code and with synchronous code,
43+
according to use cases and users' preference.
2344

2445
## If you have doubts about authentication vs authorization...
25-
`Authentication` answers the question: _Who is the user who is executing the action?_, or more in general: _Who is the user, or what is the service, that is executing the action?_.
46+
`Authentication` answers the question: _Who is the user who is executing the
47+
action?_, or more in general: _Who is the user, or what is the service, that is
48+
executing the action?_.
2649

27-
`Authorization` answers the question: _Is the user, or service, authorized to do something?_.
50+
`Authorization` answers the question: _Is the user, or service, authorized to
51+
do something?_.
2852

29-
Usually, to implement authorization, is necessary to have the context of the entity that is executing the action. Anyway, the two things are logically separated and GuardPost is designed to keep them separate.
53+
Usually, to implement authorization, is necessary to have the context of the
54+
entity that is executing the action. Anyway, the two things are logically
55+
separated and GuardPost is designed to keep them separate.

guardpost/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
from .authentication import User, Identity, ClaimValueType
2-
from .authorization import Policy, PolicyNotFoundError, AuthorizationError, UnauthorizedError, BaseRequirement
1+
from .authentication import User, Identity
2+
from .authorization import (
3+
Policy,
4+
PolicyNotFoundError,
5+
AuthorizationError,
6+
UnauthorizedError,
7+
BaseRequirement,
8+
)

guardpost/asynchronous/authentication.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
from abc import abstractmethod
22
from typing import Any, Optional, Sequence
3-
from guardpost.authentication import Identity, BaseAuthenticationHandler, BaseAuthenticationStrategy
3+
from guardpost.authentication import (
4+
Identity,
5+
BaseAuthenticationHandler,
6+
BaseAuthenticationStrategy,
7+
)
48

59

610
class AuthenticationHandler(BaseAuthenticationHandler):
7-
811
@abstractmethod
912
async def authenticate(self, context: Any) -> Optional[Identity]:
1013
"""Obtains an identity from a context."""
1114

1215

1316
class AuthenticationStrategy(BaseAuthenticationStrategy):
14-
15-
async def authenticate(self,
16-
context: Any,
17-
authentication_schemes: Optional[Sequence[str]] = None):
17+
async def authenticate(
18+
self, context: Any, authentication_schemes: Optional[Sequence[str]] = None
19+
):
1820
if not context:
19-
raise ValueError('Missing context to evaluate authentication')
21+
raise ValueError("Missing context to evaluate authentication")
2022

2123
for handler in self.get_handlers(authentication_schemes):
2224
identity = await handler.authenticate(context)

0 commit comments

Comments
 (0)