mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-12-15 00:35:47 -07:00
ENGINE: Adjusted Persistent Assets
This commit is contained in:
39
Borealis.ps1
39
Borealis.ps1
@@ -1432,15 +1432,46 @@ switch ($choice) {
|
|||||||
New-Item -Path $engineDataRoot -ItemType Directory -Force | Out-Null
|
New-Item -Path $engineDataRoot -ItemType Directory -Force | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Test-Path (Join-Path $scriptDir $engineDataDestination)) {
|
$engineDataAbsolute = Join-Path $scriptDir $engineDataDestination
|
||||||
Remove-Item (Join-Path $scriptDir $engineDataDestination) -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
|
$runtimeAssemblies = Join-Path $scriptDir 'Engine\Assemblies'
|
||||||
|
$sourceAssemblies = Join-Path $engineSourceAbsolute 'Assemblies'
|
||||||
|
|
||||||
|
$runtimeDatabase = Join-Path $scriptDir 'Engine\database.db'
|
||||||
|
|
||||||
|
$runtimeAuthTokens = Join-Path $scriptDir 'Engine\Auth_Tokens'
|
||||||
|
|
||||||
|
if (Test-Path $engineDataAbsolute) {
|
||||||
|
Remove-Item $engineDataAbsolute -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
}
|
}
|
||||||
New-Item -Path (Join-Path $scriptDir $engineDataDestination) -ItemType Directory -Force | Out-Null
|
New-Item -Path $engineDataAbsolute -ItemType Directory -Force | Out-Null
|
||||||
|
|
||||||
if (-not (Test-Path $engineSourceAbsolute)) {
|
if (-not (Test-Path $engineSourceAbsolute)) {
|
||||||
throw "Engine source directory '$engineSourceAbsolute' not found."
|
throw "Engine source directory '$engineSourceAbsolute' not found."
|
||||||
}
|
}
|
||||||
Copy-Item (Join-Path $engineSourceAbsolute '*') (Join-Path $scriptDir $engineDataDestination) -Recurse -Force
|
Get-ChildItem -Path $engineSourceAbsolute -Force | ForEach-Object {
|
||||||
|
if ($_.Name -ieq 'Assemblies') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Copy-Item -Path $_.FullName -Destination $engineDataAbsolute -Recurse -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $runtimeAssemblies) -and (Test-Path $sourceAssemblies)) {
|
||||||
|
Copy-Item -Path $sourceAssemblies -Destination $runtimeAssemblies -Recurse -Force
|
||||||
|
} elseif (-not (Test-Path $runtimeAssemblies)) {
|
||||||
|
New-Item -Path $runtimeAssemblies -ItemType Directory -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $runtimeAuthTokens)) {
|
||||||
|
New-Item -Path $runtimeAuthTokens -ItemType Directory -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $runtimeDatabase)) {
|
||||||
|
$runtimeDatabaseDir = Split-Path -Path $runtimeDatabase -Parent
|
||||||
|
if (-not (Test-Path $runtimeDatabaseDir)) {
|
||||||
|
New-Item -Path $runtimeDatabaseDir -ItemType Directory -Force | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
. (Join-Path $venvFolder 'Scripts\Activate')
|
. (Join-Path $venvFolder 'Scripts\Activate')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,21 @@ CREATE TABLE IF NOT EXISTS enrollment_install_codes (
|
|||||||
use_count INTEGER,
|
use_count INTEGER,
|
||||||
last_used_at TEXT
|
last_used_at TEXT
|
||||||
);
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS enrollment_install_codes_persistent (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
code TEXT UNIQUE,
|
||||||
|
created_at TEXT NOT NULL,
|
||||||
|
expires_at TEXT NOT NULL,
|
||||||
|
created_by_user_id TEXT,
|
||||||
|
used_at TEXT,
|
||||||
|
used_by_guid TEXT,
|
||||||
|
max_uses INTEGER NOT NULL DEFAULT 1,
|
||||||
|
last_known_use_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
last_used_at TEXT,
|
||||||
|
is_active INTEGER NOT NULL DEFAULT 1,
|
||||||
|
archived_at TEXT,
|
||||||
|
consumed_at TEXT
|
||||||
|
);
|
||||||
CREATE TABLE IF NOT EXISTS device_approvals (
|
CREATE TABLE IF NOT EXISTS device_approvals (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
approval_reference TEXT UNIQUE,
|
approval_reference TEXT UNIQUE,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from cryptography.hazmat.primitives.asymmetric import ed25519
|
|||||||
from flask.testing import FlaskClient
|
from flask.testing import FlaskClient
|
||||||
|
|
||||||
from Data.Engine.crypto import keys as crypto_keys
|
from Data.Engine.crypto import keys as crypto_keys
|
||||||
|
from Data.Engine.database import initialise_engine_database
|
||||||
|
|
||||||
from .conftest import EngineTestHarness
|
from .conftest import EngineTestHarness
|
||||||
|
|
||||||
@@ -32,7 +33,9 @@ def _iso(dt: datetime) -> str:
|
|||||||
|
|
||||||
def _seed_install_code(db_path: os.PathLike[str], code: str) -> str:
|
def _seed_install_code(db_path: os.PathLike[str], code: str) -> str:
|
||||||
record_id = str(uuid.uuid4())
|
record_id = str(uuid.uuid4())
|
||||||
expires_at = _iso(_now() + timedelta(days=1))
|
baseline = _now()
|
||||||
|
issued_at = _iso(baseline)
|
||||||
|
expires_at = _iso(baseline + timedelta(days=1))
|
||||||
with sqlite3.connect(str(db_path)) as conn:
|
with sqlite3.connect(str(db_path)) as conn:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""
|
"""
|
||||||
@@ -42,6 +45,40 @@ def _seed_install_code(db_path: os.PathLike[str], code: str) -> str:
|
|||||||
""",
|
""",
|
||||||
(record_id, code, expires_at, None, None, 1, 0, None),
|
(record_id, code, expires_at, None, None, 1, 0, None),
|
||||||
)
|
)
|
||||||
|
conn.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO enrollment_install_codes_persistent (
|
||||||
|
id,
|
||||||
|
code,
|
||||||
|
created_at,
|
||||||
|
expires_at,
|
||||||
|
created_by_user_id,
|
||||||
|
used_at,
|
||||||
|
used_by_guid,
|
||||||
|
max_uses,
|
||||||
|
last_known_use_count,
|
||||||
|
last_used_at,
|
||||||
|
is_active,
|
||||||
|
archived_at,
|
||||||
|
consumed_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
record_id,
|
||||||
|
code,
|
||||||
|
issued_at,
|
||||||
|
expires_at,
|
||||||
|
"test-suite",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
1,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return record_id
|
return record_id
|
||||||
|
|
||||||
@@ -193,6 +230,15 @@ def test_enrollment_poll_finalizes_when_approved(engine_harness: EngineTestHarne
|
|||||||
(final_guid,),
|
(final_guid,),
|
||||||
)
|
)
|
||||||
key_count = cur.fetchone()[0]
|
key_count = cur.fetchone()[0]
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT is_active, last_known_use_count, used_by_guid, consumed_at
|
||||||
|
FROM enrollment_install_codes_persistent
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(install_code_id,),
|
||||||
|
)
|
||||||
|
persistent_row = cur.fetchone()
|
||||||
|
|
||||||
assert approval_row is not None
|
assert approval_row is not None
|
||||||
approval_guid, approval_status = approval_row
|
approval_guid, approval_status = approval_row
|
||||||
@@ -211,3 +257,75 @@ def test_enrollment_poll_finalizes_when_approved(engine_harness: EngineTestHarne
|
|||||||
assert use_count == 1
|
assert use_count == 1
|
||||||
assert used_by_guid == final_guid
|
assert used_by_guid == final_guid
|
||||||
assert key_count == 1
|
assert key_count == 1
|
||||||
|
assert persistent_row is not None
|
||||||
|
is_active, last_known_use_count, persistent_guid, consumed_at = persistent_row
|
||||||
|
assert is_active == 0
|
||||||
|
assert last_known_use_count == 1
|
||||||
|
assert persistent_guid == final_guid
|
||||||
|
assert consumed_at is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_persistent_enrollment_codes_restore_active_table(engine_harness: EngineTestHarness) -> None:
|
||||||
|
harness = engine_harness
|
||||||
|
code_id = str(uuid.uuid4())
|
||||||
|
baseline = _now()
|
||||||
|
issued_iso = _iso(baseline)
|
||||||
|
expires_iso = _iso(baseline + timedelta(hours=4))
|
||||||
|
|
||||||
|
with sqlite3.connect(str(harness.db_path)) as conn:
|
||||||
|
conn.execute("DELETE FROM enrollment_install_codes")
|
||||||
|
conn.execute(
|
||||||
|
"""
|
||||||
|
INSERT OR REPLACE INTO enrollment_install_codes_persistent (
|
||||||
|
id,
|
||||||
|
code,
|
||||||
|
created_at,
|
||||||
|
expires_at,
|
||||||
|
created_by_user_id,
|
||||||
|
used_at,
|
||||||
|
used_by_guid,
|
||||||
|
max_uses,
|
||||||
|
last_known_use_count,
|
||||||
|
last_used_at,
|
||||||
|
is_active,
|
||||||
|
archived_at,
|
||||||
|
consumed_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
code_id,
|
||||||
|
"RESTORE-CODE-001",
|
||||||
|
issued_iso,
|
||||||
|
expires_iso,
|
||||||
|
"restorer",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
1,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
initialise_engine_database(str(harness.db_path))
|
||||||
|
|
||||||
|
with sqlite3.connect(str(harness.db_path)) as conn:
|
||||||
|
cur = conn.execute(
|
||||||
|
"""
|
||||||
|
SELECT code, expires_at, max_uses, use_count
|
||||||
|
FROM enrollment_install_codes
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(code_id,),
|
||||||
|
)
|
||||||
|
row = cur.fetchone()
|
||||||
|
|
||||||
|
assert row is not None
|
||||||
|
restored_code, restored_expires, restored_max_uses, restored_use_count = row
|
||||||
|
assert restored_code == "RESTORE-CODE-001"
|
||||||
|
assert restored_expires == expires_iso
|
||||||
|
assert restored_max_uses == 3
|
||||||
|
assert restored_use_count == 0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================
|
# ======================================================
|
||||||
# Data\Engine\auth\jwt_service.py
|
# Data\Engine\auth\jwt_service.py
|
||||||
# Description: Engine-native JWT access-token helpers with signing key storage under Engine/Data/Auth_Tokens.
|
# Description: Engine-native JWT access-token helpers with signing key storage under Engine/Auth_Tokens.
|
||||||
#
|
#
|
||||||
# API Endpoints (if applicable): None
|
# API Endpoints (if applicable): None
|
||||||
# ======================================================
|
# ======================================================
|
||||||
@@ -53,7 +53,7 @@ def _token_root() -> Path:
|
|||||||
if env:
|
if env:
|
||||||
env.mkdir(parents=True, exist_ok=True)
|
env.mkdir(parents=True, exist_ok=True)
|
||||||
return env
|
return env
|
||||||
root = _engine_runtime_root() / "Data" / "Auth_Tokens"
|
root = _engine_runtime_root() / "Auth_Tokens"
|
||||||
root.mkdir(parents=True, exist_ok=True)
|
root.mkdir(parents=True, exist_ok=True)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ environment variables prefixed with ``BOREALIS_``, and finally built-in
|
|||||||
defaults that mirror the legacy server runtime. Key environment variables are
|
defaults that mirror the legacy server runtime. Key environment variables are
|
||||||
|
|
||||||
``BOREALIS_DATABASE_PATH`` path to the SQLite database file. Defaults to
|
``BOREALIS_DATABASE_PATH`` path to the SQLite database file. Defaults to
|
||||||
the Engine runtime copy under ``<ProjectRoot>/Engine/Data/Engine/database.db``.
|
``<ProjectRoot>/Engine/database.db`` so data persists across Engine redeploys.
|
||||||
``BOREALIS_CORS_ORIGINS`` comma separated list of allowed origins for CORS.
|
``BOREALIS_CORS_ORIGINS`` comma separated list of allowed origins for CORS.
|
||||||
``BOREALIS_SECRET`` Flask session secret key.
|
``BOREALIS_SECRET`` Flask session secret key.
|
||||||
``BOREALIS_COOKIE_*`` Session cookie policies (``SAMESITE``, ``SECURE``,
|
``BOREALIS_COOKIE_*`` Session cookie policies (``SAMESITE``, ``SECURE``,
|
||||||
@@ -72,7 +72,7 @@ def _discover_project_root() -> Path:
|
|||||||
|
|
||||||
|
|
||||||
PROJECT_ROOT = _discover_project_root()
|
PROJECT_ROOT = _discover_project_root()
|
||||||
DEFAULT_DATABASE_PATH = PROJECT_ROOT / "Engine" / "Data" / "Engine" / "database.db"
|
DEFAULT_DATABASE_PATH = PROJECT_ROOT / "Engine" / "database.db"
|
||||||
LOG_ROOT = PROJECT_ROOT / "Engine" / "Logs"
|
LOG_ROOT = PROJECT_ROOT / "Engine" / "Logs"
|
||||||
LOG_FILE_PATH = LOG_ROOT / "engine.log"
|
LOG_FILE_PATH = LOG_ROOT / "engine.log"
|
||||||
ERROR_LOG_FILE_PATH = LOG_ROOT / "error.log"
|
ERROR_LOG_FILE_PATH = LOG_ROOT / "error.log"
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ def initialise_engine_database(database_path: str, *, logger: Optional[logging.L
|
|||||||
conn = sqlite3.connect(str(path))
|
conn = sqlite3.connect(str(path))
|
||||||
try:
|
try:
|
||||||
_apply_legacy_migrations(conn, logger=logger)
|
_apply_legacy_migrations(conn, logger=logger)
|
||||||
|
_restore_persisted_enrollment_codes(conn, logger=logger)
|
||||||
_ensure_activity_history(conn, logger=logger)
|
_ensure_activity_history(conn, logger=logger)
|
||||||
_ensure_device_list_views(conn, logger=logger)
|
_ensure_device_list_views(conn, logger=logger)
|
||||||
_ensure_sites(conn, logger=logger)
|
_ensure_sites(conn, logger=logger)
|
||||||
@@ -79,6 +80,58 @@ def _apply_legacy_migrations(conn: sqlite3.Connection, *, logger: Optional[loggi
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _restore_persisted_enrollment_codes(conn: sqlite3.Connection, *, logger: Optional[logging.Logger]) -> None:
|
||||||
|
cur = conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute(
|
||||||
|
"SELECT 1 FROM sqlite_master WHERE type='table' AND name='enrollment_install_codes_persistent'"
|
||||||
|
)
|
||||||
|
if cur.fetchone() is None:
|
||||||
|
return
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO enrollment_install_codes (
|
||||||
|
id,
|
||||||
|
code,
|
||||||
|
expires_at,
|
||||||
|
created_by_user_id,
|
||||||
|
used_at,
|
||||||
|
used_by_guid,
|
||||||
|
max_uses,
|
||||||
|
use_count,
|
||||||
|
last_used_at
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
p.id,
|
||||||
|
p.code,
|
||||||
|
p.expires_at,
|
||||||
|
p.created_by_user_id,
|
||||||
|
p.used_at,
|
||||||
|
p.used_by_guid,
|
||||||
|
p.max_uses,
|
||||||
|
p.last_known_use_count,
|
||||||
|
p.last_used_at
|
||||||
|
FROM enrollment_install_codes_persistent AS p
|
||||||
|
WHERE p.is_active = 1
|
||||||
|
ON CONFLICT(id) DO UPDATE
|
||||||
|
SET code = excluded.code,
|
||||||
|
expires_at = excluded.expires_at,
|
||||||
|
created_by_user_id = excluded.created_by_user_id,
|
||||||
|
used_at = excluded.used_at,
|
||||||
|
used_by_guid = excluded.used_by_guid,
|
||||||
|
max_uses = excluded.max_uses,
|
||||||
|
use_count = excluded.use_count,
|
||||||
|
last_used_at = excluded.last_used_at
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
except Exception as exc:
|
||||||
|
if logger:
|
||||||
|
logger.error("Failed to restore enrollment codes from persistence: %s", exc, exc_info=True)
|
||||||
|
finally:
|
||||||
|
cur.close()
|
||||||
|
|
||||||
|
|
||||||
def _ensure_activity_history(conn: sqlite3.Connection, *, logger: Optional[logging.Logger]) -> None:
|
def _ensure_activity_history(conn: sqlite3.Connection, *, logger: Optional[logging.Logger]) -> None:
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -244,7 +244,8 @@ class AdminDeviceService:
|
|||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
created_by = self._lookup_user_id(cur, username) or username or "system"
|
created_by = self._lookup_user_id(cur, username) or username or "system"
|
||||||
code_value = _generate_install_code()
|
code_value = _generate_install_code()
|
||||||
expires_at = _now() + timedelta(hours=ttl_hours)
|
issued_at = _now()
|
||||||
|
expires_at = issued_at + timedelta(hours=ttl_hours)
|
||||||
record_id = str(uuid.uuid4())
|
record_id = str(uuid.uuid4())
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
@@ -255,6 +256,40 @@ class AdminDeviceService:
|
|||||||
""",
|
""",
|
||||||
(record_id, code_value, _iso(expires_at), created_by, max_uses),
|
(record_id, code_value, _iso(expires_at), created_by, max_uses),
|
||||||
)
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO enrollment_install_codes_persistent (
|
||||||
|
id,
|
||||||
|
code,
|
||||||
|
created_at,
|
||||||
|
expires_at,
|
||||||
|
created_by_user_id,
|
||||||
|
used_at,
|
||||||
|
used_by_guid,
|
||||||
|
max_uses,
|
||||||
|
last_known_use_count,
|
||||||
|
last_used_at,
|
||||||
|
is_active,
|
||||||
|
archived_at,
|
||||||
|
consumed_at
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, NULL, NULL, ?, 0, NULL, 1, NULL, NULL)
|
||||||
|
ON CONFLICT(id) DO UPDATE
|
||||||
|
SET code = excluded.code,
|
||||||
|
created_at = excluded.created_at,
|
||||||
|
expires_at = excluded.expires_at,
|
||||||
|
created_by_user_id = excluded.created_by_user_id,
|
||||||
|
max_uses = excluded.max_uses,
|
||||||
|
last_known_use_count = 0,
|
||||||
|
used_at = NULL,
|
||||||
|
used_by_guid = NULL,
|
||||||
|
last_used_at = NULL,
|
||||||
|
is_active = 1,
|
||||||
|
archived_at = NULL,
|
||||||
|
consumed_at = NULL
|
||||||
|
""",
|
||||||
|
(record_id, code_value, _iso(issued_at), _iso(expires_at), created_by, max_uses),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -284,6 +319,17 @@ class AdminDeviceService:
|
|||||||
(code_id,),
|
(code_id,),
|
||||||
)
|
)
|
||||||
deleted = cur.rowcount
|
deleted = cur.rowcount
|
||||||
|
if deleted:
|
||||||
|
archive_ts = _iso(_now())
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
UPDATE enrollment_install_codes_persistent
|
||||||
|
SET is_active = 0,
|
||||||
|
archived_at = COALESCE(archived_at, ?)
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(archive_ts, code_id),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -681,6 +681,32 @@ def register(
|
|||||||
enrollment_code_id,
|
enrollment_code_id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
UPDATE enrollment_install_codes_persistent
|
||||||
|
SET last_known_use_count = ?,
|
||||||
|
used_by_guid = ?,
|
||||||
|
last_used_at = ?,
|
||||||
|
used_at = CASE WHEN ? THEN ? ELSE used_at END,
|
||||||
|
is_active = CASE WHEN ? THEN 0 ELSE is_active END,
|
||||||
|
consumed_at = CASE WHEN ? THEN COALESCE(consumed_at, ?) ELSE consumed_at END,
|
||||||
|
archived_at = CASE WHEN ? THEN COALESCE(archived_at, ?) ELSE archived_at END
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
new_count,
|
||||||
|
effective_guid,
|
||||||
|
now_iso,
|
||||||
|
1 if consumed else 0,
|
||||||
|
now_iso,
|
||||||
|
1 if consumed else 0,
|
||||||
|
1 if consumed else 0,
|
||||||
|
now_iso,
|
||||||
|
1 if consumed else 0,
|
||||||
|
now_iso,
|
||||||
|
enrollment_code_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# Update approval record with final state
|
# Update approval record with final state
|
||||||
cur.execute(
|
cur.execute(
|
||||||
|
|||||||
@@ -195,7 +195,8 @@ def register(
|
|||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
created_by = _lookup_user_id(cur, username) or username or "system"
|
created_by = _lookup_user_id(cur, username) or username or "system"
|
||||||
code_value = _generate_install_code()
|
code_value = _generate_install_code()
|
||||||
expires_at = _now() + timedelta(hours=ttl_hours)
|
issued_at = _now()
|
||||||
|
expires_at = issued_at + timedelta(hours=ttl_hours)
|
||||||
record_id = str(uuid.uuid4())
|
record_id = str(uuid.uuid4())
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
@@ -206,6 +207,40 @@ def register(
|
|||||||
""",
|
""",
|
||||||
(record_id, code_value, _iso(expires_at), created_by, max_uses),
|
(record_id, code_value, _iso(expires_at), created_by, max_uses),
|
||||||
)
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO enrollment_install_codes_persistent (
|
||||||
|
id,
|
||||||
|
code,
|
||||||
|
created_at,
|
||||||
|
expires_at,
|
||||||
|
created_by_user_id,
|
||||||
|
used_at,
|
||||||
|
used_by_guid,
|
||||||
|
max_uses,
|
||||||
|
last_known_use_count,
|
||||||
|
last_used_at,
|
||||||
|
is_active,
|
||||||
|
archived_at,
|
||||||
|
consumed_at
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, NULL, NULL, ?, 0, NULL, 1, NULL, NULL)
|
||||||
|
ON CONFLICT(id) DO UPDATE
|
||||||
|
SET code = excluded.code,
|
||||||
|
created_at = excluded.created_at,
|
||||||
|
expires_at = excluded.expires_at,
|
||||||
|
created_by_user_id = excluded.created_by_user_id,
|
||||||
|
max_uses = excluded.max_uses,
|
||||||
|
last_known_use_count = 0,
|
||||||
|
used_at = NULL,
|
||||||
|
used_by_guid = NULL,
|
||||||
|
last_used_at = NULL,
|
||||||
|
is_active = 1,
|
||||||
|
archived_at = NULL,
|
||||||
|
consumed_at = NULL
|
||||||
|
""",
|
||||||
|
(record_id, code_value, _iso(issued_at), _iso(expires_at), created_by, max_uses),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -235,6 +270,17 @@ def register(
|
|||||||
(code_id,),
|
(code_id,),
|
||||||
)
|
)
|
||||||
deleted = cur.rowcount
|
deleted = cur.rowcount
|
||||||
|
if deleted:
|
||||||
|
archive_ts = _iso(_now())
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
UPDATE enrollment_install_codes_persistent
|
||||||
|
SET is_active = 0,
|
||||||
|
archived_at = COALESCE(archived_at, ?)
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(archive_ts, code_id),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ def apply_all(conn: sqlite3.Connection) -> None:
|
|||||||
_ensure_device_aux_tables(conn)
|
_ensure_device_aux_tables(conn)
|
||||||
_ensure_refresh_token_table(conn)
|
_ensure_refresh_token_table(conn)
|
||||||
_ensure_install_code_table(conn)
|
_ensure_install_code_table(conn)
|
||||||
|
_ensure_install_code_persistence_table(conn)
|
||||||
_ensure_device_approval_table(conn)
|
_ensure_device_approval_table(conn)
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -190,6 +191,92 @@ def _ensure_install_code_table(conn: sqlite3.Connection) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_install_code_persistence_table(conn: sqlite3.Connection) -> None:
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS enrollment_install_codes_persistent (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
code TEXT NOT NULL UNIQUE,
|
||||||
|
created_at TEXT NOT NULL,
|
||||||
|
expires_at TEXT NOT NULL,
|
||||||
|
created_by_user_id TEXT,
|
||||||
|
used_at TEXT,
|
||||||
|
used_by_guid TEXT,
|
||||||
|
max_uses INTEGER NOT NULL DEFAULT 1,
|
||||||
|
last_known_use_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
last_used_at TEXT,
|
||||||
|
is_active INTEGER NOT NULL DEFAULT 1,
|
||||||
|
archived_at TEXT,
|
||||||
|
consumed_at TEXT
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_eicp_active
|
||||||
|
ON enrollment_install_codes_persistent(is_active, expires_at)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_eicp_code
|
||||||
|
ON enrollment_install_codes_persistent(code)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
columns = {row[1] for row in _table_info(cur, "enrollment_install_codes_persistent")}
|
||||||
|
if "last_known_use_count" not in columns:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE enrollment_install_codes_persistent
|
||||||
|
ADD COLUMN last_known_use_count INTEGER NOT NULL DEFAULT 0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if "archived_at" not in columns:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE enrollment_install_codes_persistent
|
||||||
|
ADD COLUMN archived_at TEXT
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if "consumed_at" not in columns:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE enrollment_install_codes_persistent
|
||||||
|
ADD COLUMN consumed_at TEXT
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if "is_active" not in columns:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE enrollment_install_codes_persistent
|
||||||
|
ADD COLUMN is_active INTEGER NOT NULL DEFAULT 1
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if "used_at" not in columns:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE enrollment_install_codes_persistent
|
||||||
|
ADD COLUMN used_at TEXT
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if "used_by_guid" not in columns:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE enrollment_install_codes_persistent
|
||||||
|
ADD COLUMN used_by_guid TEXT
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if "last_used_at" not in columns:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE enrollment_install_codes_persistent
|
||||||
|
ADD COLUMN last_used_at TEXT
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _ensure_device_approval_table(conn: sqlite3.Connection) -> None:
|
def _ensure_device_approval_table(conn: sqlite3.Connection) -> None:
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute(
|
cur.execute(
|
||||||
|
|||||||
@@ -671,6 +671,32 @@ def register(
|
|||||||
enrollment_code_id,
|
enrollment_code_id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
UPDATE enrollment_install_codes_persistent
|
||||||
|
SET last_known_use_count = ?,
|
||||||
|
used_by_guid = ?,
|
||||||
|
last_used_at = ?,
|
||||||
|
used_at = CASE WHEN ? THEN ? ELSE used_at END,
|
||||||
|
is_active = CASE WHEN ? THEN 0 ELSE is_active END,
|
||||||
|
consumed_at = CASE WHEN ? THEN COALESCE(consumed_at, ?) ELSE consumed_at END,
|
||||||
|
archived_at = CASE WHEN ? THEN COALESCE(archived_at, ?) ELSE archived_at END
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
new_count,
|
||||||
|
effective_guid,
|
||||||
|
now_iso,
|
||||||
|
1 if consumed else 0,
|
||||||
|
now_iso,
|
||||||
|
1 if consumed else 0,
|
||||||
|
1 if consumed else 0,
|
||||||
|
now_iso,
|
||||||
|
1 if consumed else 0,
|
||||||
|
now_iso,
|
||||||
|
enrollment_code_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# Update approval record with final state
|
# Update approval record with final state
|
||||||
cur.execute(
|
cur.execute(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Callable, Optional
|
from typing import Callable, List, Optional
|
||||||
|
|
||||||
import eventlet
|
import eventlet
|
||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
@@ -31,6 +31,27 @@ def _run_once(db_conn_factory: Callable[[], any], log: Callable[[str, str, Optio
|
|||||||
conn = db_conn_factory()
|
conn = db_conn_factory()
|
||||||
try:
|
try:
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
persistent_table_exists = False
|
||||||
|
try:
|
||||||
|
cur.execute(
|
||||||
|
"SELECT 1 FROM sqlite_master WHERE type='table' AND name='enrollment_install_codes_persistent'"
|
||||||
|
)
|
||||||
|
persistent_table_exists = cur.fetchone() is not None
|
||||||
|
except Exception:
|
||||||
|
persistent_table_exists = False
|
||||||
|
|
||||||
|
expired_ids: List[str] = []
|
||||||
|
if persistent_table_exists:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT id
|
||||||
|
FROM enrollment_install_codes
|
||||||
|
WHERE use_count = 0
|
||||||
|
AND expires_at < ?
|
||||||
|
""",
|
||||||
|
(now_iso,),
|
||||||
|
)
|
||||||
|
expired_ids = [str(row[0]) for row in cur.fetchall() if row and row[0]]
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
DELETE FROM enrollment_install_codes
|
DELETE FROM enrollment_install_codes
|
||||||
@@ -40,6 +61,21 @@ def _run_once(db_conn_factory: Callable[[], any], log: Callable[[str, str, Optio
|
|||||||
(now_iso,),
|
(now_iso,),
|
||||||
)
|
)
|
||||||
codes_pruned = cur.rowcount or 0
|
codes_pruned = cur.rowcount or 0
|
||||||
|
if expired_ids:
|
||||||
|
placeholders = ",".join("?" for _ in expired_ids)
|
||||||
|
try:
|
||||||
|
cur.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE enrollment_install_codes_persistent
|
||||||
|
SET is_active = 0,
|
||||||
|
archived_at = COALESCE(archived_at, ?)
|
||||||
|
WHERE id IN ({placeholders})
|
||||||
|
""",
|
||||||
|
(now_iso, *expired_ids),
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
# Best-effort archival; continue if the persistence table is absent.
|
||||||
|
pass
|
||||||
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user