Odstranil jsem hardcoded IP fallbacky z portálového Python kódu:
DEFAULT_APPFACTORY_HOST = "192.168.66.130" je pryč z config.py (line 10). DEFAULT_GITEA_URL = "http://192.168.66.130:3000" je pryč z auth.py (line 24). Doplněné chování: APPFACTORY_HOST se odvozuje přes helper: env APPFACTORY_HOST, potom APPFACTORY_PORTAL_PUBLIC_URL, potom request host. Gitea browser redirect používá jen explicitní veřejnou URL z GITEA_URL nebo GITEA_ROOT_URL. Server-side Gitea token/userinfo requesty používají GITEA_URL, potom GITEA_ROOT_URL, jinak interní Docker URL http://appfactory-gitea:3000. Gitea login tlačítko se zobrazí jen při kompletní konfiguraci: public URL, client id, client secret a redirect URI. Přímý Gitea login bez kompletní konfigurace vrací čitelnou chybu.
This commit is contained in:
+42
-1
@@ -8,7 +8,7 @@ APPFACTORY_ENV = "/opt/appfactory/config/appfactory.env"
|
|||||||
|
|
||||||
DEFAULT_BACKUP_DIR = "/opt/appfactory/backups"
|
DEFAULT_BACKUP_DIR = "/opt/appfactory/backups"
|
||||||
DEFAULT_GITEA_ORG = "appfactory"
|
DEFAULT_GITEA_ORG = "appfactory"
|
||||||
DEFAULT_APPFACTORY_HOST = "192.168.66.130"
|
INTERNAL_GITEA_URL = "http://appfactory-gitea:3000"
|
||||||
|
|
||||||
PORTAL_PREFIX = "/portal"
|
PORTAL_PREFIX = "/portal"
|
||||||
|
|
||||||
@@ -58,6 +58,47 @@ def get_google_redirect_uri() -> str:
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_gitea_public_url() -> str:
|
||||||
|
return (read_env_value("GITEA_URL", "") or read_env_value("GITEA_ROOT_URL", "")).rstrip("/")
|
||||||
|
|
||||||
|
|
||||||
|
def get_gitea_server_url() -> str:
|
||||||
|
return get_gitea_public_url() or INTERNAL_GITEA_URL
|
||||||
|
|
||||||
|
|
||||||
|
def get_gitea_redirect_uri() -> str:
|
||||||
|
redirect_uri = read_env_value("GITEA_OAUTH_REDIRECT_URI", "").strip()
|
||||||
|
if redirect_uri:
|
||||||
|
return redirect_uri
|
||||||
|
|
||||||
|
public_url = get_portal_public_url()
|
||||||
|
if public_url:
|
||||||
|
return f"{public_url}/auth/gitea/callback"
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def is_gitea_oauth_button_enabled() -> bool:
|
||||||
|
return bool(
|
||||||
|
get_gitea_public_url()
|
||||||
|
and read_env_value("GITEA_OAUTH_CLIENT_ID", "")
|
||||||
|
and read_env_value("GITEA_OAUTH_CLIENT_SECRET", "")
|
||||||
|
and get_gitea_redirect_uri()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_appfactory_host(request_host: str = "") -> str:
|
||||||
|
configured_host = read_env_value("APPFACTORY_HOST", "").strip()
|
||||||
|
if configured_host:
|
||||||
|
return configured_host
|
||||||
|
|
||||||
|
public_url = get_portal_public_url()
|
||||||
|
if public_url:
|
||||||
|
return public_url.split("://", 1)[-1].split("/", 1)[0].split(":", 1)[0]
|
||||||
|
|
||||||
|
return (request_host or "").split(":", 1)[0]
|
||||||
|
|
||||||
|
|
||||||
def get_auth_domain_readiness() -> dict[str, bool]:
|
def get_auth_domain_readiness() -> dict[str, bool]:
|
||||||
google_enabled = read_env_bool("GOOGLE_OAUTH_ENABLED")
|
google_enabled = read_env_bool("GOOGLE_OAUTH_ENABLED")
|
||||||
google_client_id = read_env_value("GOOGLE_CLIENT_ID", "")
|
google_client_id = read_env_value("GOOGLE_CLIENT_ID", "")
|
||||||
|
|||||||
+7
-6
@@ -7,11 +7,12 @@ from fastapi.responses import HTMLResponse, RedirectResponse
|
|||||||
|
|
||||||
from ..auth import require_user
|
from ..auth import require_user
|
||||||
from ..config import (
|
from ..config import (
|
||||||
DEFAULT_APPFACTORY_HOST,
|
|
||||||
DEFAULT_GITEA_ORG,
|
DEFAULT_GITEA_ORG,
|
||||||
DELETE_APP_SCRIPT,
|
DELETE_APP_SCRIPT,
|
||||||
DEPLOY_SCRIPT,
|
DEPLOY_SCRIPT,
|
||||||
GENERATE_COMPOSE_SCRIPT,
|
GENERATE_COMPOSE_SCRIPT,
|
||||||
|
get_appfactory_host,
|
||||||
|
get_gitea_public_url,
|
||||||
read_env_value,
|
read_env_value,
|
||||||
)
|
)
|
||||||
from ..db.apps import (
|
from ..db.apps import (
|
||||||
@@ -206,9 +207,9 @@ def apps_page(
|
|||||||
offset = (page_number - 1) * DEFAULT_PAGE_SIZE
|
offset = (page_number - 1) * DEFAULT_PAGE_SIZE
|
||||||
apps = apps[offset : offset + DEFAULT_PAGE_SIZE]
|
apps = apps[offset : offset + DEFAULT_PAGE_SIZE]
|
||||||
|
|
||||||
gitea_url = read_env_value("GITEA_URL", "")
|
gitea_url = get_gitea_public_url()
|
||||||
gitea_org = read_env_value("GITEA_ORG", DEFAULT_GITEA_ORG)
|
gitea_org = read_env_value("GITEA_ORG", DEFAULT_GITEA_ORG)
|
||||||
host = read_env_value("APPFACTORY_HOST", DEFAULT_APPFACTORY_HOST)
|
host = get_appfactory_host(request.url.hostname or "")
|
||||||
|
|
||||||
rows = ""
|
rows = ""
|
||||||
|
|
||||||
@@ -226,8 +227,8 @@ def apps_page(
|
|||||||
memory_label = memory or "výchozí"
|
memory_label = memory or "výchozí"
|
||||||
cpus_label = cpus or "výchozí"
|
cpus_label = cpus or "výchozí"
|
||||||
|
|
||||||
http_clone = html.escape(f"git clone {gitea_url}/{gitea_org}/{app_id}.git")
|
http_clone = html.escape(f"git clone {gitea_url}/{gitea_org}/{app_id}.git") if gitea_url else ""
|
||||||
ssh_clone = html.escape(f"git clone ssh://git@{host}:2222/{gitea_org}/{app_id}.git")
|
ssh_clone = html.escape(f"git clone ssh://git@{host}:2222/{gitea_org}/{app_id}.git") if host else ""
|
||||||
|
|
||||||
rows += f"""
|
rows += f"""
|
||||||
<tr class="service-row">
|
<tr class="service-row">
|
||||||
@@ -970,7 +971,7 @@ def create_app(
|
|||||||
user=user,
|
user=user,
|
||||||
)
|
)
|
||||||
|
|
||||||
gitea_url = read_env_value("GITEA_URL", "").rstrip("/")
|
gitea_url = get_gitea_public_url()
|
||||||
gitea_org = read_env_value("GITEA_ORG", DEFAULT_GITEA_ORG)
|
gitea_org = read_env_value("GITEA_ORG", DEFAULT_GITEA_ORG)
|
||||||
repository_url = f"{gitea_url}/{gitea_org}/{app_id}.git" if gitea_url else None
|
repository_url = f"{gitea_url}/{gitea_org}/{app_id}.git" if gitea_url else None
|
||||||
owner_value = clean_optional(owner) or user.get("display_name") or user.get("username") or None
|
owner_value = clean_optional(owner) or user.get("display_name") or user.get("username") or None
|
||||||
|
|||||||
+25
-13
@@ -10,12 +10,19 @@ from fastapi import APIRouter, Form, Request
|
|||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
|
|
||||||
from app.auth import authenticate_user, current_user, find_or_create_oauth_user
|
from app.auth import authenticate_user, current_user, find_or_create_oauth_user
|
||||||
from app.config import get_google_redirect_uri, is_google_oauth_button_enabled, read_env_value
|
from app.config import (
|
||||||
|
get_gitea_public_url,
|
||||||
|
get_gitea_redirect_uri,
|
||||||
|
get_gitea_server_url,
|
||||||
|
get_google_redirect_uri,
|
||||||
|
is_gitea_oauth_button_enabled,
|
||||||
|
is_google_oauth_button_enabled,
|
||||||
|
read_env_value,
|
||||||
|
)
|
||||||
from app.db.audit import log_audit_event
|
from app.db.audit import log_audit_event
|
||||||
from app.templates.layout import page
|
from app.templates.layout import page
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
DEFAULT_GITEA_URL = "http://192.168.66.130:3000"
|
|
||||||
GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"
|
GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"
|
||||||
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
|
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
|
||||||
GOOGLE_USERINFO_URL = "https://openidconnect.googleapis.com/v1/userinfo"
|
GOOGLE_USERINFO_URL = "https://openidconnect.googleapis.com/v1/userinfo"
|
||||||
@@ -54,12 +61,13 @@ def gitea_login(request: Request):
|
|||||||
if current_user(request):
|
if current_user(request):
|
||||||
return RedirectResponse(url="/portal/operations", status_code=303)
|
return RedirectResponse(url="/portal/operations", status_code=303)
|
||||||
|
|
||||||
gitea_url = read_env_value("GITEA_URL", DEFAULT_GITEA_URL).rstrip("/")
|
gitea_url = get_gitea_public_url()
|
||||||
client_id = read_env_value("GITEA_OAUTH_CLIENT_ID", "")
|
client_id = read_env_value("GITEA_OAUTH_CLIENT_ID", "")
|
||||||
redirect_uri = read_env_value("GITEA_OAUTH_REDIRECT_URI", "")
|
client_secret = read_env_value("GITEA_OAUTH_CLIENT_SECRET", "")
|
||||||
if not client_id or not redirect_uri:
|
redirect_uri = get_gitea_redirect_uri()
|
||||||
|
if not gitea_url or not client_id or not client_secret or not redirect_uri:
|
||||||
_log_gitea_failure("missing_oauth_config")
|
_log_gitea_failure("missing_oauth_config")
|
||||||
return _render_login("Přihlášení přes Gitea není správně nastavené.")
|
return _render_login("Gitea login is not fully configured.")
|
||||||
|
|
||||||
state = secrets.token_urlsafe(32)
|
state = secrets.token_urlsafe(32)
|
||||||
request.session["gitea_oauth_state"] = state
|
request.session["gitea_oauth_state"] = state
|
||||||
@@ -227,10 +235,10 @@ def logout(request: Request):
|
|||||||
|
|
||||||
|
|
||||||
def _exchange_gitea_code(code: str) -> str:
|
def _exchange_gitea_code(code: str) -> str:
|
||||||
gitea_url = read_env_value("GITEA_URL", DEFAULT_GITEA_URL).rstrip("/")
|
gitea_url = get_gitea_server_url()
|
||||||
client_id = read_env_value("GITEA_OAUTH_CLIENT_ID", "")
|
client_id = read_env_value("GITEA_OAUTH_CLIENT_ID", "")
|
||||||
client_secret = read_env_value("GITEA_OAUTH_CLIENT_SECRET", "")
|
client_secret = read_env_value("GITEA_OAUTH_CLIENT_SECRET", "")
|
||||||
redirect_uri = read_env_value("GITEA_OAUTH_REDIRECT_URI", "")
|
redirect_uri = get_gitea_redirect_uri()
|
||||||
if not client_id or not client_secret or not redirect_uri:
|
if not client_id or not client_secret or not redirect_uri:
|
||||||
raise RuntimeError("Missing Gitea OAuth configuration")
|
raise RuntimeError("Missing Gitea OAuth configuration")
|
||||||
|
|
||||||
@@ -260,7 +268,7 @@ def _exchange_gitea_code(code: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _fetch_gitea_user(access_token: str) -> dict:
|
def _fetch_gitea_user(access_token: str) -> dict:
|
||||||
gitea_url = read_env_value("GITEA_URL", DEFAULT_GITEA_URL).rstrip("/")
|
gitea_url = get_gitea_server_url()
|
||||||
request = UrlRequest(
|
request = UrlRequest(
|
||||||
f"{gitea_url}/api/v1/user",
|
f"{gitea_url}/api/v1/user",
|
||||||
headers={
|
headers={
|
||||||
@@ -350,6 +358,13 @@ def _render_login(error: str | None = None) -> str:
|
|||||||
error_html = ""
|
error_html = ""
|
||||||
if error:
|
if error:
|
||||||
error_html = f'<p class="alert alert-danger">{html.escape(error)}</p>'
|
error_html = f'<p class="alert alert-danger">{html.escape(error)}</p>'
|
||||||
|
gitea_login_html = ""
|
||||||
|
if is_gitea_oauth_button_enabled():
|
||||||
|
gitea_login_html = """
|
||||||
|
<p>
|
||||||
|
<a class="btn" href="/portal/auth/gitea/login">Sign in with Gitea</a>
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
google_login_html = ""
|
google_login_html = ""
|
||||||
if is_google_oauth_button_enabled():
|
if is_google_oauth_button_enabled():
|
||||||
google_login_html = """
|
google_login_html = """
|
||||||
@@ -365,10 +380,7 @@ def _render_login(error: str | None = None) -> str:
|
|||||||
<h2>CSBot Services Portal</h2>
|
<h2>CSBot Services Portal</h2>
|
||||||
<p class="muted">Doporučené přihlášení je přes Gitea. Lokální účet slouží pouze jako nouzový administrátorský přístup.</p>
|
<p class="muted">Doporučené přihlášení je přes Gitea. Lokální účet slouží pouze jako nouzový administrátorský přístup.</p>
|
||||||
{error_html}
|
{error_html}
|
||||||
|
{gitea_login_html}
|
||||||
<p>
|
|
||||||
<a class="btn" href="/portal/auth/gitea/login">Přihlásit přes Gitea</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{google_login_html}
|
{google_login_html}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user