feat(auth): add DB-backed login, token management, and idempotent auth migration
This commit is contained in:
+41
-3
@@ -39,7 +39,15 @@ class FakeRepo:
|
||||
def list_users(self) -> list[User]:
|
||||
return sorted(self._users.values(), key=lambda user: user.username)
|
||||
|
||||
def create_user(self, username: str, *, email: str | None = None) -> User:
|
||||
def create_user(
|
||||
self,
|
||||
username: str,
|
||||
*,
|
||||
email: str | None = None,
|
||||
password_hash: str | None = None,
|
||||
permission: str = "read",
|
||||
is_active: bool = True,
|
||||
) -> User:
|
||||
if any(user.username == username for user in self._users.values()):
|
||||
raise ValueError(f"이미 존재하는 username입니다: {username}")
|
||||
if email is not None and any(user.email == email for user in self._users.values()):
|
||||
@@ -49,6 +57,9 @@ class FakeRepo:
|
||||
id=f"user-{self._user_seq}",
|
||||
username=username,
|
||||
email=email,
|
||||
permission=permission,
|
||||
is_active=is_active,
|
||||
password_hash=password_hash,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
@@ -56,7 +67,16 @@ class FakeRepo:
|
||||
self._users[user.id] = user
|
||||
return user
|
||||
|
||||
def update_user(self, user_id: str, *, username: str | None = None, email: str | None = None) -> User:
|
||||
def update_user(
|
||||
self,
|
||||
user_id: str,
|
||||
*,
|
||||
username: str | None = None,
|
||||
email: str | None = None,
|
||||
password_hash: str | None = None,
|
||||
permission: str | None = None,
|
||||
is_active: bool | None = None,
|
||||
) -> User:
|
||||
user = self.get_user(user_id)
|
||||
if username is not None and any(item.id != user_id and item.username == username for item in self._users.values()):
|
||||
raise ValueError(f"이미 존재하는 username입니다: {username}")
|
||||
@@ -66,6 +86,12 @@ class FakeRepo:
|
||||
user.username = username
|
||||
if email is not None:
|
||||
user.email = email
|
||||
if password_hash is not None:
|
||||
user.password_hash = password_hash
|
||||
if permission is not None:
|
||||
user.permission = permission
|
||||
if is_active is not None:
|
||||
user.is_active = is_active
|
||||
user.updated_at = datetime.now(timezone.utc)
|
||||
return user
|
||||
|
||||
@@ -459,7 +485,19 @@ def test_cli_user_create_supports_json() -> None:
|
||||
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
["--format", "json", "user", "create", "--username", "alice", "--email", "alice@example.com", "--force"],
|
||||
[
|
||||
"--format",
|
||||
"json",
|
||||
"user",
|
||||
"create",
|
||||
"--username",
|
||||
"alice",
|
||||
"--password",
|
||||
"password123",
|
||||
"--email",
|
||||
"alice@example.com",
|
||||
"--force",
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
Reference in New Issue
Block a user