admin: store version history (#11520)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		 Marc 'risson' Schmitt
					Marc 'risson' Schmitt
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						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