Merge pull request #799 from christianlouis/copilot/fix-greyed-out-toggles
fix(admin): fix greyed-out QR login toggle on /admin/connections
This commit is contained in:
@@ -31,6 +31,7 @@ from pydantic import BaseModel, Field
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.auth import require_login
|
from app.auth import require_login
|
||||||
|
from app.config import settings
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
from app.middleware.audit_log import get_client_ip
|
from app.middleware.audit_log import get_client_ip
|
||||||
from app.utils.session_manager import (
|
from app.utils.session_manager import (
|
||||||
@@ -151,6 +152,11 @@ async def create_challenge(
|
|||||||
displayed to the user. The mobile app scans this QR code and
|
displayed to the user. The mobile app scans this QR code and
|
||||||
calls the ``/claim`` endpoint.
|
calls the ``/claim`` endpoint.
|
||||||
"""
|
"""
|
||||||
|
if not settings.qr_login_enabled:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="QR login feature is currently disabled. Please contact your administrator to enable it.",
|
||||||
|
)
|
||||||
ip = get_client_ip(request)
|
ip = get_client_ip(request)
|
||||||
challenge = create_qr_challenge(db, owner_id, ip_address=ip)
|
challenge = create_qr_challenge(db, owner_id, ip_address=ip)
|
||||||
|
|
||||||
@@ -187,6 +193,11 @@ async def poll_challenge_status(
|
|||||||
The web UI calls this endpoint every few seconds to check if the
|
The web UI calls this endpoint every few seconds to check if the
|
||||||
mobile app has scanned the QR code and claimed the challenge.
|
mobile app has scanned the QR code and claimed the challenge.
|
||||||
"""
|
"""
|
||||||
|
if not settings.qr_login_enabled:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="QR login feature is currently disabled. Please contact your administrator to enable it.",
|
||||||
|
)
|
||||||
result = get_challenge_status(db, challenge_id, owner_id)
|
result = get_challenge_status(db, challenge_id, owner_id)
|
||||||
if not result:
|
if not result:
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Challenge not found")
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Challenge not found")
|
||||||
@@ -206,6 +217,11 @@ async def claim_challenge(
|
|||||||
serves as proof that the user authorized this login from their web
|
serves as proof that the user authorized this login from their web
|
||||||
session.
|
session.
|
||||||
"""
|
"""
|
||||||
|
if not settings.qr_login_enabled:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="QR login feature is currently disabled. Please contact your administrator to enable it.",
|
||||||
|
)
|
||||||
ip = get_client_ip(request)
|
ip = get_client_ip(request)
|
||||||
result = claim_qr_challenge(db, body.challenge_token, device_name=body.device_name, ip_address=ip)
|
result = claim_qr_challenge(db, body.challenge_token, device_name=body.device_name, ip_address=ip)
|
||||||
|
|
||||||
|
|||||||
@@ -244,6 +244,10 @@ class Settings(BaseSettings):
|
|||||||
"Useful for admin-configured non-standard durations."
|
"Useful for admin-configured non-standard durations."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
qr_login_enabled: bool = Field(
|
||||||
|
default=True,
|
||||||
|
description="Enable QR code-based login for mobile device authentication (default: True).",
|
||||||
|
)
|
||||||
qr_login_challenge_ttl_seconds: int = Field(
|
qr_login_challenge_ttl_seconds: int = Field(
|
||||||
default=120,
|
default=120,
|
||||||
description="Time-to-live in seconds for QR login challenges (default: 2 minutes).",
|
description="Time-to-live in seconds for QR login challenges (default: 2 minutes).",
|
||||||
|
|||||||
@@ -206,6 +206,14 @@ SETTING_METADATA = {
|
|||||||
"required": False,
|
"required": False,
|
||||||
"restart_required": True,
|
"restart_required": True,
|
||||||
},
|
},
|
||||||
|
"qr_login_enabled": {
|
||||||
|
"category": "Authentication",
|
||||||
|
"description": "Enable QR code-based login for mobile device authentication.",
|
||||||
|
"type": "boolean",
|
||||||
|
"sensitive": False,
|
||||||
|
"required": False,
|
||||||
|
"restart_required": False,
|
||||||
|
},
|
||||||
"qr_login_challenge_ttl_seconds": {
|
"qr_login_challenge_ttl_seconds": {
|
||||||
"category": "Authentication",
|
"category": "Authentication",
|
||||||
"description": "Time-to-live in seconds for QR login challenges (default 120).",
|
"description": "Time-to-live in seconds for QR login challenges (default 120).",
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ async def connections_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
|
|
||||||
# Feature toggles
|
# Feature toggles
|
||||||
sso_auto_login = _is_truthy(_get_effective("sso_auto_login"))
|
sso_auto_login = _is_truthy(_get_effective("sso_auto_login"))
|
||||||
qr_login_enabled = _is_truthy(_get_effective("qr_login_challenge_ttl_seconds"))
|
qr_login_enabled = _is_truthy(_get_effective("qr_login_enabled"))
|
||||||
frontend_url_configured = bool(_get_effective("public_base_url"))
|
frontend_url_configured = bool(_get_effective("public_base_url"))
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
|
|||||||
@@ -46,7 +46,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<label class="relative inline-flex items-center cursor-pointer">
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
<input type="checkbox" id="qr-upload-toggle" class="sr-only peer"
|
<input type="checkbox" id="qr-upload-toggle" class="sr-only peer"
|
||||||
{% if qr_login_enabled %}checked{% endif %} disabled>
|
{% if qr_login_enabled %}checked{% endif %}
|
||||||
|
onchange="toggleSetting('qr_login_enabled', this.checked)">
|
||||||
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-indigo-500 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600" style="min-width:44px; min-height:24px;"></div>
|
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-indigo-500 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600" style="min-width:44px; min-height:24px;"></div>
|
||||||
<span class="ml-3 text-sm font-medium text-gray-700 dark:text-gray-300">{{ _("connections.qr_code_enabled") }}</span>
|
<span class="ml-3 text-sm font-medium text-gray-700 dark:text-gray-300">{{ _("connections.qr_code_enabled") }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user