Skip to content

Commit 6e08b27

Browse files
authored
Merge pull request #116 from eadwinCode/auth_example
Added auth example
2 parents ab4ac0f + e2749e3 commit 6e08b27

File tree

24 files changed

+1358
-1
lines changed

24 files changed

+1358
-1
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Auth With Guards
2+
Example of ellar authentication and authorization with guards
3+
4+
## Requirements
5+
Python >= 3.7
6+
Poetry
7+
8+
## Project setup
9+
```
10+
poetry install
11+
```
12+
13+
### Development Server
14+
```
15+
ellar runserver --reload
16+
```

examples/03-auth-with-guards/auth_project/__init__.py

Whitespace-only changes.

examples/03-auth-with-guards/auth_project/auth/__init__.py

Whitespace-only changes.

examples/03-auth-with-guards/auth_project/auth/constants.py

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Define endpoints routes in python class-based fashion
3+
example:
4+
5+
@Controller("/dogs", tag="Dogs", description="Dogs Resources")
6+
class MyController(ControllerBase):
7+
@get('/')
8+
def index(self):
9+
return {'detail': "Welcome Dog's Resources"}
10+
"""
11+
from ellar.common import Body, Controller, ControllerBase, UseGuards, get, post
12+
13+
from .guards import AllowAnyGuard
14+
from .schemas import UserCredentials
15+
from .services import AuthService
16+
17+
18+
@Controller("/auth")
19+
class AuthController(ControllerBase):
20+
def __init__(self, auth_service: AuthService) -> None:
21+
self.auth_service = auth_service
22+
23+
@post("/sign-in")
24+
@UseGuards(AllowAnyGuard)
25+
async def sign_in(self, payload: UserCredentials = Body()):
26+
return await self.auth_service.sign_in(payload)
27+
28+
@get("/profile")
29+
def get_profile(self):
30+
return self.context.user
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import typing as t
2+
3+
from ellar.auth import UserIdentity
4+
from ellar.common import GuardCanActivate, IExecutionContext
5+
from ellar.common.serializer.guard import (
6+
HTTPAuthorizationCredentials,
7+
HTTPBasicCredentials,
8+
)
9+
from ellar.core.guards import GuardHttpBearerAuth
10+
from ellar.di import injectable
11+
from ellar_jwt import JWTService
12+
13+
if t.TYPE_CHECKING:
14+
from ellar.core import HTTPConnection
15+
16+
17+
@injectable
18+
class AuthGuard(GuardHttpBearerAuth):
19+
def __init__(self, jwt_service: JWTService) -> None:
20+
self.jwt_service = jwt_service
21+
22+
async def authentication_handler(
23+
self,
24+
connection: "HTTPConnection",
25+
credentials: t.Union[HTTPBasicCredentials, HTTPAuthorizationCredentials],
26+
) -> t.Optional[t.Any]:
27+
try:
28+
data = await self.jwt_service.decode_async(credentials.credentials)
29+
return UserIdentity(auth_type="bearer", **dict(data))
30+
except Exception:
31+
self.raise_exception()
32+
33+
34+
@injectable
35+
class AllowAnyGuard(GuardCanActivate):
36+
async def can_activate(self, context: IExecutionContext) -> bool:
37+
return True
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
@Module(
3+
controllers=[MyController],
4+
providers=[
5+
YourService,
6+
ProviderConfig(IService, use_class=AService),
7+
ProviderConfig(IFoo, use_value=FooService()),
8+
],
9+
routers=(routerA, routerB)
10+
statics='statics',
11+
template='template_folder',
12+
# base_directory -> default is the `auth` folder
13+
)
14+
class MyModule(ModuleBase):
15+
def register_providers(self, container: Container) -> None:
16+
# for more complicated provider registrations
17+
pass
18+
19+
"""
20+
from ellar.common import Module
21+
from ellar.core import ModuleBase
22+
from ellar.di import Container
23+
24+
from .controllers import AuthController
25+
from .services import AuthService
26+
27+
28+
@Module(
29+
controllers=[AuthController],
30+
providers=[AuthService],
31+
routers=[],
32+
)
33+
class AuthModule(ModuleBase):
34+
"""
35+
Auth Module
36+
"""
37+
38+
def register_providers(self, container: Container) -> None:
39+
"""for more complicated provider registrations, use container.register_instance(...)"""
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
Define Serializers/DTOs
3+
Example:
4+
5+
class ASampleDTO(Serializer):
6+
name: str
7+
age: t.Optional[int] = None
8+
9+
for dataclasses, Inherit from DataclassSerializer
10+
11+
@dataclass
12+
class ASampleDTO(DataclassSerializer):
13+
name: str
14+
age: t.Optional[int] = None
15+
"""
16+
from dataclasses import dataclass
17+
18+
from ellar.common import DataclassSerializer
19+
20+
21+
@dataclass
22+
class UserCredentials(DataclassSerializer):
23+
username: str
24+
password: str
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Create a provider and declare its scope
3+
4+
@injectable
5+
class AProvider
6+
pass
7+
8+
@injectable(scope=transient_scope)
9+
class BProvider
10+
pass
11+
"""
12+
import typing as t
13+
14+
from ellar.di import injectable
15+
from ellar_jwt import JWTService
16+
from starlette import status
17+
from starlette.exceptions import HTTPException
18+
19+
from ..users.services import UserService
20+
from .schemas import UserCredentials
21+
22+
23+
@injectable
24+
class AuthService:
25+
def __init__(self, user_service: UserService, jwt_service: JWTService):
26+
self.user_service = user_service
27+
self.jwt_service = jwt_service
28+
29+
async def sign_in(self, credentials: UserCredentials) -> t.Dict:
30+
user = await self.user_service.find_one(credentials.username)
31+
if user.password != credentials.password:
32+
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
33+
34+
return {"access_token": await self.jwt_service.sign_async(user.dict())}

examples/03-auth-with-guards/auth_project/auth/tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)