api.auth.user: POST with hashing of input password
This commit is contained in:
parent
8494f3737d
commit
b6e6f1c833
5 changed files with 29 additions and 8 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
# scopes.api.auth user
|
# scopes.api.auth user
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from fastapi import APIRouter, Body
|
from fastapi import APIRouter, Body
|
||||||
import jwt
|
import jwt
|
||||||
from pwdlib import PasswordHash
|
from pwdlib import PasswordHash
|
||||||
|
|
@ -13,6 +14,7 @@ from scopes.storage.common import Storage
|
||||||
|
|
||||||
class User(BaseModel):
|
class User(BaseModel):
|
||||||
uid: str | None = None
|
uid: str | None = None
|
||||||
|
timeStamp: datetime | None = None
|
||||||
name: str
|
name: str
|
||||||
login: str
|
login: str
|
||||||
display_name: str
|
display_name: str
|
||||||
|
|
@ -20,6 +22,9 @@ class User(BaseModel):
|
||||||
pw_hash: str | None = Field(exclude=True, default=None)
|
pw_hash: str | None = Field(exclude=True, default=None)
|
||||||
groups: List[str] = Field(default_factory=list)
|
groups: List[str] = Field(default_factory=list)
|
||||||
|
|
||||||
|
class UserIn(User):
|
||||||
|
password: str | None = None
|
||||||
|
|
||||||
# password handling, authentication
|
# password handling, authentication
|
||||||
|
|
||||||
pw_hash = PasswordHash.recommended()
|
pw_hash = PasswordHash.recommended()
|
||||||
|
|
@ -52,7 +57,7 @@ async def get_users(storage: deps.Storage):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@router.post('/users', response_model=User)
|
@router.post('/users', response_model=User)
|
||||||
async def post_user(user: Annotated[User, Body()], storage: deps.Storage):
|
async def post_user(user: Annotated[UserIn, Body()], storage: deps.Storage):
|
||||||
users = storage.create(st_user.Users)
|
users = storage.create(st_user.Users)
|
||||||
tr = st_user.User.fromObject(user)
|
tr = st_user.User.fromObject(user)
|
||||||
users.save(tr)
|
users.save(tr)
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,14 @@ class Record:
|
||||||
def toObject(self, cls):
|
def toObject(self, cls):
|
||||||
return cls(**self.asDict(flatten=True))
|
return cls(**self.asDict(flatten=True))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def prepareData(data):
|
||||||
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fromObject(cls, obj):
|
def fromObject(cls, obj):
|
||||||
inp = obj.model_dump()
|
inp = obj.model_dump()
|
||||||
headValues = [inp.pop(hf) for hf in cls.headFields]
|
headValues = [inp.pop(hf) for hf in cls.headFields]
|
||||||
return cls(*headValues, data=inp)
|
data = cls.prepareData(inp)
|
||||||
|
return cls(*headValues, data=data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,11 @@ class Record(shape.Record):
|
||||||
return base | self.head | self.data
|
return base | self.head | self.data
|
||||||
return base | super().asDict()
|
return base | super().asDict()
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def fromObject(cls, obj):
|
def prepareData(data):
|
||||||
tr = super().fromObject(obj)
|
data.pop('uid', None)
|
||||||
tr.data.pop('uid', None)
|
data.pop('timeStamp', None)
|
||||||
return tr
|
return data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def uid(self):
|
def uid(self):
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,15 @@ class User(concept.Concept):
|
||||||
headFields = ('name', 'login')
|
headFields = ('name', 'login')
|
||||||
prefix = 'user'
|
prefix = 'user'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def prepareData(data):
|
||||||
|
from scopes.api.auth.user import hashPassword
|
||||||
|
data = concept.Concept.prepareData(data)
|
||||||
|
pw = data.pop('password', None)
|
||||||
|
if pw:
|
||||||
|
data['pw_hash'] = hashPassword(pw)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
@registerContainerClass
|
@registerContainerClass
|
||||||
class Users(concept.Concepts):
|
class Users(concept.Concepts):
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,10 @@ def test_user(storage, caplog):
|
||||||
u01i = users[0]
|
u01i = users[0]
|
||||||
assert u01i.get(pw_hash) is None
|
assert u01i.get(pw_hash) is None
|
||||||
resp = clt.post('/users', json={'name': 'jane', 'login': 'jane',
|
resp = clt.post('/users', json={'name': 'jane', 'login': 'jane',
|
||||||
'display_name': 'Jane', 'email': 'jane@xmpl.org'})
|
'display_name': 'Jane', 'email': 'jane@xmpl.org',
|
||||||
|
'password': 'secret'})
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
u02 = resp.json()
|
u02 = resp.json()
|
||||||
assert u02['uid'] == 'user-2'
|
assert u02['uid'] == 'user-2'
|
||||||
|
assert u02.get('timeStamp') is not None
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue