Skip to content

Commit 5eb781a

Browse files
authored
Merge pull request #111 from eadwinCode/auth_doc
Linting, MyPy Upgrade and Example fixes
2 parents 7f4ecbe + af9c0fe commit 5eb781a

File tree

259 files changed

+1720
-1427
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

259 files changed

+1720
-1427
lines changed

.github/workflows/test_full.yml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Install Dependencies
2424
run: flit install --symlink
2525
- name: Test
26-
run: pytest tests
26+
run: pytest
2727

2828
codestyle:
2929
runs-on: ubuntu-latest
@@ -38,10 +38,8 @@ jobs:
3838
- name: Install Dependencies
3939
run: flit install --symlink
4040
- name: Black
41-
run: black --check ellar tests
42-
- name: isort
43-
run: isort --check ellar tests
44-
- name: Flake8
45-
run: flake8 ellar tests
41+
run: black --check ellar tests examples
42+
- name: Ruff Linting Check
43+
run: ruff check ellar tests examples
4644
- name: mypy
4745
run: mypy ellar

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,4 @@ dist
126126
test.py
127127

128128
docs/site
129-
site/
129+
site/

Makefile

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,19 @@ install-full: ## Install dependencies
1818
pre-commit install -f
1919

2020
lint: ## Run code linters
21-
black --check ellar tests
22-
isort --check ellar tests
23-
autoflake --remove-unused-variables --remove-unused-variables -r ellar tests
24-
flake8 ellar tests
21+
black --check ellar tests examples
22+
ruff check ellar tests examples
2523
mypy ellar
2624

2725
fmt format: ## Run code formatters
28-
black ellar tests
29-
isort ellar tests
26+
black ellar tests examples
27+
ruff check --fix ellar tests examples
3028

3129
test: ## Run tests
32-
pytest tests
30+
pytest
3331

3432
test-cov: ## Run tests with coverage
35-
pytest --cov=ellar --cov-report term-missing tests
33+
pytest --cov=ellar --cov-report term-missing
3634

3735
doc-deploy: ## Run Deploy Documentation
3836
make clean

docs/overview/middleware.md

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,7 @@ Actions that can be performed by middleware functions:
3939
- End the request-response cycle if need be
4040
- Each middleware class or function must call `app` or `call_next` respectively else the request will be left without response
4141

42-
## **Dependency Injection**
43-
This is still feature is still in progress
44-
45-
```python
46-
import typing as t
47-
from starlette.types import ASGIApp
48-
from ellar.di import injectable
49-
50-
51-
@injectable
52-
class MyCustomService:
53-
pass
54-
5542

56-
class EllarASGIMiddlewareStructure:
57-
def __init__(self, app: ASGIApp, service: MyCustomService, **other_options: t.Any):
58-
self.app = app
59-
self.options = other_options
60-
self.custom_service = service
61-
62-
```
6343
## **Application Middleware**
6444
Ellar applies some ASGI middleware necessary for resource protection, error handling, and context management.
6545
They include:
@@ -69,9 +49,11 @@ They include:
6949
- **`RequestServiceProviderMiddleware`**: - This inherits from `ServerErrorMiddleware`. It provides DI context during request and
7050
also ensures that application exceptions may return a custom 500 page, or display an application traceback in DEBUG mode.
7151
- **`RequestVersioningMiddleware`**: This computes resource versioning info from request object based on configured resource versioning scheme at the application level.
72-
- **`ExceptionMiddleware`**: - Adds exception handlers, so that particular types of expected exception cases can be associated with handler functions. For example raising `HTTPException(status_code=404)` within an endpoint will end up rendering a custom 404 page.
52+
- **`ExceptionMiddleware`**: - Adds exception handlers, so that some common exception raised with the application can be associated with handler functions. For example raising `HTTPException(status_code=404)` within an endpoint will end up rendering a custom 404 page.
53+
- **`SessionMiddleware`**: controls session state using the session strategy configured in the application.
54+
- **`IdentityMiddleware`**: controls all registered authentication schemes and provides user identity to all request
7355

74-
## Applying Middleware
56+
## **Applying Middleware**
7557
Middleware can be applied through the application `config` - `MIDDLEWARES` variable.
7658

7759
Let's apply some middleware in our previous project. At the project root level, open `config.py`.
@@ -95,6 +77,36 @@ class DevelopmentConfig(BaseConfig):
9577
!!! Hint
9678
This is how to apply any `ASGI` middlewares such as `GZipMiddleware`, `EllarASGIMiddlewareStructure`, and others available in the `Starlette` library.
9779

80+
## **Dependency Injection**
81+
In section above, we saw how middleware are registered to the application. But what if the middleware class depends on other services, how then should we configure it?
82+
The `Middleware` does all the work.
83+
84+
For example, lets modify the `GZipMiddleware` class and make it depend on `Config` service.
85+
```python
86+
from ellar.core import Config
87+
from ellar.core.middleware import GZipMiddleware, Middleware
88+
from ellar.common.types import ASGIApp
89+
90+
91+
class CustomGZipMiddleware(GZipMiddleware):
92+
def __init__(self, app: ASGIApp, config: Config, minimum_size: int = 500, compresslevel: int = 9):
93+
super().__init__(app, minimum_size, compresslevel)
94+
self._config = config
95+
96+
## And in Config.py
97+
...
98+
99+
class DevelopmentConfig(BaseConfig):
100+
DEBUG: bool = True
101+
# Application middlewares
102+
MIDDLEWARE: list[Middleware] = [
103+
Middleware(CustomGZipMiddleware, minimum_size=1000)
104+
]
105+
```
106+
107+
In the example above, `Middleware` that wraps `CustomGZipMiddleware` with ensure dependent classes in `CustomGZipMiddleware` are resolved when
108+
instantiating `CustomGZipMiddleware` object. As you see, the `config` value was not provided but will be injected during runtime.
109+
98110
## **Starlette Middlewares**
99111
Let's explore other Starlette middlewares and other third party `ASGI` Middlewares
100112

docs/security/csrf.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,31 @@
1-
# Coming Soon
1+
# **CSRF or XSRF**
2+
CSRF or XSRF is a security vulnerability and attack method in web applications. It involves tricking a user's browser
3+
into sending unauthorized requests to a website where the user is authenticated, allowing attackers to perform actions on behalf of the user.
4+
5+
## **Available ASGI CSRF Middlewares**
6+
7+
- [Piccolo CSRF Middleware](https://piccolo-api.readthedocs.io/en/latest/csrf/usage.html)
8+
- [Starlette CSRF](https://pypi.org/project/starlette-csrf/)
9+
10+
These middlewares can be configured as every other asgi middleware as shown in middleware [docs](../../overview/middleware/#applying-middleware) to work in Ellar
11+
12+
For example, using [Starlette CSRF](https://pypi.org/project/starlette-csrf/) Middleware
13+
```python
14+
# config.py
15+
import typing as t
16+
from ellar.core.middleware import Middleware
17+
from starlette_csrf import CSRFMiddleware
18+
19+
class Development(BaseConfig):
20+
DEBUG: bool = True
21+
# Application middlewares
22+
MIDDLEWARE: t.Sequence[Middleware] = [
23+
Middleware(
24+
CSRFMiddleware,
25+
secret="__CHANGE_ME__",
26+
cookie_name='csrftoken',
27+
safe_methods={"GET", "HEAD", "OPTIONS", "TRACE"}
28+
)
29+
]
30+
31+
```

ellar/auth/guard.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import typing as t
22
from functools import partial
33

4-
from starlette import status
5-
64
from ellar.common import APIException, GuardCanActivate, IExecutionContext
75
from ellar.di import injectable
6+
from starlette import status
87

98
from .constants import POLICY_KEYS
109
from .policy import BasePolicyHandler, PolicyType

ellar/auth/handlers/schemes/base.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import typing as t
22
from abc import ABC, abstractmethod
33

4-
from starlette.exceptions import HTTPException
5-
from starlette.status import HTTP_401_UNAUTHORIZED
6-
74
from ellar.common.exceptions import APIException
85
from ellar.common.interfaces import IHostContext
96
from ellar.common.serializer.guard import (
107
HTTPAuthorizationCredentials,
118
HTTPBasicCredentials,
129
)
10+
from starlette.exceptions import HTTPException
11+
from starlette.status import HTTP_401_UNAUTHORIZED
1312

1413
if t.TYPE_CHECKING: # pragma: no cover
1514
from ellar.core.connection import HTTPConnection

ellar/auth/handlers/schemes/http.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def _get_credentials(self, connection: "HTTPConnection") -> HTTPBasicCredentials
9191

9292
if not separator:
9393
self._not_unauthorized_exception("Invalid authentication credentials")
94-
return HTTPBasicCredentials(username=username, password=password)
94+
return HTTPBasicCredentials(username=username, password=password) # type: ignore[arg-type]
9595

9696

9797
class HttpDigestAuth(HttpBearerAuth, ABC):

ellar/auth/services/auth_schemes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ def add_authentication(
2020
def find_authentication_scheme(self, scheme: str) -> AuthenticationHandlerType:
2121
try:
2222
return self._authentication_schemes[scheme]
23-
except KeyError:
23+
except KeyError as ex:
2424
raise RuntimeError(
2525
f'No Authentication Scheme found with the name:"{scheme}"'
26-
)
26+
) from ex
2727

2828
def get_authentication_schemes(
2929
self,

ellar/auth/session/strategy.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
try:
22
import itsdangerous
3-
except Exception: # pragma: no cover
3+
except Exception as ex: # pragma: no cover
44
raise RuntimeError(
55
"SessionClientStrategy requires itsdangerous package installed. Run `pip install itsdangerous`"
6-
)
6+
) from ex
77

88
import json
99
import typing as t
1010
from base64 import b64decode, b64encode
1111

12-
import itsdangerous
13-
from itsdangerous import BadSignature
14-
1512
from ellar.core import Config
1613
from ellar.di import injectable
14+
from itsdangerous import BadSignature
1715

1816
from .cookie_dict import SessionCookieObject
1917
from .interface import ISessionStrategy
@@ -26,12 +24,12 @@ def __init__(self, config: Config) -> None:
2624
self._signer = itsdangerous.TimestampSigner(str(config.SECRET_KEY))
2725
self.config = config
2826
self._session_config = SessionCookieOption(
29-
NAME=config.SESSION_COOKIE_NAME,
27+
NAME=config.SESSION_COOKIE_NAME or "",
3028
DOMAIN=config.SESSION_COOKIE_DOMAIN,
3129
PATH=config.SESSION_COOKIE_PATH or "/",
32-
HTTPONLY=config.SESSION_COOKIE_HTTPONLY,
33-
SECURE=config.SESSION_COOKIE_SECURE,
34-
SAME_SITE=config.SESSION_COOKIE_SAME_SITE,
30+
HTTPONLY=config.SESSION_COOKIE_HTTPONLY or False,
31+
SECURE=config.SESSION_COOKIE_SECURE or False,
32+
SAME_SITE=config.SESSION_COOKIE_SAME_SITE or "none",
3533
MAX_AGE=config.SESSION_COOKIE_MAX_AGE,
3634
)
3735

0 commit comments

Comments
 (0)