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.system import SystemView | ||||
| from authentik.admin.api.version import VersionView | ||||
| from authentik.admin.api.version_history import VersionHistoryViewSet | ||||
| from authentik.admin.api.workers import WorkerView | ||||
|  | ||||
| api_urlpatterns = [ | ||||
| @ -17,6 +18,7 @@ api_urlpatterns = [ | ||||
|         name="admin_metrics", | ||||
|     ), | ||||
|     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/system/", SystemView.as_view(), name="admin_system"), | ||||
| ] | ||||
|  | ||||
| @ -84,7 +84,9 @@ def run_migrations(): | ||||
|     curr = conn.cursor() | ||||
|     try: | ||||
|         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) | ||||
|             if not spec: | ||||
|                 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: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           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/: | ||||
|     get: | ||||
|       operationId: admin_workers_retrieve | ||||
| @ -53109,6 +53193,25 @@ components: | ||||
|       - version_current | ||||
|       - version_latest | ||||
|       - 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: | ||||
|       type: object | ||||
|       description: Serializer for WebAuthn authenticator devices | ||||
|  | ||||
		Reference in New Issue
	
	Block a user