mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-05 06:41:13 +02:00
Secure users API and handle manual password recovery.
This commit is contained in:
1 parent
c03226e368
commit
e28452f09a
7 files changed
+92
-39
No files matched your search
@@ -44,6 +44,10 @@ async def get_user_from_token(
|
||||
|
||||
async def get_current_active_user(current_user: schemas.User = Depends(get_user_from_token)) -> schemas.User:
|
||||
|
||||
# Super admin is always authorized
|
||||
if current_user.is_superadmin:
|
||||
return current_user
|
||||
|
||||
if not current_user.is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
|
||||
@@ -45,7 +45,10 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=List[schemas.User])
|
||||
async def get_users(users_repo: UsersRepository = Depends(get_repository(UsersRepository))) -> List[schemas.User]:
|
||||
async def get_users(
|
||||
users_repo: UsersRepository = Depends(get_repository(UsersRepository)),
|
||||
current_user: schemas.User = Depends(get_current_active_user)
|
||||
) -> List[schemas.User]:
|
||||
"""
|
||||
Get all users.
|
||||
"""
|
||||
@@ -55,7 +58,9 @@ async def get_users(users_repo: UsersRepository = Depends(get_repository(UsersRe
|
||||
|
||||
@router.post("", response_model=schemas.User, status_code=status.HTTP_201_CREATED)
|
||||
async def create_user(
|
||||
user_create: schemas.UserCreate, users_repo: UsersRepository = Depends(get_repository(UsersRepository))
|
||||
user_create: schemas.UserCreate,
|
||||
users_repo: UsersRepository = Depends(get_repository(UsersRepository)),
|
||||
current_user: schemas.User = Depends(get_current_active_user)
|
||||
) -> schemas.User:
|
||||
"""
|
||||
Create a new user.
|
||||
@@ -70,9 +75,11 @@ async def create_user(
|
||||
return await users_repo.create_user(user_create)
|
||||
|
||||
|
||||
@router.get("/{user_id}", response_model=schemas.User)
|
||||
@router.get("/{user_id}",response_model=schemas.User)
|
||||
async def get_user(
|
||||
user_id: UUID, users_repo: UsersRepository = Depends(get_repository(UsersRepository))
|
||||
user_id: UUID,
|
||||
users_repo: UsersRepository = Depends(get_repository(UsersRepository)),
|
||||
current_user: schemas.User = Depends(get_current_active_user)
|
||||
) -> schemas.User:
|
||||
"""
|
||||
Get an user.
|
||||
@@ -86,9 +93,10 @@ async def get_user(
|
||||
|
||||
@router.put("/{user_id}", response_model=schemas.User)
|
||||
async def update_user(
|
||||
user_id: UUID,
|
||||
user_update: schemas.UserUpdate,
|
||||
users_repo: UsersRepository = Depends(get_repository(UsersRepository)),
|
||||
user_id: UUID,
|
||||
user_update: schemas.UserUpdate,
|
||||
users_repo: UsersRepository = Depends(get_repository(UsersRepository)),
|
||||
current_user: schemas.User = Depends(get_current_active_user)
|
||||
) -> schemas.User:
|
||||
"""
|
||||
Update an user.
|
||||
@@ -111,7 +119,7 @@ async def delete_user(
|
||||
"""
|
||||
|
||||
if current_user.is_superadmin:
|
||||
raise ControllerForbiddenError("The super user cannot be deleted")
|
||||
raise ControllerForbiddenError("The super admin cannot be deleted")
|
||||
|
||||
success = await users_repo.delete_user(user_id)
|
||||
if not success:
|
||||
|
||||
@@ -37,6 +37,7 @@ class User(BaseTable):
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_superadmin = Column(Boolean, default=False)
|
||||
|
||||
|
||||
@event.listens_for(User.__table__, 'after_create')
|
||||
def create_default_super_admin(target, connection, **kw):
|
||||
|
||||
@@ -49,4 +50,4 @@ def create_default_super_admin(target, connection, **kw):
|
||||
)
|
||||
connection.execute(stmt)
|
||||
connection.commit()
|
||||
log.info("Default super admin account added")
|
||||
log.info("The default super admin account has been created in the database")
|
||||
@@ -26,6 +26,10 @@ import gns3server.db.models as models
|
||||
from gns3server import schemas
|
||||
from gns3server.services import auth_service
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UsersRepository(BaseRepository):
|
||||
def __init__(self, db_session: AsyncSession) -> None:
|
||||
@@ -93,6 +97,13 @@ class UsersRepository(BaseRepository):
|
||||
user = await self.get_user_by_username(username)
|
||||
if not user:
|
||||
return None
|
||||
# Allow user to be authenticated if hashed password in the db is null
|
||||
# this is useful for manual password recovery like:
|
||||
# sqlite3 gns3_controller.db "UPDATE users SET hashed_password = null WHERE username = 'admin';"
|
||||
if user.hashed_password is None:
|
||||
log.warning(f"User '{username}' has been authenticated without a password "
|
||||
f"configured. Please set a new password.")
|
||||
return user
|
||||
if not self._auth_service.verify_password(password, user.hashed_password):
|
||||
return None
|
||||
return user
|
||||
@@ -37,7 +37,7 @@ class UserCreate(UserBase):
|
||||
"""
|
||||
|
||||
username: str = Field(..., min_length=3, regex="[a-zA-Z0-9_-]+$")
|
||||
password: SecretStr = Field(..., min_length=7, max_length=100)
|
||||
password: SecretStr = Field(..., min_length=6, max_length=100)
|
||||
|
||||
|
||||
class UserUpdate(UserBase):
|
||||
@@ -45,7 +45,7 @@ class UserUpdate(UserBase):
|
||||
Properties to update an user.
|
||||
"""
|
||||
|
||||
password: Optional[SecretStr] = Field(None, min_length=7, max_length=100)
|
||||
password: Optional[SecretStr] = Field(None, min_length=6, max_length=100)
|
||||
|
||||
|
||||
class User(DateTimeModelMixin, UserBase):
|
||||
|
||||
Reference in new issue
Block a user