api.auth: HTTP Basic Auth working
This commit is contained in:
parent
8963c2ac87
commit
342f48042c
4 changed files with 31 additions and 14 deletions
|
|
@ -1,14 +1,15 @@
|
||||||
# scopes.api.auth user
|
# scopes.api.auth user
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from fastapi import APIRouter, Body
|
from fastapi import APIRouter, Body, Depends, HTTPException
|
||||||
|
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||||
import jwt
|
import jwt
|
||||||
from pwdlib import PasswordHash
|
from pwdlib import PasswordHash
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing import Annotated, List
|
from typing import Annotated, List
|
||||||
|
|
||||||
from scopes.api import deps
|
from scopes.api import deps
|
||||||
from scopes.config import settings
|
from scopes.api.auth import config
|
||||||
from scopes.storage import user as st_user
|
from scopes.storage import user as st_user
|
||||||
from scopes.storage.common import Storage
|
from scopes.storage.common import Storage
|
||||||
|
|
||||||
|
|
@ -33,6 +34,10 @@ class UserEdit(UserBase):
|
||||||
|
|
||||||
# password handling, authentication
|
# password handling, authentication
|
||||||
|
|
||||||
|
def dummy_auth() -> User:
|
||||||
|
return User(name='tester', login='tester', display_name='Tester')
|
||||||
|
config.auth_methods['testing'] = dummy_auth
|
||||||
|
|
||||||
pw_hash = PasswordHash.recommended()
|
pw_hash = PasswordHash.recommended()
|
||||||
|
|
||||||
def hashPassword(pw):
|
def hashPassword(pw):
|
||||||
|
|
@ -41,11 +46,27 @@ def hashPassword(pw):
|
||||||
def verifyPassword(plain, hashed):
|
def verifyPassword(plain, hashed):
|
||||||
return pw_hash.verify(plain, hashed)
|
return pw_hash.verify(plain, hashed)
|
||||||
|
|
||||||
def userFromToken(users: deps.Users, token: deps.AuthToken):
|
security = HTTPBasic()
|
||||||
payload = jwt.decode(token, settings.jwt_key, algorithms=[settings.jwt_algorithm])
|
|
||||||
|
type Credentials = Annotated[HTTPBasicCredentials, Depends(security)]
|
||||||
|
|
||||||
|
def basicAuth(users: deps.Users, cred: Credentials) -> User:
|
||||||
|
tr = users.queryLast(login=cred.username)
|
||||||
|
users.storage.commit()
|
||||||
|
user = tr.toObject(User)
|
||||||
|
if verifyPassword(cred.password, user.pw_hash):
|
||||||
|
return user
|
||||||
|
raise HTTPException(status_code=401, detail='credentials',
|
||||||
|
headers={"WWW-Authenticate": "Basic"})
|
||||||
|
config.auth_methods['basic'] = basicAuth
|
||||||
|
|
||||||
|
def userFromToken(users: deps.Users, token: deps.AuthToken) -> User:
|
||||||
|
payload = jwt.decode(token, config.settings.jwt_key,
|
||||||
|
algorithms=[config.settings.jwt_algorithm])
|
||||||
userName = payload.get('sub')
|
userName = payload.get('sub')
|
||||||
user = users.queryLast(name=userName)
|
tr = users.queryLast(name=userName)
|
||||||
return user
|
users.storage.commit()
|
||||||
|
return tr.toObject(User)
|
||||||
|
|
||||||
# routes
|
# routes
|
||||||
|
|
||||||
|
|
@ -59,8 +80,8 @@ async def get_users(users: deps.Users):
|
||||||
@router.post('/users', response_model=User)
|
@router.post('/users', response_model=User)
|
||||||
async def post_user(user: Annotated[UserEdit, Body()], users: deps.Users,
|
async def post_user(user: Annotated[UserEdit, Body()], users: deps.Users,
|
||||||
auth_user: deps.AuthUser):
|
auth_user: deps.AuthUser):
|
||||||
print('*** post_user', auth_user)
|
|
||||||
tr = st_user.User.fromObject(user)
|
tr = st_user.User.fromObject(user)
|
||||||
users.save(tr)
|
users.save(tr)
|
||||||
|
#print('*** post_user', auth_user, tr)
|
||||||
return tr.toObject(User)
|
return tr.toObject(User)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from scopes.storage import user as st_user
|
||||||
type Storage = Annotated[common.Storage, Depends(storage)]
|
type Storage = Annotated[common.Storage, Depends(storage)]
|
||||||
type Users = Annotated[st_user.Users, Depends(users)]
|
type Users = Annotated[st_user.Users, Depends(users)]
|
||||||
|
|
||||||
type AuthUser = Annotated[str, Depends(auth_user)]
|
type AuthUser = Annotated[str, Depends(auth_user())]
|
||||||
type AuthToken = Annotated[str, Depends(auth_token)]
|
type AuthToken = Annotated[str, Depends(auth_token)]
|
||||||
|
|
||||||
# storage
|
# storage
|
||||||
|
|
@ -30,7 +30,7 @@ def users(storage: Storage):
|
||||||
def auth_user():
|
def auth_user():
|
||||||
auth = auth_config.auth_methods.get(auth_config.settings.auth_method)
|
auth = auth_config.auth_methods.get(auth_config.settings.auth_method)
|
||||||
if auth is not None:
|
if auth is not None:
|
||||||
return auth()
|
return auth
|
||||||
|
|
||||||
def auth_token():
|
def auth_token():
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ DBUSER=testuser
|
||||||
DBPASSWORD=secret
|
DBPASSWORD=secret
|
||||||
DBSCHEMA=testing
|
DBSCHEMA=testing
|
||||||
|
|
||||||
AUTH_METHOD=testing
|
AUTH_METHOD=basic
|
||||||
|
|
||||||
# openssl rand -hex 32:
|
# openssl rand -hex 32:
|
||||||
JWT_KEY=391a521ee5ba2b6ce67b8fdbb9f0ca88f40a87d3bc1e82974381b647bb0fc776
|
JWT_KEY=391a521ee5ba2b6ce67b8fdbb9f0ca88f40a87d3bc1e82974381b647bb0fc776
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,6 @@ def storage():
|
||||||
storage.commit()
|
storage.commit()
|
||||||
return storage
|
return storage
|
||||||
|
|
||||||
def dummy_auth():
|
|
||||||
return api_user.User(name='tester', login='tester', display_name='Tester')
|
|
||||||
auth_config.auth_methods['testing'] = dummy_auth
|
|
||||||
|
|
||||||
def async_client():
|
def async_client():
|
||||||
return AsyncClient(transport=ASGITransport(app=app), base_url='http://test')
|
return AsyncClient(transport=ASGITransport(app=app), base_url='http://test')
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue