admin: store version history (#11520)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
committed by
GitHub
parent
0976e05c7d
commit
3262e70eac
33
authentik/admin/api/version_history.py
Normal file
33
authentik/admin/api/version_history.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from rest_framework.permissions import IsAdminUser
|
||||||
|
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||||
|
|
||||||
|
from authentik.admin.models import VersionHistory
|
||||||
|
from authentik.core.api.utils import ModelSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class VersionHistorySerializer(ModelSerializer):
|
||||||
|
"""VersionHistory Serializer"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = VersionHistory
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"timestamp",
|
||||||
|
"version",
|
||||||
|
"build",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class VersionHistoryViewSet(ReadOnlyModelViewSet):
|
||||||
|
"""VersionHistory Viewset"""
|
||||||
|
|
||||||
|
queryset = VersionHistory.objects.all()
|
||||||
|
serializer_class = VersionHistorySerializer
|
||||||
|
permission_classes = [IsAdminUser]
|
||||||
|
filterset_fields = [
|
||||||
|
"version",
|
||||||
|
"build",
|
||||||
|
]
|
||||||
|
search_fields = ["version", "build"]
|
||||||
|
ordering = ["-timestamp"]
|
||||||
|
pagination_class = None
|
||||||
22
authentik/admin/models.py
Normal file
22
authentik/admin/models.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"""authentik admin models"""
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class VersionHistory(models.Model):
|
||||||
|
id = models.BigAutoField(primary_key=True)
|
||||||
|
timestamp = models.DateTimeField()
|
||||||
|
version = models.TextField()
|
||||||
|
build = models.TextField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
managed = False
|
||||||
|
db_table = "authentik_version_history"
|
||||||
|
ordering = ("-timestamp",)
|
||||||
|
verbose_name = _("Version history")
|
||||||
|
verbose_name_plural = _("Version history")
|
||||||
|
default_permissions = []
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.version}.{self.build} ({self.timestamp})"
|
||||||
@ -6,6 +6,7 @@ from authentik.admin.api.meta import AppsViewSet, ModelViewSet
|
|||||||
from authentik.admin.api.metrics import AdministrationMetricsViewSet
|
from authentik.admin.api.metrics import AdministrationMetricsViewSet
|
||||||
from authentik.admin.api.system import SystemView
|
from authentik.admin.api.system import SystemView
|
||||||
from authentik.admin.api.version import VersionView
|
from authentik.admin.api.version import VersionView
|
||||||
|
from authentik.admin.api.version_history import VersionHistoryViewSet
|
||||||
from authentik.admin.api.workers import WorkerView
|
from authentik.admin.api.workers import WorkerView
|
||||||
|
|
||||||
api_urlpatterns = [
|
api_urlpatterns = [
|
||||||
@ -17,6 +18,7 @@ api_urlpatterns = [
|
|||||||
name="admin_metrics",
|
name="admin_metrics",
|
||||||
),
|
),
|
||||||
path("admin/version/", VersionView.as_view(), name="admin_version"),
|
path("admin/version/", VersionView.as_view(), name="admin_version"),
|
||||||
|
("admin/version/history", VersionHistoryViewSet, "version_history"),
|
||||||
path("admin/workers/", WorkerView.as_view(), name="admin_workers"),
|
path("admin/workers/", WorkerView.as_view(), name="admin_workers"),
|
||||||
path("admin/system/", SystemView.as_view(), name="admin_system"),
|
path("admin/system/", SystemView.as_view(), name="admin_system"),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -84,7 +84,9 @@ def run_migrations():
|
|||||||
curr = conn.cursor()
|
curr = conn.cursor()
|
||||||
try:
|
try:
|
||||||
wait_for_lock(curr)
|
wait_for_lock(curr)
|
||||||
for migration_path in Path(__file__).parent.absolute().glob("system_migrations/*.py"):
|
for migration_path in sorted(
|
||||||
|
Path(__file__).parent.absolute().glob("system_migrations/*.py")
|
||||||
|
):
|
||||||
spec = spec_from_file_location("lifecycle.system_migrations", migration_path)
|
spec = spec_from_file_location("lifecycle.system_migrations", migration_path)
|
||||||
if not spec:
|
if not spec:
|
||||||
continue
|
continue
|
||||||
|
|||||||
24
lifecycle/system_migrations/version_history_create.py
Normal file
24
lifecycle/system_migrations/version_history_create.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# flake8: noqa
|
||||||
|
from lifecycle.migrate import BaseMigration
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(BaseMigration):
|
||||||
|
def needs_migration(self) -> bool:
|
||||||
|
self.cur.execute(
|
||||||
|
"SELECT * FROM information_schema.tables WHERE table_name = 'authentik_version_history';"
|
||||||
|
)
|
||||||
|
return not bool(self.cur.rowcount)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.cur.execute(
|
||||||
|
"""
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
CREATE TABLE IF NOT EXISTS authentik_version_history (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
"timestamp" timestamp with time zone NOT NULL,
|
||||||
|
version text NOT NULL,
|
||||||
|
build text NOT NULL
|
||||||
|
);
|
||||||
|
COMMIT;
|
||||||
|
"""
|
||||||
|
)
|
||||||
38
lifecycle/system_migrations/version_history_update.py
Normal file
38
lifecycle/system_migrations/version_history_update.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# flake8: noqa
|
||||||
|
from lifecycle.migrate import BaseMigration
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from authentik import __version__, get_build_hash
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(BaseMigration):
|
||||||
|
def needs_migration(self) -> bool:
|
||||||
|
self.cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT * FROM authentik_version_history
|
||||||
|
WHERE version = %s AND build = %s
|
||||||
|
ORDER BY "timestamp" DESC
|
||||||
|
LIMIT 1
|
||||||
|
""",
|
||||||
|
(__version__, get_build_hash()),
|
||||||
|
)
|
||||||
|
return not bool(self.cur.rowcount)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO authentik_version_history ("timestamp", version, build)
|
||||||
|
VALUES (%s, %s, %s)
|
||||||
|
""",
|
||||||
|
(datetime.now(), __version__, get_build_hash()),
|
||||||
|
)
|
||||||
|
self.cur.execute(
|
||||||
|
"""
|
||||||
|
DELETE FROM authentik_version_history WHERE id NOT IN (
|
||||||
|
SELECT id FROM authentik_version_history
|
||||||
|
ORDER BY "timestamp" DESC
|
||||||
|
LIMIT 1000
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.con.commit()
|
||||||
103
schema.yml
103
schema.yml
@ -263,6 +263,90 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
description: ''
|
description: ''
|
||||||
|
/admin/version/history/:
|
||||||
|
get:
|
||||||
|
operationId: admin_version_history_list
|
||||||
|
description: VersionHistory Viewset
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: build
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: ordering
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: Which field to use when ordering the results.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: search
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: A search term.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: version
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
- admin
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/VersionHistory'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
|
/admin/version/history/{id}/:
|
||||||
|
get:
|
||||||
|
operationId: admin_version_history_retrieve
|
||||||
|
description: VersionHistory Viewset
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
description: A unique integer value identifying this Version history.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- admin
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VersionHistory'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
/admin/workers/:
|
/admin/workers/:
|
||||||
get:
|
get:
|
||||||
operationId: admin_workers_retrieve
|
operationId: admin_workers_retrieve
|
||||||
@ -53109,6 +53193,25 @@ components:
|
|||||||
- version_current
|
- version_current
|
||||||
- version_latest
|
- version_latest
|
||||||
- version_latest_valid
|
- version_latest_valid
|
||||||
|
VersionHistory:
|
||||||
|
type: object
|
||||||
|
description: VersionHistory Serializer
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
readOnly: true
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
version:
|
||||||
|
type: string
|
||||||
|
build:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- build
|
||||||
|
- id
|
||||||
|
- timestamp
|
||||||
|
- version
|
||||||
WebAuthnDevice:
|
WebAuthnDevice:
|
||||||
type: object
|
type: object
|
||||||
description: Serializer for WebAuthn authenticator devices
|
description: Serializer for WebAuthn authenticator devices
|
||||||
|
|||||||
Reference in New Issue
Block a user