Merge branch 'master' into stage-challenge
# Conflicts: # authentik/api/v2/urls.py
This commit is contained in:
		
							
								
								
									
										30
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										30
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							| @ -116,18 +116,18 @@ | |||||||
|         }, |         }, | ||||||
|         "boto3": { |         "boto3": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:1709ff5feb363fee7fcaa2330e659fcbc2b4c03a14f75a884ed682ee66011fc4", |                 "sha256:7d44cbd931c653cc68e8ccbf39f3ad8b304cb50d4e964d8c8d0936de33ff8c8b", | ||||||
|                 "sha256:80a761eff3b1cb0798d7e1a41b7c8e6d85c9647a8f7b6105335201a69404caa2" |                 "sha256:b6131751e3cf2f8d4c027518373b6b82264c3897de65d3519e2d782927e8bf1e" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==1.17.10" |             "version": "==1.17.11" | ||||||
|         }, |         }, | ||||||
|         "botocore": { |         "botocore": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:8c84eac6daf38890714e005623083106d68e9b2088e62132fdbf7d2b1228ecbd", |                 "sha256:8efd206b78269eb115279ca2d23f50eead1307dbe0bf9bcc2bba3ab2ff7bfd87", | ||||||
|                 "sha256:a601ee5a4ae66832f328ca362b5404d22b75f1c181f6cc0934f3cfca749eb27d" |                 "sha256:dd7c528c6c936d941b2c267339f0b01cce377b640856240b588d0e0d82fd29e3" | ||||||
|             ], |             ], | ||||||
|             "version": "==1.20.10" |             "version": "==1.20.11" | ||||||
|         }, |         }, | ||||||
|         "cachetools": { |         "cachetools": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -409,11 +409,11 @@ | |||||||
|         }, |         }, | ||||||
|         "docker": { |         "docker": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:20d71afc593486f2297bb7fb7406b03876f31894337e914a5062050c65085cab", |                 "sha256:d4625e70e3d5a12d7cbf1fd68cef2e081ac86b83889e00e5466d975f90e50dad", | ||||||
|                 "sha256:67f33d4cf95182db631a17eef7d666d2c91f624c1d3fbc4df6009cb2f2a4c604" |                 "sha256:de5753b7f6486dd541a98393e423e387579b8974a5068748b83f852cc76a89d6" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==4.4.2" |             "version": "==4.4.3" | ||||||
|         }, |         }, | ||||||
|         "drf-yasg2": { |         "drf-yasg2": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -1093,11 +1093,11 @@ | |||||||
|         }, |         }, | ||||||
|         "sentry-sdk": { |         "sentry-sdk": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:9044b616ec6663cd50794fb3362efd87586e476e7b51ef2439a711d6569aede7", |                 "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237", | ||||||
|                 "sha256:efc65e5ffd38324797a7e1dfc8a4e74b62ea6f56a59df5bb03217b84d58dff6a" |                 "sha256:e75c8c58932bda8cd293ea8e4b242527129e1caaec91433d21b8b2f20fee030b" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==0.20.2" |             "version": "==0.20.3" | ||||||
|         }, |         }, | ||||||
|         "service-identity": { |         "service-identity": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -1123,11 +1123,11 @@ | |||||||
|         }, |         }, | ||||||
|         "structlog": { |         "structlog": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:33dd6bd5f49355e52c1c61bb6a4f20d0b48ce0328cc4a45fe872d38b97a05ccd", |                 "sha256:62f06fc0ee32fb8580f0715eea66cb87271eb7efb0eaf9af6b639cba8981de47", | ||||||
|                 "sha256:af79dfa547d104af8d60f86eac12fb54825f54a46bc998e4504ef66177103174" |                 "sha256:d9d2d890532e8db83c6977a2a676fb1889922ff0c26ad4dc0ecac26f9fafbc57" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==20.2.0" |             "version": "==21.1.0" | ||||||
|         }, |         }, | ||||||
|         "swagger-spec-validator": { |         "swagger-spec-validator": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|  | |||||||
| @ -1,114 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-users"></i> |  | ||||||
|             {% trans 'Groups' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Group users together and give them permissions based on the membership." %} |  | ||||||
|         </p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-modal-button href="{% url 'authentik_admin:group-create' %}"> |  | ||||||
|                         <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                             {% trans 'Create' %} |  | ||||||
|                         </ak-spinner-button> |  | ||||||
|                         <div slot="modal"></div> |  | ||||||
|                     </ak-modal-button> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Name' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Parent' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Members' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for group in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ group.name }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ group.parent }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ group.users.all|length }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:group-update' pk=group.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                 {% trans 'Edit' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:group-delete' pk=group.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                 {% trans 'Delete' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="pf-icon pf-icon-users pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Groups.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any groups." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no group exist. Click the button below to create one.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-modal-button href="{% url 'authentik_admin:group-create' %}"> |  | ||||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                         {% trans 'Create' %} |  | ||||||
|                     </ak-spinner-button> |  | ||||||
|                     <div slot="modal"></div> |  | ||||||
|                 </ak-modal-button> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,153 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load humanize %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon-integration"></i> |  | ||||||
|             {% trans 'Outpost Service-Connections' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Outpost Service-Connections define how authentik connects to external platforms to manage and deploy Outposts." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-dropdown class="pf-c-dropdown"> |  | ||||||
|                         <button class="pf-m-primary pf-c-dropdown__toggle" type="button"> |  | ||||||
|                             <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span> |  | ||||||
|                             <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |  | ||||||
|                         </button> |  | ||||||
|                         <ul class="pf-c-dropdown__menu" hidden> |  | ||||||
|                             {% for type, name in types.items %} |  | ||||||
|                             <li> |  | ||||||
|                                 <ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-create' %}?type={{ type }}"> |  | ||||||
|                                     <button slot="trigger" class="pf-c-dropdown__menu-item"> |  | ||||||
|                                         {{ name|verbose_name }}<br> |  | ||||||
|                                         <small> |  | ||||||
|                                             {{ name|doc }} |  | ||||||
|                                         </small> |  | ||||||
|                                     </button> |  | ||||||
|                                     <div slot="modal"></div> |  | ||||||
|                                 </ak-modal-button> |  | ||||||
|                             </li> |  | ||||||
|                             {% endfor %} |  | ||||||
|                         </ul> |  | ||||||
|                     </ak-dropdown> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Name' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Type' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Local?' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Status' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for sc in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader"> |  | ||||||
|                         <span>{{ sc.name }}</span> |  | ||||||
|                     </th> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ sc|verbose_name }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ sc.local|yesno:"Yes,No" }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {% if sc.state.healthy %} |  | ||||||
|                             <i class="fas fa-check pf-m-success"></i> {{ sc.state.version }} |  | ||||||
|                             {% else %} |  | ||||||
|                             <i class="fas fa-times pf-m-danger"></i> {% trans 'Unhealthy' %} |  | ||||||
|                             {% endif %} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-update' pk=sc.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                 {% trans 'Edit' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-delete' pk=sc.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                 {% trans 'Delete' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="fas fa-map-marker pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Outpost Service Connections.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any outposts." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no service connections exist. Click the button below to create one.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-dropdown class="pf-c-dropdown"> |  | ||||||
|                     <button class="pf-m-primary pf-c-dropdown__toggle" type="button"> |  | ||||||
|                         <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span> |  | ||||||
|                         <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |  | ||||||
|                     </button> |  | ||||||
|                     <ul class="pf-c-dropdown__menu" hidden> |  | ||||||
|                         {% for type, name in types.items %} |  | ||||||
|                         <li> |  | ||||||
|                             <ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-create' %}?type={{ type }}"> |  | ||||||
|                                 <button slot="trigger" class="pf-c-dropdown__menu-item"> |  | ||||||
|                                     {{ name|verbose_name }}<br> |  | ||||||
|                                     <small> |  | ||||||
|                                         {{ name|doc }} |  | ||||||
|                                     </small> |  | ||||||
|                                 </button> |  | ||||||
|                                 <div slot="modal"></div> |  | ||||||
|                             </ak-modal-button> |  | ||||||
|                         </li> |  | ||||||
|                         {% endfor %} |  | ||||||
|                     </ul> |  | ||||||
|                 </ak-dropdown> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,151 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-infrastructure"></i> |  | ||||||
|             {% trans 'Policies' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-dropdown class="pf-c-dropdown"> |  | ||||||
|                         <button class="pf-m-primary pf-c-dropdown__toggle" type="button"> |  | ||||||
|                             <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span> |  | ||||||
|                             <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |  | ||||||
|                         </button> |  | ||||||
|                         <ul class="pf-c-dropdown__menu" hidden> |  | ||||||
|                             {% for type, name in types.items %} |  | ||||||
|                             <li> |  | ||||||
|                                 <ak-modal-button href="{% url 'authentik_admin:policy-create' %}?type={{ type }}"> |  | ||||||
|                                     <button slot="trigger" class="pf-c-dropdown__menu-item"> |  | ||||||
|                                         {{ name|verbose_name }}<br> |  | ||||||
|                                         <small> |  | ||||||
|                                             {{ name|doc }} |  | ||||||
|                                         </small> |  | ||||||
|                                     </button> |  | ||||||
|                                     <div slot="modal"></div> |  | ||||||
|                                 </ak-modal-button> |  | ||||||
|                             </li> |  | ||||||
|                             {% endfor %} |  | ||||||
|                         </ul> |  | ||||||
|                     </ak-dropdown> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Name' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Type' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for policy in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader"> |  | ||||||
|                         <div> |  | ||||||
|                             <div>{{ policy.name }}</div> |  | ||||||
|                             {% if not policy.bindings.exists and not policy.promptstage_set.exists %} |  | ||||||
|                             <i class="pf-icon pf-icon-warning-triangle"></i> |  | ||||||
|                             <small>{% trans 'Warning: Policy is not assigned.' %}</small> |  | ||||||
|                             {% else %} |  | ||||||
|                             <i class="pf-icon pf-icon-ok"></i> |  | ||||||
|                             <small>{% blocktrans with object_count=policy.bindings.all|length %}Assigned to {{ object_count }} objects.{% endblocktrans %}</small> |  | ||||||
|                             {% endif %} |  | ||||||
|                         </div> |  | ||||||
|                     </th> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ policy|verbose_name }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:policy-update' pk=policy.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                 {% trans 'Edit' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:policy-test' pk=policy.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                 {% trans 'Test' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:policy-delete' pk=policy.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                 {% trans 'Delete' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="pf-icon pf-icon-infrastructure pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Policies.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any policies." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no policies exist. Click the button below to create one.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-dropdown class="pf-c-dropdown"> |  | ||||||
|                     <button class="pf-m-primary pf-c-dropdown__toggle" type="button"> |  | ||||||
|                         <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span> |  | ||||||
|                         <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |  | ||||||
|                     </button> |  | ||||||
|                     <ul class="pf-c-dropdown__menu" hidden> |  | ||||||
|                         {% for type, name in types.items %} |  | ||||||
|                         <li> |  | ||||||
|                             <ak-modal-button href="{% url 'authentik_admin:policy-create' %}?type={{ type }}"> |  | ||||||
|                                 <button slot="trigger" class="pf-c-dropdown__menu-item"> |  | ||||||
|                                     {{ name|verbose_name }}<br> |  | ||||||
|                                     <small> |  | ||||||
|                                         {{ name|doc }} |  | ||||||
|                                     </small> |  | ||||||
|                                 </button> |  | ||||||
|                                 <div slot="modal"></div> |  | ||||||
|                             </ak-modal-button> |  | ||||||
|                         </li> |  | ||||||
|                         {% endfor %} |  | ||||||
|                     </ul> |  | ||||||
|                 </ak-dropdown> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,119 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-infrastructure"></i> |  | ||||||
|             {% trans 'Policy Bindings' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Bind existing Policies to Models accepting policies." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-modal-button href="{% url 'authentik_admin:policy-binding-create' %}"> |  | ||||||
|                         <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                             {% trans 'Create' %} |  | ||||||
|                         </ak-spinner-button> |  | ||||||
|                         <div slot="modal"></div> |  | ||||||
|                     </ak-modal-button> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Policy' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Enabled' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Order' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Timeout' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for pbm in object_list %} |  | ||||||
|                     <tr role="role"> |  | ||||||
|                         <td> |  | ||||||
|                             {{ pbm }} |  | ||||||
|                             <small> |  | ||||||
|                                 {{ pbm|fieldtype }} |  | ||||||
|                             </small> |  | ||||||
|                         </td> |  | ||||||
|                         <td></td> |  | ||||||
|                         <td></td> |  | ||||||
|                         <td></td> |  | ||||||
|                         <td></td> |  | ||||||
|                     </tr> |  | ||||||
|                     {% for binding in pbm.bindings %} |  | ||||||
|                     <tr class="row pf-c-table__expandable-row pf-m-expanded"> |  | ||||||
|                         <th role="cell"> |  | ||||||
|                             <div>{{ binding.policy }}</div> |  | ||||||
|                             <small> |  | ||||||
|                                 {{ binding.policy|fieldtype }} |  | ||||||
|                             </small> |  | ||||||
|                         </th> |  | ||||||
|                         <th role="cell"> |  | ||||||
|                             <div>{{ binding.enabled }}</div> |  | ||||||
|                         </th> |  | ||||||
|                         <th role="cell"> |  | ||||||
|                             <div>{{ binding.order }}</div> |  | ||||||
|                         </th> |  | ||||||
|                         <th role="cell"> |  | ||||||
|                             <div>{{ binding.timeout }}</div> |  | ||||||
|                         </th> |  | ||||||
|                         <td> |  | ||||||
|                             <ak-modal-button href="{% url 'authentik_admin:policy-binding-update' pk=binding.pk %}"> |  | ||||||
|                                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                     {% trans 'Edit' %} |  | ||||||
|                                 </ak-spinner-button> |  | ||||||
|                                 <div slot="modal"></div> |  | ||||||
|                             </ak-modal-button> |  | ||||||
|                             <ak-modal-button href="{% url 'authentik_admin:policy-binding-delete' pk=binding.pk %}"> |  | ||||||
|                                 <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                     {% trans 'Delete' %} |  | ||||||
|                                 </ak-spinner-button> |  | ||||||
|                                 <div slot="modal"></div> |  | ||||||
|                             </ak-modal-button> |  | ||||||
|                         </td> |  | ||||||
|                     </tr> |  | ||||||
|                     {% endfor %} |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Policy Bindings.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                     {% trans 'Currently no policy bindings exist. Click the button below to create one.' %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-modal-button href="{% url 'authentik_admin:policy-binding-create' %}"> |  | ||||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                         {% trans 'Create' %} |  | ||||||
|                     </ak-spinner-button> |  | ||||||
|                     <div slot="modal"></div> |  | ||||||
|                 </ak-modal-button> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,143 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-plugged"></i> |  | ||||||
|             {% trans 'Stages' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Stages are single steps of a Flow that a user is guided through." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-dropdown class="pf-c-dropdown"> |  | ||||||
|                         <button class="pf-m-primary pf-c-dropdown__toggle" type="button"> |  | ||||||
|                             <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span> |  | ||||||
|                             <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |  | ||||||
|                         </button> |  | ||||||
|                         <ul class="pf-c-dropdown__menu" hidden> |  | ||||||
|                             {% for type, name in types.items %} |  | ||||||
|                             <li> |  | ||||||
|                                 <ak-modal-button href="{% url 'authentik_admin:stage-create' %}?type={{ type }}"> |  | ||||||
|                                     <button slot="trigger" class="pf-c-dropdown__menu-item"> |  | ||||||
|                                         {{ name|verbose_name }}<br> |  | ||||||
|                                         <small> |  | ||||||
|                                             {{ name|doc }} |  | ||||||
|                                         </small> |  | ||||||
|                                     </button> |  | ||||||
|                                     <div slot="modal"></div> |  | ||||||
|                                 </ak-modal-button> |  | ||||||
|                             </li> |  | ||||||
|                             {% endfor %} |  | ||||||
|                         </ul> |  | ||||||
|                     </ak-dropdown> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Name' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Flows' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for stage in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader"> |  | ||||||
|                         <div> |  | ||||||
|                             <div>{{ stage.name }}</div> |  | ||||||
|                             <small>{{ stage|verbose_name }}</small> |  | ||||||
|                         </div> |  | ||||||
|                     </th> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <ul> |  | ||||||
|                             {% for flow in stage.flow_set.all %} |  | ||||||
|                             <li>{{ flow.slug }}</li> |  | ||||||
|                             {% empty %} |  | ||||||
|                             <li>-</li> |  | ||||||
|                             {% endfor %} |  | ||||||
|                         </ul> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:stage-update' pk=stage.stage_uuid %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                 {% trans 'Edit' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:stage-delete' pk=stage.stage_uuid %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                 {% trans 'Delete' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="pf-icon pf-icon-plugged pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Stages.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any stages." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no stages exist. Click the button below to create one.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-dropdown class="pf-c-dropdown"> |  | ||||||
|                     <button class="pf-m-primary pf-c-dropdown__toggle" type="button"> |  | ||||||
|                         <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span> |  | ||||||
|                         <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |  | ||||||
|                     </button> |  | ||||||
|                     <ul class="pf-c-dropdown__menu" hidden> |  | ||||||
|                         {% for type, name in types.items %} |  | ||||||
|                         <li> |  | ||||||
|                             <ak-modal-button href="{% url 'authentik_admin:stage-create' %}?type={{ type }}"> |  | ||||||
|                                 <button slot="trigger" class="pf-c-dropdown__menu-item"> |  | ||||||
|                                     {{ name|verbose_name }}<br> |  | ||||||
|                                     <small> |  | ||||||
|                                         {{ name|doc }} |  | ||||||
|                                     </small> |  | ||||||
|                                 </button> |  | ||||||
|                                 <div slot="modal"></div> |  | ||||||
|                             </ak-modal-button> |  | ||||||
|                         </li> |  | ||||||
|                         {% endfor %} |  | ||||||
|                     </ul> |  | ||||||
|                 </ak-dropdown> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,125 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-infrastructure"></i> |  | ||||||
|             {% trans 'Stage Bindings' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Bind existing Stages to Flows." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-modal-button href="{% url 'authentik_admin:stage-binding-create' %}"> |  | ||||||
|                         <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                             {% trans 'Create' %} |  | ||||||
|                         </ak-spinner-button> |  | ||||||
|                         <div slot="modal"></div> |  | ||||||
|                     </ak-modal-button> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Order' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Name' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Stage Type' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% regroup object_list by target as grouped_bindings %} |  | ||||||
|                 {% for flow in grouped_bindings %} |  | ||||||
|                     <tr role="role"> |  | ||||||
|                         <td> |  | ||||||
|                             {% blocktrans with slug=flow.grouper.slug %} |  | ||||||
|                             Flow {{ slug }} |  | ||||||
|                             {% endblocktrans %} |  | ||||||
|                         </td> |  | ||||||
|                         <td></td> |  | ||||||
|                         <td></td> |  | ||||||
|                         <td></td> |  | ||||||
|                     </tr> |  | ||||||
|                     {% for binding in flow.list %} |  | ||||||
|                     <tr class="pf-c-table__expandable-row pf-m-expanded" role="row"> |  | ||||||
|                         <td role="cell"> |  | ||||||
|                             <span> |  | ||||||
|                                 {{ binding.order }} |  | ||||||
|                             </span> |  | ||||||
|                         </td> |  | ||||||
|                         <th role="columnheader"> |  | ||||||
|                             <div> |  | ||||||
|                                 <div>{{ binding.target.slug }}</div> |  | ||||||
|                                 <small> |  | ||||||
|                                     {{ binding.target.name }} |  | ||||||
|                                 </small> |  | ||||||
|                             </div> |  | ||||||
|                         </th> |  | ||||||
|                         <td role="cell"> |  | ||||||
|                             <div> |  | ||||||
|                                 <div> |  | ||||||
|                                     {{ binding.stage.name }} |  | ||||||
|                                 </div> |  | ||||||
|                                 <small> |  | ||||||
|                                     {{ binding.stage }} |  | ||||||
|                                 </small> |  | ||||||
|                             </div> |  | ||||||
|                         </td> |  | ||||||
|                         <td> |  | ||||||
|                             <ak-modal-button href="{% url 'authentik_admin:stage-binding-update' pk=binding.pk %}"> |  | ||||||
|                                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                     {% trans 'Update' %} |  | ||||||
|                                 </ak-spinner-button> |  | ||||||
|                                 <div slot="modal"></div> |  | ||||||
|                             </ak-modal-button> |  | ||||||
|                             <ak-modal-button href="{% url 'authentik_admin:stage-binding-delete' pk=binding.pk %}"> |  | ||||||
|                                 <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                     {% trans 'Delete' %} |  | ||||||
|                                 </ak-spinner-button> |  | ||||||
|                                 <div slot="modal"></div> |  | ||||||
|                             </ak-modal-button> |  | ||||||
|                         </td> |  | ||||||
|                     </tr> |  | ||||||
|                     {% endfor %} |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Flow-Stage Bindings.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                     {% trans 'Currently no flow-stage bindings exist. Click the button below to create one.' %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-modal-button href="{% url 'authentik_admin:stage-binding-create' %}"> |  | ||||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                         {% trans 'Create' %} |  | ||||||
|                     </ak-spinner-button> |  | ||||||
|                     <div slot="modal"></div> |  | ||||||
|                 </ak-modal-button> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,109 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-migration"></i> |  | ||||||
|             {% trans 'Invitations' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Create Invitation Links to enroll Users, and optionally force specific attributes of their account." %} |  | ||||||
|         </p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-modal-button href="{% url 'authentik_admin:stage-invitation-create' %}"> |  | ||||||
|                         <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                             {% trans 'Create' %} |  | ||||||
|                         </ak-spinner-button> |  | ||||||
|                         <div slot="modal"></div> |  | ||||||
|                     </ak-modal-button> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'ID' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Created by' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Expiry' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for invitation in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ invitation.invite_uuid }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ invitation.created_by }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ invitation.expiry|default:"-" }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:stage-invitation-delete' pk=invitation.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                 {% trans 'Delete' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="pf-icon pf-icon-migration pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Invitations.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any invitations." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no invitations exist. Click the button below to create one.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-modal-button href="{% url 'authentik_admin:stage-invitation-create' %}"> |  | ||||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                         {% trans 'Create' %} |  | ||||||
|                     </ak-spinner-button> |  | ||||||
|                     <div slot="modal"></div> |  | ||||||
|                 </ak-modal-button> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,125 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-plugged"></i> |  | ||||||
|             {% trans 'Prompts' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Single Prompts that can be used for Prompt Stages." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-modal-button href="{% url 'authentik_admin:stage-prompt-create' %}"> |  | ||||||
|                         <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                             {% trans 'Create' %} |  | ||||||
|                         </ak-spinner-button> |  | ||||||
|                         <div slot="modal"></div> |  | ||||||
|                     </ak-modal-button> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Field' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Label' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Type' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Order' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Flows' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for prompt in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader"> |  | ||||||
|                         <div> |  | ||||||
|                             <div>{{ prompt.field_key }}</div> |  | ||||||
|                         </div> |  | ||||||
|                     </th> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <div> |  | ||||||
|                             {{ prompt.label }} |  | ||||||
|                         </div> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <div> |  | ||||||
|                             {{ prompt.type }} |  | ||||||
|                         </div> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <div> |  | ||||||
|                             {{ prompt.order }} |  | ||||||
|                         </div> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <ul> |  | ||||||
|                             {% for flow in prompt.flow_set.all %} |  | ||||||
|                             <li>{{ flow.slug }}</li> |  | ||||||
|                             {% empty %} |  | ||||||
|                             <li>-</li> |  | ||||||
|                             {% endfor %} |  | ||||||
|                         </ul> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:stage-prompt-update' pk=prompt.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                 {% trans 'Update' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:stage-prompt-delete' pk=prompt.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                 {% trans 'Delete' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="pf-icon pf-icon-plugged pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Stage Prompts.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any stage prompts." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no stage prompts exist. Click the button below to create one.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|                 <a href="{% url 'authentik_admin:stage-prompt-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,84 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load humanize %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-automation"></i> |  | ||||||
|             {% trans 'System Tasks' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Long-running operations which authentik executes in the background." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                     {% trans 'Refresh' %} |  | ||||||
|                 </button> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Identifier' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Description' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Last Run' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Status' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Messages' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for task in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader"> |  | ||||||
|                         <span>{{ task.html_name|join:"_­" }}</span> |  | ||||||
|                     </th> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ task.task_description }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ task.finish_timestamp|naturaltime }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {% if task.result.status == task_successful %} |  | ||||||
|                             <i class="fas fa-check pf-m-success"></i> {% trans 'Successful' %} |  | ||||||
|                             {% elif task.result.status == task_warning %} |  | ||||||
|                             <i class="fas fa-exclamation-triangle pf-m-warning"></i> {% trans 'Warning' %} |  | ||||||
|                             {% elif task.result.status == task_error %} |  | ||||||
|                             <i class="fas fa-times pf-m-danger"></i> {% trans 'Error' %} |  | ||||||
|                             {% else %} |  | ||||||
|                             <i class="fas fa-question-circle"></i> {% trans 'Unknown' %} |  | ||||||
|                             {% endif %} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         {% for message in task.result.messages %} |  | ||||||
|                         <div> |  | ||||||
|                             {{ message }} |  | ||||||
|                         </div> |  | ||||||
|                         {% endfor %} |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-action-button url="{% url 'authentik_api:admin_system_tasks-retry' pk=task.task_name %}"> |  | ||||||
|                             {% trans 'Retry Task' %} |  | ||||||
|                         </ak-action-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,102 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-security"></i> |  | ||||||
|             {% trans 'Tokens' %} |  | ||||||
|         </h1> |  | ||||||
|         <p>{% trans "Tokens are used throughout authentik for Email validation stages, Recovery keys and API access." %}</p> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Identifier' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'User' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Expires?' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Expiry Date' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for token in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader"> |  | ||||||
|                         <div>{{ token.identifier }}</div> |  | ||||||
|                     </th> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ token.user }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ token.expiring|yesno:"Yes,No" }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {% if not token.expiring %} |  | ||||||
|                             - |  | ||||||
|                             {% else %} |  | ||||||
|                             {{ token.expires }} |  | ||||||
|                             {% endif %} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:token-delete' pk=token.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-danger"> |  | ||||||
|                                 {% trans 'Delete' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         <ak-token-copy-button identifier="{{ token.identifier }}"> |  | ||||||
|                             {% trans 'Copy token' %} |  | ||||||
|                         </ak-token-copy-button> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="fas fa-key pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Tokens.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any token." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no tokens exist.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -1,125 +0,0 @@ | |||||||
| {% extends "administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} |  | ||||||
| {% load authentik_utils %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> |  | ||||||
|     <div class="pf-c-content"> |  | ||||||
|         <h1> |  | ||||||
|             <i class="pf-icon pf-icon-user"></i> |  | ||||||
|             {% trans 'Users' %} |  | ||||||
|         </h1> |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| <section class="pf-c-page__main-section pf-m-no-padding-mobile"> |  | ||||||
|     <div class="pf-c-card"> |  | ||||||
|         {% if object_list %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|                 <div class="pf-c-toolbar__bulk-select"> |  | ||||||
|                     <ak-modal-button href="{% url 'authentik_admin:user-create' %}"> |  | ||||||
|                         <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                             {% trans 'Create' %} |  | ||||||
|                         </ak-spinner-button> |  | ||||||
|                         <div slot="modal"></div> |  | ||||||
|                     </ak-modal-button> |  | ||||||
|                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> |  | ||||||
|                         {% trans 'Refresh' %} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|                 {% include 'partials/pagination.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> |  | ||||||
|             <thead> |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Name' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Active' %}</th> |  | ||||||
|                     <th role="columnheader" scope="col">{% trans 'Last Login' %}</th> |  | ||||||
|                     <th role="cell"></th> |  | ||||||
|                 </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody role="rowgroup"> |  | ||||||
|                 {% for user in object_list %} |  | ||||||
|                 <tr role="row"> |  | ||||||
|                     <th role="columnheader"> |  | ||||||
|                         <div> |  | ||||||
|                             <div>{{ user.username }}</div> |  | ||||||
|                             <small>{{ user.name }}</small> |  | ||||||
|                         </div> |  | ||||||
|                     </th> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ user.is_active }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td role="cell"> |  | ||||||
|                         <span> |  | ||||||
|                             {{ user.last_login }} |  | ||||||
|                         </span> |  | ||||||
|                     </td> |  | ||||||
|                     <td> |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:user-update' pk=user.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-secondary"> |  | ||||||
|                                 {% trans 'Edit' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         {% if user.is_active %} |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:user-disable' pk=user.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-warning"> |  | ||||||
|                                 {% trans 'Disable' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         {% else %} |  | ||||||
|                         <ak-modal-button href="{% url 'authentik_admin:user-delete' pk=user.pk %}"> |  | ||||||
|                             <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                                 {% trans 'Enable' %} |  | ||||||
|                             </ak-spinner-button> |  | ||||||
|                             <div slot="modal"></div> |  | ||||||
|                         </ak-modal-button> |  | ||||||
|                         {% endif %} |  | ||||||
|                         <a class="pf-c-button pf-m-tertiary ak-root-link" href="{% url 'authentik_admin:user-password-reset' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Reset Password' %}</a> |  | ||||||
|                         <a class="pf-c-button pf-m-tertiary ak-root-link" href="{% url 'authentik_core:impersonate-init' user_id=user.pk %}">{% trans 'Impersonate' %}</a> |  | ||||||
|                     </td> |  | ||||||
|                 </tr> |  | ||||||
|                 {% endfor %} |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|         <div class="pf-c-pagination pf-m-bottom"> |  | ||||||
|             {% include 'partials/pagination.html' %} |  | ||||||
|         </div> |  | ||||||
|         {% else %} |  | ||||||
|         <div class="pf-c-toolbar"> |  | ||||||
|             <div class="pf-c-toolbar__content"> |  | ||||||
|                 {% include 'partials/toolbar_search.html' %} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <div class="pf-c-empty-state"> |  | ||||||
|             <div class="pf-c-empty-state__content"> |  | ||||||
|                 <i class="pf-icon pf-icon-user pf-c-empty-state__icon" aria-hidden="true"></i> |  | ||||||
|                 <h1 class="pf-c-title pf-m-lg"> |  | ||||||
|                     {% trans 'No Users.' %} |  | ||||||
|                 </h1> |  | ||||||
|                 <div class="pf-c-empty-state__body"> |  | ||||||
|                 {% if request.GET.search != "" %} |  | ||||||
|                     {% trans "Your search query doesn't match any users." %} |  | ||||||
|                 {% else %} |  | ||||||
|                     {% trans 'Currently no users exist. How did you even get here.' %} |  | ||||||
|                 {% endif %} |  | ||||||
|                 </div> |  | ||||||
|                 <ak-modal-button href="{% url 'authentik_admin:user-create' %}"> |  | ||||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> |  | ||||||
|                         {% trans 'Create' %} |  | ||||||
|                     </ak-spinner-button> |  | ||||||
|                     <div slot="modal"></div> |  | ||||||
|                 </ak-modal-button> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
| {% endblock %} |  | ||||||
| @ -20,7 +20,6 @@ from authentik.admin.views import ( | |||||||
|     stages_bindings, |     stages_bindings, | ||||||
|     stages_invitations, |     stages_invitations, | ||||||
|     stages_prompts, |     stages_prompts, | ||||||
|     tasks, |  | ||||||
|     tokens, |     tokens, | ||||||
|     users, |     users, | ||||||
| ) | ) | ||||||
| @ -54,7 +53,6 @@ urlpatterns = [ | |||||||
|         name="application-delete", |         name="application-delete", | ||||||
|     ), |     ), | ||||||
|     # Tokens |     # Tokens | ||||||
|     path("tokens/", tokens.TokenListView.as_view(), name="tokens"), |  | ||||||
|     path( |     path( | ||||||
|         "tokens/<uuid:pk>/delete/", |         "tokens/<uuid:pk>/delete/", | ||||||
|         tokens.TokenDeleteView.as_view(), |         tokens.TokenDeleteView.as_view(), | ||||||
| @ -73,7 +71,6 @@ urlpatterns = [ | |||||||
|         name="source-delete", |         name="source-delete", | ||||||
|     ), |     ), | ||||||
|     # Policies |     # Policies | ||||||
|     path("policies/", policies.PolicyListView.as_view(), name="policies"), |  | ||||||
|     path("policies/create/", policies.PolicyCreateView.as_view(), name="policy-create"), |     path("policies/create/", policies.PolicyCreateView.as_view(), name="policy-create"), | ||||||
|     path( |     path( | ||||||
|         "policies/<uuid:pk>/update/", |         "policies/<uuid:pk>/update/", | ||||||
| @ -91,11 +88,6 @@ urlpatterns = [ | |||||||
|         name="policy-test", |         name="policy-test", | ||||||
|     ), |     ), | ||||||
|     # Policy bindings |     # Policy bindings | ||||||
|     path( |  | ||||||
|         "policies/bindings/", |  | ||||||
|         policies_bindings.PolicyBindingListView.as_view(), |  | ||||||
|         name="policies-bindings", |  | ||||||
|     ), |  | ||||||
|     path( |     path( | ||||||
|         "policies/bindings/create/", |         "policies/bindings/create/", | ||||||
|         policies_bindings.PolicyBindingCreateView.as_view(), |         policies_bindings.PolicyBindingCreateView.as_view(), | ||||||
| @ -133,7 +125,6 @@ urlpatterns = [ | |||||||
|         name="provider-delete", |         name="provider-delete", | ||||||
|     ), |     ), | ||||||
|     # Stages |     # Stages | ||||||
|     path("stages/", stages.StageListView.as_view(), name="stages"), |  | ||||||
|     path("stages/create/", stages.StageCreateView.as_view(), name="stage-create"), |     path("stages/create/", stages.StageCreateView.as_view(), name="stage-create"), | ||||||
|     path( |     path( | ||||||
|         "stages/<uuid:pk>/update/", |         "stages/<uuid:pk>/update/", | ||||||
| @ -146,11 +137,6 @@ urlpatterns = [ | |||||||
|         name="stage-delete", |         name="stage-delete", | ||||||
|     ), |     ), | ||||||
|     # Stage bindings |     # Stage bindings | ||||||
|     path( |  | ||||||
|         "stages/bindings/", |  | ||||||
|         stages_bindings.StageBindingListView.as_view(), |  | ||||||
|         name="stage-bindings", |  | ||||||
|     ), |  | ||||||
|     path( |     path( | ||||||
|         "stages/bindings/create/", |         "stages/bindings/create/", | ||||||
|         stages_bindings.StageBindingCreateView.as_view(), |         stages_bindings.StageBindingCreateView.as_view(), | ||||||
| @ -167,11 +153,6 @@ urlpatterns = [ | |||||||
|         name="stage-binding-delete", |         name="stage-binding-delete", | ||||||
|     ), |     ), | ||||||
|     # Stage Prompts |     # Stage Prompts | ||||||
|     path( |  | ||||||
|         "stages_prompts/", |  | ||||||
|         stages_prompts.PromptListView.as_view(), |  | ||||||
|         name="stage-prompts", |  | ||||||
|     ), |  | ||||||
|     path( |     path( | ||||||
|         "stages_prompts/create/", |         "stages_prompts/create/", | ||||||
|         stages_prompts.PromptCreateView.as_view(), |         stages_prompts.PromptCreateView.as_view(), | ||||||
| @ -188,11 +169,6 @@ urlpatterns = [ | |||||||
|         name="stage-prompt-delete", |         name="stage-prompt-delete", | ||||||
|     ), |     ), | ||||||
|     # Stage Invitations |     # Stage Invitations | ||||||
|     path( |  | ||||||
|         "stages/invitations/", |  | ||||||
|         stages_invitations.InvitationListView.as_view(), |  | ||||||
|         name="stage-invitations", |  | ||||||
|     ), |  | ||||||
|     path( |     path( | ||||||
|         "stages/invitations/create/", |         "stages/invitations/create/", | ||||||
|         stages_invitations.InvitationCreateView.as_view(), |         stages_invitations.InvitationCreateView.as_view(), | ||||||
| @ -256,7 +232,6 @@ urlpatterns = [ | |||||||
|         name="property-mapping-test", |         name="property-mapping-test", | ||||||
|     ), |     ), | ||||||
|     # Users |     # Users | ||||||
|     path("users/", users.UserListView.as_view(), name="users"), |  | ||||||
|     path("users/create/", users.UserCreateView.as_view(), name="user-create"), |     path("users/create/", users.UserCreateView.as_view(), name="user-create"), | ||||||
|     path("users/<int:pk>/update/", users.UserUpdateView.as_view(), name="user-update"), |     path("users/<int:pk>/update/", users.UserUpdateView.as_view(), name="user-update"), | ||||||
|     path("users/<int:pk>/delete/", users.UserDeleteView.as_view(), name="user-delete"), |     path("users/<int:pk>/delete/", users.UserDeleteView.as_view(), name="user-delete"), | ||||||
| @ -270,7 +245,6 @@ urlpatterns = [ | |||||||
|         name="user-password-reset", |         name="user-password-reset", | ||||||
|     ), |     ), | ||||||
|     # Groups |     # Groups | ||||||
|     path("groups/", groups.GroupListView.as_view(), name="groups"), |  | ||||||
|     path("groups/create/", groups.GroupCreateView.as_view(), name="group-create"), |     path("groups/create/", groups.GroupCreateView.as_view(), name="group-create"), | ||||||
|     path( |     path( | ||||||
|         "groups/<uuid:pk>/update/", |         "groups/<uuid:pk>/update/", | ||||||
| @ -320,11 +294,6 @@ urlpatterns = [ | |||||||
|         name="outpost-delete", |         name="outpost-delete", | ||||||
|     ), |     ), | ||||||
|     # Outpost Service Connections |     # Outpost Service Connections | ||||||
|     path( |  | ||||||
|         "outpost_service_connections/", |  | ||||||
|         outposts_service_connections.OutpostServiceConnectionListView.as_view(), |  | ||||||
|         name="outpost-service-connections", |  | ||||||
|     ), |  | ||||||
|     path( |     path( | ||||||
|         "outpost_service_connections/create/", |         "outpost_service_connections/create/", | ||||||
|         outposts_service_connections.OutpostServiceConnectionCreateView.as_view(), |         outposts_service_connections.OutpostServiceConnectionCreateView.as_view(), | ||||||
| @ -340,12 +309,6 @@ urlpatterns = [ | |||||||
|         outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(), |         outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(), | ||||||
|         name="outpost-service-connection-delete", |         name="outpost-service-connection-delete", | ||||||
|     ), |     ), | ||||||
|     # Tasks |  | ||||||
|     path( |  | ||||||
|         "tasks/", |  | ||||||
|         tasks.TaskListView.as_view(), |  | ||||||
|         name="tasks", |  | ||||||
|     ), |  | ||||||
|     # Event Notification Transpots |     # Event Notification Transpots | ||||||
|     path( |     path( | ||||||
|         "events/transports/create/", |         "events/transports/create/", | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ from django.views.generic import UpdateView | |||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView | from authentik.admin.views.utils import DeleteMessageView | ||||||
| from authentik.core.forms.applications import ApplicationForm | from authentik.core.forms.applications import ApplicationForm | ||||||
| from authentik.core.models import Application | from authentik.core.models import Application | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| @ -19,7 +19,6 @@ from authentik.lib.views import CreateAssignPermView | |||||||
|  |  | ||||||
| class ApplicationCreateView( | class ApplicationCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -52,7 +51,6 @@ class ApplicationCreateView( | |||||||
|  |  | ||||||
| class ApplicationUpdateView( | class ApplicationUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ from django.views.generic import UpdateView | |||||||
| from django.views.generic.edit import FormView | from django.views.generic.edit import FormView | ||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView | from authentik.admin.views.utils import DeleteMessageView | ||||||
| from authentik.crypto.builder import CertificateBuilder | from authentik.crypto.builder import CertificateBuilder | ||||||
| from authentik.crypto.forms import ( | from authentik.crypto.forms import ( | ||||||
|     CertificateKeyPairForm, |     CertificateKeyPairForm, | ||||||
| @ -22,7 +22,6 @@ from authentik.lib.views import CreateAssignPermView | |||||||
|  |  | ||||||
| class CertificateKeyPairCreateView( | class CertificateKeyPairCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -40,7 +39,6 @@ class CertificateKeyPairCreateView( | |||||||
|  |  | ||||||
| class CertificateKeyPairGenerateView( | class CertificateKeyPairGenerateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     FormView, |     FormView, | ||||||
| @ -68,7 +66,6 @@ class CertificateKeyPairGenerateView( | |||||||
|  |  | ||||||
| class CertificateKeyPairUpdateView( | class CertificateKeyPairUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ from django.utils.translation import gettext as _ | |||||||
| from django.views.generic import UpdateView | from django.views.generic import UpdateView | ||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView | from authentik.admin.views.utils import DeleteMessageView | ||||||
| from authentik.events.forms import NotificationRuleForm | from authentik.events.forms import NotificationRuleForm | ||||||
| from authentik.events.models import NotificationRule | from authentik.events.models import NotificationRule | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| @ -16,7 +16,6 @@ from authentik.lib.views import CreateAssignPermView | |||||||
|  |  | ||||||
| class NotificationRuleCreateView( | class NotificationRuleCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -33,7 +32,6 @@ class NotificationRuleCreateView( | |||||||
|  |  | ||||||
| class NotificationRuleUpdateView( | class NotificationRuleUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ from django.utils.translation import gettext as _ | |||||||
| from django.views.generic import UpdateView | from django.views.generic import UpdateView | ||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView | from authentik.admin.views.utils import DeleteMessageView | ||||||
| from authentik.events.forms import NotificationTransportForm | from authentik.events.forms import NotificationTransportForm | ||||||
| from authentik.events.models import NotificationTransport | from authentik.events.models import NotificationTransport | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| @ -16,7 +16,6 @@ from authentik.lib.views import CreateAssignPermView | |||||||
|  |  | ||||||
| class NotificationTransportCreateView( | class NotificationTransportCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -33,7 +32,6 @@ class NotificationTransportCreateView( | |||||||
|  |  | ||||||
| class NotificationTransportUpdateView( | class NotificationTransportUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ from django.utils.translation import gettext as _ | |||||||
| from django.views.generic import DetailView, FormView, UpdateView | from django.views.generic import DetailView, FormView, UpdateView | ||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView | from authentik.admin.views.utils import DeleteMessageView | ||||||
| from authentik.flows.exceptions import FlowNonApplicableException | from authentik.flows.exceptions import FlowNonApplicableException | ||||||
| from authentik.flows.forms import FlowForm, FlowImportForm | from authentik.flows.forms import FlowForm, FlowImportForm | ||||||
| from authentik.flows.models import Flow | from authentik.flows.models import Flow | ||||||
| @ -25,7 +25,6 @@ from authentik.lib.views import CreateAssignPermView, bad_request_message | |||||||
|  |  | ||||||
| class FlowCreateView( | class FlowCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -43,7 +42,6 @@ class FlowCreateView( | |||||||
|  |  | ||||||
| class FlowUpdateView( | class FlowUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
|  | |||||||
| @ -4,41 +4,18 @@ from django.contrib.auth.mixins import ( | |||||||
|     PermissionRequiredMixin as DjangoPermissionRequiredMixin, |     PermissionRequiredMixin as DjangoPermissionRequiredMixin, | ||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import ListView, UpdateView | from django.views.generic import UpdateView | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |  | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.core.forms.groups import GroupForm | from authentik.core.forms.groups import GroupForm | ||||||
| from authentik.core.models import Group | from authentik.core.models import Group | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     ListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all groups""" |  | ||||||
|  |  | ||||||
|     model = Group |  | ||||||
|     permission_required = "authentik_core.view_group" |  | ||||||
|     ordering = "name" |  | ||||||
|     template_name = "administration/group/list.html" |  | ||||||
|     search_fields = ["name", "attributes"] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupCreateView( | class GroupCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -50,13 +27,12 @@ class GroupCreateView( | |||||||
|     permission_required = "authentik_core.add_group" |     permission_required = "authentik_core.add_group" | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:groups") |     success_url = "/" | ||||||
|     success_message = _("Successfully created Group") |     success_message = _("Successfully created Group") | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupUpdateView( | class GroupUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
| @ -68,7 +44,7 @@ class GroupUpdateView( | |||||||
|     permission_required = "authentik_core.change_group" |     permission_required = "authentik_core.change_group" | ||||||
|  |  | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:groups") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated Group") |     success_message = _("Successfully updated Group") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -79,5 +55,5 @@ class GroupDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage | |||||||
|     permission_required = "authentik_flows.delete_group" |     permission_required = "authentik_flows.delete_group" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:groups") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted Group") |     success_message = _("Successfully deleted Group") | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ from django.utils.translation import gettext as _ | |||||||
| from django.views.generic import UpdateView | from django.views.generic import UpdateView | ||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView | from authentik.admin.views.utils import DeleteMessageView | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| from authentik.outposts.forms import OutpostForm | from authentik.outposts.forms import OutpostForm | ||||||
| from authentik.outposts.models import Outpost, OutpostConfig | from authentik.outposts.models import Outpost, OutpostConfig | ||||||
| @ -19,7 +19,6 @@ from authentik.outposts.models import Outpost, OutpostConfig | |||||||
|  |  | ||||||
| class OutpostCreateView( | class OutpostCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -43,7 +42,6 @@ class OutpostCreateView( | |||||||
|  |  | ||||||
| class OutpostUpdateView( | class OutpostUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
|  | |||||||
| @ -4,41 +4,19 @@ from django.contrib.auth.mixins import ( | |||||||
|     PermissionRequiredMixin as DjangoPermissionRequiredMixin, |     PermissionRequiredMixin as DjangoPermissionRequiredMixin, | ||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import ( | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |     DeleteMessageView, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
|     InheritanceListView, |  | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) | ) | ||||||
| from authentik.outposts.models import OutpostServiceConnection | from authentik.outposts.models import OutpostServiceConnection | ||||||
|  |  | ||||||
|  |  | ||||||
| class OutpostServiceConnectionListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     InheritanceListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all outpost-service-connections""" |  | ||||||
|  |  | ||||||
|     model = OutpostServiceConnection |  | ||||||
|     permission_required = "authentik_outposts.add_outpostserviceconnection" |  | ||||||
|     template_name = "administration/outpost_service_connection/list.html" |  | ||||||
|     ordering = "pk" |  | ||||||
|     search_fields = ["pk", "name"] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class OutpostServiceConnectionCreateView( | class OutpostServiceConnectionCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
| @ -49,13 +27,12 @@ class OutpostServiceConnectionCreateView( | |||||||
|     permission_required = "authentik_outposts.add_outpostserviceconnection" |     permission_required = "authentik_outposts.add_outpostserviceconnection" | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:outpost-service-connections") |     success_url = "/" | ||||||
|     success_message = _("Successfully created Outpost Service Connection") |     success_message = _("Successfully created Outpost Service Connection") | ||||||
|  |  | ||||||
|  |  | ||||||
| class OutpostServiceConnectionUpdateView( | class OutpostServiceConnectionUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
| @ -66,7 +43,7 @@ class OutpostServiceConnectionUpdateView( | |||||||
|     permission_required = "authentik_outposts.change_outpostserviceconnection" |     permission_required = "authentik_outposts.change_outpostserviceconnection" | ||||||
|  |  | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:outpost-service-connections") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated Outpost Service Connection") |     success_message = _("Successfully updated Outpost Service Connection") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -79,5 +56,5 @@ class OutpostServiceConnectionDeleteView( | |||||||
|     permission_required = "authentik_outposts.delete_outpostserviceconnection" |     permission_required = "authentik_outposts.delete_outpostserviceconnection" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:outpost-service-connections") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted Outpost Service Connection") |     success_message = _("Successfully deleted Outpost Service Connection") | ||||||
|  | |||||||
| @ -7,45 +7,23 @@ from django.contrib.auth.mixins import ( | |||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.http import HttpResponse | from django.http import HttpResponse | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import FormView | from django.views.generic import FormView | ||||||
| from django.views.generic.detail import DetailView | from django.views.generic.detail import DetailView | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.forms.policies import PolicyTestForm | from authentik.admin.forms.policies import PolicyTestForm | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import ( | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |     DeleteMessageView, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
|     InheritanceListView, |  | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) | ) | ||||||
| from authentik.policies.models import Policy, PolicyBinding | from authentik.policies.models import Policy, PolicyBinding | ||||||
| from authentik.policies.process import PolicyProcess, PolicyRequest | from authentik.policies.process import PolicyProcess, PolicyRequest | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     InheritanceListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all policies""" |  | ||||||
|  |  | ||||||
|     model = Policy |  | ||||||
|     permission_required = "authentik_policies.view_policy" |  | ||||||
|     ordering = "name" |  | ||||||
|     template_name = "administration/policy/list.html" |  | ||||||
|     search_fields = ["name"] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyCreateView( | class PolicyCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
| @ -56,13 +34,12 @@ class PolicyCreateView( | |||||||
|     permission_required = "authentik_policies.add_policy" |     permission_required = "authentik_policies.add_policy" | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:policies") |     success_url = "/" | ||||||
|     success_message = _("Successfully created Policy") |     success_message = _("Successfully created Policy") | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyUpdateView( | class PolicyUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
| @ -73,7 +50,7 @@ class PolicyUpdateView( | |||||||
|     permission_required = "authentik_policies.change_policy" |     permission_required = "authentik_policies.change_policy" | ||||||
|  |  | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:policies") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated Policy") |     success_message = _("Successfully updated Policy") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -84,7 +61,7 @@ class PolicyDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag | |||||||
|     permission_required = "authentik_policies.delete_policy" |     permission_required = "authentik_policies.delete_policy" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:policies") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted Policy") |     success_message = _("Successfully deleted Policy") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -6,55 +6,19 @@ from django.contrib.auth.mixins import ( | |||||||
|     PermissionRequiredMixin as DjangoPermissionRequiredMixin, |     PermissionRequiredMixin as DjangoPermissionRequiredMixin, | ||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.db.models import Max, QuerySet | from django.db.models import Max | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import ListView, UpdateView | from django.views.generic import UpdateView | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
| from guardian.shortcuts import get_objects_for_user |  | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| from authentik.policies.forms import PolicyBindingForm | from authentik.policies.forms import PolicyBindingForm | ||||||
| from authentik.policies.models import PolicyBinding, PolicyBindingModel | from authentik.policies.models import PolicyBinding, PolicyBindingModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyBindingListView( |  | ||||||
|     LoginRequiredMixin, PermissionListMixin, UserPaginateListMixin, ListView |  | ||||||
| ): |  | ||||||
|     """Show list of all policies""" |  | ||||||
|  |  | ||||||
|     model = PolicyBinding |  | ||||||
|     permission_required = "authentik_policies.view_policybinding" |  | ||||||
|     ordering = ["order", "target"] |  | ||||||
|     template_name = "administration/policy_binding/list.html" |  | ||||||
|  |  | ||||||
|     def get_queryset(self) -> QuerySet: |  | ||||||
|         # Since `select_subclasses` does not work with a foreign key, we have to do two queries here |  | ||||||
|         # First, get all pbm objects that have bindings attached |  | ||||||
|         objects = ( |  | ||||||
|             get_objects_for_user( |  | ||||||
|                 self.request.user, "authentik_policies.view_policybindingmodel" |  | ||||||
|             ) |  | ||||||
|             .filter(policies__isnull=False) |  | ||||||
|             .select_subclasses() |  | ||||||
|             .select_related() |  | ||||||
|             .order_by("pk") |  | ||||||
|         ) |  | ||||||
|         for pbm in objects: |  | ||||||
|             pbm.bindings = get_objects_for_user( |  | ||||||
|                 self.request.user, self.permission_required |  | ||||||
|             ).filter(target__pk=pbm.pbm_uuid) |  | ||||||
|         return objects |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyBindingCreateView( | class PolicyBindingCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -66,7 +30,7 @@ class PolicyBindingCreateView( | |||||||
|     form_class = PolicyBindingForm |     form_class = PolicyBindingForm | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:policies-bindings") |     success_url = "/" | ||||||
|     success_message = _("Successfully created PolicyBinding") |     success_message = _("Successfully created PolicyBinding") | ||||||
|  |  | ||||||
|     def get_initial(self) -> dict[str, Any]: |     def get_initial(self) -> dict[str, Any]: | ||||||
| @ -88,7 +52,6 @@ class PolicyBindingCreateView( | |||||||
|  |  | ||||||
| class PolicyBindingUpdateView( | class PolicyBindingUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
| @ -100,7 +63,7 @@ class PolicyBindingUpdateView( | |||||||
|     form_class = PolicyBindingForm |     form_class = PolicyBindingForm | ||||||
|  |  | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:policies-bindings") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated PolicyBinding") |     success_message = _("Successfully updated PolicyBinding") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -113,5 +76,5 @@ class PolicyBindingDeleteView( | |||||||
|     permission_required = "authentik_policies.delete_policybinding" |     permission_required = "authentik_policies.delete_policybinding" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:policies-bindings") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted PolicyBinding") |     success_message = _("Successfully deleted PolicyBinding") | ||||||
|  | |||||||
| @ -15,7 +15,6 @@ from guardian.mixins import PermissionRequiredMixin | |||||||
|  |  | ||||||
| from authentik.admin.forms.policies import PolicyTestForm | from authentik.admin.forms.policies import PolicyTestForm | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import ( | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |     DeleteMessageView, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
| @ -25,7 +24,6 @@ from authentik.core.models import PropertyMapping | |||||||
|  |  | ||||||
| class PropertyMappingCreateView( | class PropertyMappingCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
| @ -41,7 +39,6 @@ class PropertyMappingCreateView( | |||||||
|  |  | ||||||
| class PropertyMappingUpdateView( | class PropertyMappingUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ from django.utils.translation import gettext as _ | |||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import ( | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |     DeleteMessageView, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
| @ -18,7 +17,6 @@ from authentik.core.models import Provider | |||||||
|  |  | ||||||
| class ProviderCreateView( | class ProviderCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
| @ -34,7 +32,6 @@ class ProviderCreateView( | |||||||
|  |  | ||||||
| class ProviderUpdateView( | class ProviderUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ from django.utils.translation import gettext as _ | |||||||
| from guardian.mixins import PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import ( | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |     DeleteMessageView, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
| @ -18,7 +17,6 @@ from authentik.core.models import Source | |||||||
|  |  | ||||||
| class SourceCreateView( | class SourceCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
| @ -34,7 +32,6 @@ class SourceCreateView( | |||||||
|  |  | ||||||
| class SourceUpdateView( | class SourceUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
|  | |||||||
| @ -4,41 +4,19 @@ from django.contrib.auth.mixins import ( | |||||||
|     PermissionRequiredMixin as DjangoPermissionRequiredMixin, |     PermissionRequiredMixin as DjangoPermissionRequiredMixin, | ||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import ( | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |     DeleteMessageView, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
|     InheritanceListView, |  | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) | ) | ||||||
| from authentik.flows.models import Stage | from authentik.flows.models import Stage | ||||||
|  |  | ||||||
|  |  | ||||||
| class StageListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     InheritanceListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all stages""" |  | ||||||
|  |  | ||||||
|     model = Stage |  | ||||||
|     template_name = "administration/stage/list.html" |  | ||||||
|     permission_required = "authentik_flows.view_stage" |  | ||||||
|     ordering = "name" |  | ||||||
|     search_fields = ["name"] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class StageCreateView( | class StageCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     InheritanceCreateView, |     InheritanceCreateView, | ||||||
| @ -49,13 +27,12 @@ class StageCreateView( | |||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     permission_required = "authentik_flows.add_stage" |     permission_required = "authentik_flows.add_stage" | ||||||
|  |  | ||||||
|     success_url = reverse_lazy("authentik_admin:stages") |     success_url = "/" | ||||||
|     success_message = _("Successfully created Stage") |     success_message = _("Successfully created Stage") | ||||||
|  |  | ||||||
|  |  | ||||||
| class StageUpdateView( | class StageUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     InheritanceUpdateView, |     InheritanceUpdateView, | ||||||
| @ -65,7 +42,7 @@ class StageUpdateView( | |||||||
|     model = Stage |     model = Stage | ||||||
|     permission_required = "authentik_flows.update_application" |     permission_required = "authentik_flows.update_application" | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stages") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated Stage") |     success_message = _("Successfully updated Stage") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -75,5 +52,5 @@ class StageDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage | |||||||
|     model = Stage |     model = Stage | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     permission_required = "authentik_flows.delete_stage" |     permission_required = "authentik_flows.delete_stage" | ||||||
|     success_url = reverse_lazy("authentik_admin:stages") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted Stage") |     success_message = _("Successfully deleted Stage") | ||||||
|  | |||||||
| @ -7,35 +7,18 @@ from django.contrib.auth.mixins import ( | |||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.db.models import Max | from django.db.models import Max | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import ListView, UpdateView | from django.views.generic import UpdateView | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.flows.forms import FlowStageBindingForm | from authentik.flows.forms import FlowStageBindingForm | ||||||
| from authentik.flows.models import Flow, FlowStageBinding | from authentik.flows.models import Flow, FlowStageBinding | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
|  |  | ||||||
|  |  | ||||||
| class StageBindingListView( |  | ||||||
|     LoginRequiredMixin, PermissionListMixin, UserPaginateListMixin, ListView |  | ||||||
| ): |  | ||||||
|     """Show list of all flows""" |  | ||||||
|  |  | ||||||
|     model = FlowStageBinding |  | ||||||
|     permission_required = "authentik_flows.view_flowstagebinding" |  | ||||||
|     ordering = ["target", "order"] |  | ||||||
|     template_name = "administration/stage_binding/list.html" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class StageBindingCreateView( | class StageBindingCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -47,7 +30,7 @@ class StageBindingCreateView( | |||||||
|     form_class = FlowStageBindingForm |     form_class = FlowStageBindingForm | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-bindings") |     success_url = "/" | ||||||
|     success_message = _("Successfully created StageBinding") |     success_message = _("Successfully created StageBinding") | ||||||
|  |  | ||||||
|     def get_initial(self) -> dict[str, Any]: |     def get_initial(self) -> dict[str, Any]: | ||||||
| @ -67,7 +50,6 @@ class StageBindingCreateView( | |||||||
|  |  | ||||||
| class StageBindingUpdateView( | class StageBindingUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
| @ -79,7 +61,7 @@ class StageBindingUpdateView( | |||||||
|     form_class = FlowStageBindingForm |     form_class = FlowStageBindingForm | ||||||
|  |  | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-bindings") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated StageBinding") |     success_message = _("Successfully updated StageBinding") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -92,5 +74,5 @@ class StageBindingDeleteView( | |||||||
|     permission_required = "authentik_flows.delete_flowstagebinding" |     permission_required = "authentik_flows.delete_flowstagebinding" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-bindings") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted FlowStageBinding") |     success_message = _("Successfully deleted FlowStageBinding") | ||||||
|  | |||||||
| @ -5,41 +5,17 @@ from django.contrib.auth.mixins import ( | |||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.http import HttpResponseRedirect | from django.http import HttpResponseRedirect | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import ListView | from guardian.mixins import PermissionRequiredMixin | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin |  | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |  | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| from authentik.stages.invitation.forms import InvitationForm | from authentik.stages.invitation.forms import InvitationForm | ||||||
| from authentik.stages.invitation.models import Invitation | from authentik.stages.invitation.models import Invitation | ||||||
|  |  | ||||||
|  |  | ||||||
| class InvitationListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     ListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all invitations""" |  | ||||||
|  |  | ||||||
|     model = Invitation |  | ||||||
|     permission_required = "authentik_stages_invitation.view_invitation" |  | ||||||
|     template_name = "administration/stage_invitation/list.html" |  | ||||||
|     ordering = "-expires" |  | ||||||
|     search_fields = ["created_by__username", "expires", "fixed_data"] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class InvitationCreateView( | class InvitationCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -51,7 +27,7 @@ class InvitationCreateView( | |||||||
|     permission_required = "authentik_stages_invitation.add_invitation" |     permission_required = "authentik_stages_invitation.add_invitation" | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-invitations") |     success_url = "/" | ||||||
|     success_message = _("Successfully created Invitation") |     success_message = _("Successfully created Invitation") | ||||||
|  |  | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
| @ -70,5 +46,5 @@ class InvitationDeleteView( | |||||||
|     permission_required = "authentik_stages_invitation.delete_invitation" |     permission_required = "authentik_stages_invitation.delete_invitation" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-invitations") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted Invitation") |     success_message = _("Successfully deleted Invitation") | ||||||
|  | |||||||
| @ -4,46 +4,18 @@ from django.contrib.auth.mixins import ( | |||||||
|     PermissionRequiredMixin as DjangoPermissionRequiredMixin, |     PermissionRequiredMixin as DjangoPermissionRequiredMixin, | ||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import ListView, UpdateView | from django.views.generic import UpdateView | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |  | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| from authentik.stages.prompt.forms import PromptAdminForm | from authentik.stages.prompt.forms import PromptAdminForm | ||||||
| from authentik.stages.prompt.models import Prompt | from authentik.stages.prompt.models import Prompt | ||||||
|  |  | ||||||
|  |  | ||||||
| class PromptListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     ListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all prompts""" |  | ||||||
|  |  | ||||||
|     model = Prompt |  | ||||||
|     permission_required = "authentik_stages_prompt.view_prompt" |  | ||||||
|     ordering = "order" |  | ||||||
|     template_name = "administration/stage_prompt/list.html" |  | ||||||
|     search_fields = [ |  | ||||||
|         "field_key", |  | ||||||
|         "label", |  | ||||||
|         "type", |  | ||||||
|         "placeholder", |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class PromptCreateView( | class PromptCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -55,13 +27,12 @@ class PromptCreateView( | |||||||
|     permission_required = "authentik_stages_prompt.add_prompt" |     permission_required = "authentik_stages_prompt.add_prompt" | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-prompts") |     success_url = "/" | ||||||
|     success_message = _("Successfully created Prompt") |     success_message = _("Successfully created Prompt") | ||||||
|  |  | ||||||
|  |  | ||||||
| class PromptUpdateView( | class PromptUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
| @ -73,7 +44,7 @@ class PromptUpdateView( | |||||||
|     permission_required = "authentik_stages_prompt.change_prompt" |     permission_required = "authentik_stages_prompt.change_prompt" | ||||||
|  |  | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-prompts") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated Prompt") |     success_message = _("Successfully updated Prompt") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -84,5 +55,5 @@ class PromptDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag | |||||||
|     permission_required = "authentik_stages_prompt.delete_prompt" |     permission_required = "authentik_stages_prompt.delete_prompt" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:stage-prompts") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted Prompt") |     success_message = _("Successfully deleted Prompt") | ||||||
|  | |||||||
| @ -1,23 +0,0 @@ | |||||||
| """authentik Tasks List""" |  | ||||||
| from typing import Any |  | ||||||
|  |  | ||||||
| from django.views.generic.base import TemplateView |  | ||||||
|  |  | ||||||
| from authentik.admin.mixins import AdminRequiredMixin |  | ||||||
| from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TaskListView(AdminRequiredMixin, TemplateView): |  | ||||||
|     """Show list of all background tasks""" |  | ||||||
|  |  | ||||||
|     template_name = "administration/task/list.html" |  | ||||||
|  |  | ||||||
|     def get_context_data(self, **kwargs: Any) -> dict[str, Any]: |  | ||||||
|         kwargs = super().get_context_data(**kwargs) |  | ||||||
|         kwargs["object_list"] = sorted( |  | ||||||
|             TaskInfo.all().values(), key=lambda x: x.task_name |  | ||||||
|         ) |  | ||||||
|         kwargs["task_successful"] = TaskResultStatus.SUCCESSFUL |  | ||||||
|         kwargs["task_warning"] = TaskResultStatus.WARNING |  | ||||||
|         kwargs["task_error"] = TaskResultStatus.ERROR |  | ||||||
|         return kwargs |  | ||||||
| @ -1,39 +1,12 @@ | |||||||
| """authentik Token administration""" | """authentik Token administration""" | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | from django.contrib.auth.mixins import LoginRequiredMixin | ||||||
| from django.urls import reverse_lazy |  | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import ListView | from guardian.mixins import PermissionRequiredMixin | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin |  | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     DeleteMessageView, |  | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.core.models import Token | from authentik.core.models import Token | ||||||
|  |  | ||||||
|  |  | ||||||
| class TokenListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     ListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all tokens""" |  | ||||||
|  |  | ||||||
|     model = Token |  | ||||||
|     permission_required = "authentik_core.view_token" |  | ||||||
|     ordering = "expires" |  | ||||||
|     template_name = "administration/token/list.html" |  | ||||||
|     search_fields = [ |  | ||||||
|         "identifier", |  | ||||||
|         "intent", |  | ||||||
|         "user__username", |  | ||||||
|         "description", |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView): | class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView): | ||||||
|     """Delete token""" |     """Delete token""" | ||||||
|  |  | ||||||
| @ -41,5 +14,5 @@ class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage | |||||||
|     permission_required = "authentik_core.delete_token" |     permission_required = "authentik_core.delete_token" | ||||||
|  |  | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:tokens") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted Token") |     success_message = _("Successfully deleted Token") | ||||||
|  | |||||||
| @ -7,50 +7,20 @@ from django.contrib.auth.mixins import ( | |||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.http import HttpRequest, HttpResponse | from django.http import HttpRequest, HttpResponse | ||||||
| from django.http.response import HttpResponseRedirect | from django.http.response import HttpResponseRedirect | ||||||
| from django.shortcuts import redirect | from django.shortcuts import redirect, reverse | ||||||
| from django.urls import reverse, reverse_lazy |  | ||||||
| from django.utils.http import urlencode | from django.utils.http import urlencode | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import DetailView, ListView, UpdateView | from django.views.generic import DetailView, UpdateView | ||||||
| from guardian.mixins import ( | from guardian.mixins import PermissionRequiredMixin | ||||||
|     PermissionListMixin, |  | ||||||
|     PermissionRequiredMixin, |  | ||||||
|     get_anonymous_user, |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| from authentik.admin.forms.users import UserForm | from authentik.admin.forms.users import UserForm | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     DeleteMessageView, |  | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.core.models import Token, User | from authentik.core.models import Token, User | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     ListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all users""" |  | ||||||
|  |  | ||||||
|     model = User |  | ||||||
|     permission_required = "authentik_core.view_user" |  | ||||||
|     ordering = "username" |  | ||||||
|     template_name = "administration/user/list.html" |  | ||||||
|     search_fields = ["username", "name", "attributes"] |  | ||||||
|  |  | ||||||
|     def get_queryset(self): |  | ||||||
|         return super().get_queryset().exclude(pk=get_anonymous_user().pk) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserCreateView( | class UserCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     DjangoPermissionRequiredMixin, |     DjangoPermissionRequiredMixin, | ||||||
|     CreateAssignPermView, |     CreateAssignPermView, | ||||||
| @ -62,13 +32,12 @@ class UserCreateView( | |||||||
|     permission_required = "authentik_core.add_user" |     permission_required = "authentik_core.add_user" | ||||||
|  |  | ||||||
|     template_name = "generic/create.html" |     template_name = "generic/create.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:users") |     success_url = "/" | ||||||
|     success_message = _("Successfully created User") |     success_message = _("Successfully created User") | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserUpdateView( | class UserUpdateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     BackSuccessUrlMixin, |  | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|     PermissionRequiredMixin, |     PermissionRequiredMixin, | ||||||
|     UpdateView, |     UpdateView, | ||||||
| @ -82,7 +51,7 @@ class UserUpdateView( | |||||||
|     # By default the object's name is user which is used by other checks |     # By default the object's name is user which is used by other checks | ||||||
|     context_object_name = "object" |     context_object_name = "object" | ||||||
|     template_name = "generic/update.html" |     template_name = "generic/update.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:users") |     success_url = "/" | ||||||
|     success_message = _("Successfully updated User") |     success_message = _("Successfully updated User") | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -95,13 +64,11 @@ class UserDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageV | |||||||
|     # By default the object's name is user which is used by other checks |     # By default the object's name is user which is used by other checks | ||||||
|     context_object_name = "object" |     context_object_name = "object" | ||||||
|     template_name = "generic/delete.html" |     template_name = "generic/delete.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:users") |     success_url = "/" | ||||||
|     success_message = _("Successfully deleted User") |     success_message = _("Successfully deleted User") | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserDisableView( | class UserDisableView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView): | ||||||
|     LoginRequiredMixin, PermissionRequiredMixin, BackSuccessUrlMixin, DeleteMessageView |  | ||||||
| ): |  | ||||||
|     """Disable user""" |     """Disable user""" | ||||||
|  |  | ||||||
|     object: User |     object: User | ||||||
| @ -112,7 +79,7 @@ class UserDisableView( | |||||||
|     # By default the object's name is user which is used by other checks |     # By default the object's name is user which is used by other checks | ||||||
|     context_object_name = "object" |     context_object_name = "object" | ||||||
|     template_name = "administration/user/disable.html" |     template_name = "administration/user/disable.html" | ||||||
|     success_url = reverse_lazy("authentik_admin:users") |     success_url = "/" | ||||||
|     success_message = _("Successfully disabled User") |     success_message = _("Successfully disabled User") | ||||||
|  |  | ||||||
|     def delete(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: |     def delete(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: | ||||||
| @ -123,9 +90,7 @@ class UserDisableView( | |||||||
|         return HttpResponseRedirect(success_url) |         return HttpResponseRedirect(success_url) | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserEnableView( | class UserEnableView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): | ||||||
|     LoginRequiredMixin, PermissionRequiredMixin, BackSuccessUrlMixin, DetailView |  | ||||||
| ): |  | ||||||
|     """Enable user""" |     """Enable user""" | ||||||
|  |  | ||||||
|     object: User |     object: User | ||||||
| @ -135,15 +100,14 @@ class UserEnableView( | |||||||
|  |  | ||||||
|     # By default the object's name is user which is used by other checks |     # By default the object's name is user which is used by other checks | ||||||
|     context_object_name = "object" |     context_object_name = "object" | ||||||
|     success_url = reverse_lazy("authentik_admin:users") |     success_url = "/" | ||||||
|     success_message = _("Successfully enabled User") |     success_message = _("Successfully enabled User") | ||||||
|  |  | ||||||
|     def get(self, request: HttpRequest, *args, **kwargs): |     def get(self, request: HttpRequest, *args, **kwargs): | ||||||
|         self.object: User = self.get_object() |         self.object: User = self.get_object() | ||||||
|         success_url = self.get_success_url() |  | ||||||
|         self.object.is_active = True |         self.object.is_active = True | ||||||
|         self.object.save() |         self.object.save() | ||||||
|         return HttpResponseRedirect(success_url) |         return HttpResponseRedirect(self.success_url) | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): | class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): | ||||||
| @ -165,4 +129,4 @@ class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailV | |||||||
|         messages.success( |         messages.success( | ||||||
|             request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link}) |             request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link}) | ||||||
|         ) |         ) | ||||||
|         return redirect("authentik_admin:users") |         return redirect("/") | ||||||
|  | |||||||
| @ -1,15 +1,10 @@ | |||||||
| """authentik admin util views""" | """authentik admin util views""" | ||||||
| from typing import Any, Optional | from typing import Any | ||||||
| from urllib.parse import urlparse |  | ||||||
|  |  | ||||||
| from django.contrib import messages | from django.contrib import messages | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.contrib.postgres.search import SearchQuery, SearchVector |  | ||||||
| from django.db.models import QuerySet |  | ||||||
| from django.http import Http404 | from django.http import Http404 | ||||||
| from django.http.request import HttpRequest | from django.views.generic import DeleteView, UpdateView | ||||||
| from django.views.generic import DeleteView, ListView, UpdateView |  | ||||||
| from django.views.generic.list import MultipleObjectMixin |  | ||||||
|  |  | ||||||
| from authentik.lib.utils.reflection import all_subclasses | from authentik.lib.utils.reflection import all_subclasses | ||||||
| from authentik.lib.views import CreateAssignPermView | from authentik.lib.views import CreateAssignPermView | ||||||
| @ -25,37 +20,6 @@ class DeleteMessageView(SuccessMessageMixin, DeleteView): | |||||||
|         return super().delete(request, *args, **kwargs) |         return super().delete(request, *args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| class InheritanceListView(ListView): |  | ||||||
|     """ListView for objects using InheritanceManager""" |  | ||||||
|  |  | ||||||
|     def get_context_data(self, **kwargs): |  | ||||||
|         kwargs["types"] = {x.__name__: x for x in all_subclasses(self.model)} |  | ||||||
|         return super().get_context_data(**kwargs) |  | ||||||
|  |  | ||||||
|     def get_queryset(self): |  | ||||||
|         return super().get_queryset().select_subclasses() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class SearchListMixin(MultipleObjectMixin): |  | ||||||
|     """Accept search query using `search` querystring parameter. Requires self.search_fields, |  | ||||||
|     a list of all fields to search. Can contain special lookups like __icontains""" |  | ||||||
|  |  | ||||||
|     search_fields: list[str] |  | ||||||
|  |  | ||||||
|     def get_queryset(self) -> QuerySet: |  | ||||||
|         queryset = super().get_queryset() |  | ||||||
|         if "search" in self.request.GET: |  | ||||||
|             raw_query = self.request.GET["search"] |  | ||||||
|             if raw_query == "": |  | ||||||
|                 # Empty query, don't search at all |  | ||||||
|                 return queryset |  | ||||||
|             search = SearchQuery(raw_query, search_type="websearch") |  | ||||||
|             return queryset.annotate(search=SearchVector(*self.search_fields)).filter( |  | ||||||
|                 search=search |  | ||||||
|             ) |  | ||||||
|         return queryset |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class InheritanceCreateView(CreateAssignPermView): | class InheritanceCreateView(CreateAssignPermView): | ||||||
|     """CreateView for objects using InheritanceManager""" |     """CreateView for objects using InheritanceManager""" | ||||||
|  |  | ||||||
| @ -96,31 +60,3 @@ class InheritanceUpdateView(UpdateView): | |||||||
|             .select_subclasses() |             .select_subclasses() | ||||||
|             .first() |             .first() | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class BackSuccessUrlMixin: |  | ||||||
|     """Checks if a relative URL has been given as ?back param, and redirect to it. Otherwise |  | ||||||
|     default to self.success_url.""" |  | ||||||
|  |  | ||||||
|     request: HttpRequest |  | ||||||
|  |  | ||||||
|     success_url: Optional[str] |  | ||||||
|  |  | ||||||
|     def get_success_url(self) -> str: |  | ||||||
|         """get_success_url from FormMixin""" |  | ||||||
|         back_param = self.request.GET.get("back") |  | ||||||
|         if back_param: |  | ||||||
|             if not bool(urlparse(back_param).netloc): |  | ||||||
|                 return back_param |  | ||||||
|         return str(self.success_url) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserPaginateListMixin: |  | ||||||
|     """Get paginate_by value from user's attributes, defaulting to 15""" |  | ||||||
|  |  | ||||||
|     request: HttpRequest |  | ||||||
|  |  | ||||||
|     # pylint: disable=unused-argument |  | ||||||
|     def get_paginate_by(self, queryset: QuerySet) -> int: |  | ||||||
|         """get_paginate_by Function of ListView""" |  | ||||||
|         return self.request.user.attributes.get("paginate_by", 15) |  | ||||||
|  | |||||||
| @ -23,12 +23,9 @@ from authentik.events.api.event import EventViewSet | |||||||
| from authentik.events.api.notification import NotificationViewSet | from authentik.events.api.notification import NotificationViewSet | ||||||
| from authentik.events.api.notification_rule import NotificationRuleViewSet | from authentik.events.api.notification_rule import NotificationRuleViewSet | ||||||
| from authentik.events.api.notification_transport import NotificationTransportViewSet | from authentik.events.api.notification_transport import NotificationTransportViewSet | ||||||
| from authentik.flows.api import ( | from authentik.flows.api.bindings import FlowStageBindingViewSet | ||||||
|     FlowCacheViewSet, | from authentik.flows.api.flows import FlowViewSet | ||||||
|     FlowStageBindingViewSet, | from authentik.flows.api.stages import StageViewSet | ||||||
|     FlowViewSet, |  | ||||||
|     StageViewSet, |  | ||||||
| ) |  | ||||||
| from authentik.flows.views import FlowExecutorView | from authentik.flows.views import FlowExecutorView | ||||||
| from authentik.outposts.api.outpost_service_connections import ( | from authentik.outposts.api.outpost_service_connections import ( | ||||||
|     DockerServiceConnectionViewSet, |     DockerServiceConnectionViewSet, | ||||||
| @ -36,11 +33,7 @@ from authentik.outposts.api.outpost_service_connections import ( | |||||||
|     ServiceConnectionViewSet, |     ServiceConnectionViewSet, | ||||||
| ) | ) | ||||||
| from authentik.outposts.api.outposts import OutpostViewSet | from authentik.outposts.api.outposts import OutpostViewSet | ||||||
| from authentik.policies.api import ( | from authentik.policies.api import PolicyBindingViewSet, PolicyViewSet | ||||||
|     PolicyBindingViewSet, |  | ||||||
|     PolicyCacheViewSet, |  | ||||||
|     PolicyViewSet, |  | ||||||
| ) |  | ||||||
| from authentik.policies.dummy.api import DummyPolicyViewSet | from authentik.policies.dummy.api import DummyPolicyViewSet | ||||||
| from authentik.policies.event_matcher.api import EventMatcherPolicyViewSet | from authentik.policies.event_matcher.api import EventMatcherPolicyViewSet | ||||||
| from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet | from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet | ||||||
| @ -101,7 +94,6 @@ router.register( | |||||||
| router.register("outposts/proxy", ProxyOutpostConfigViewSet) | router.register("outposts/proxy", ProxyOutpostConfigViewSet) | ||||||
|  |  | ||||||
| router.register("flows/instances", FlowViewSet) | router.register("flows/instances", FlowViewSet) | ||||||
| router.register("flows/cached", FlowCacheViewSet, basename="flows_cache") |  | ||||||
| router.register("flows/bindings", FlowStageBindingViewSet) | router.register("flows/bindings", FlowStageBindingViewSet) | ||||||
|  |  | ||||||
| router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet) | router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet) | ||||||
| @ -117,7 +109,6 @@ router.register("sources/saml", SAMLSourceViewSet) | |||||||
| router.register("sources/oauth", OAuthSourceViewSet) | router.register("sources/oauth", OAuthSourceViewSet) | ||||||
|  |  | ||||||
| router.register("policies/all", PolicyViewSet) | router.register("policies/all", PolicyViewSet) | ||||||
| router.register("policies/cached", PolicyCacheViewSet, basename="policies_cache") |  | ||||||
| router.register("policies/bindings", PolicyBindingViewSet) | router.register("policies/bindings", PolicyBindingViewSet) | ||||||
| router.register("policies/expression", ExpressionPolicyViewSet) | router.register("policies/expression", ExpressionPolicyViewSet) | ||||||
| router.register("policies/event_matcher", EventMatcherPolicyViewSet) | router.register("policies/event_matcher", EventMatcherPolicyViewSet) | ||||||
| @ -146,8 +137,8 @@ router.register("stages/captcha", CaptchaStageViewSet) | |||||||
| router.register("stages/consent", ConsentStageViewSet) | router.register("stages/consent", ConsentStageViewSet) | ||||||
| router.register("stages/email", EmailStageViewSet) | router.register("stages/email", EmailStageViewSet) | ||||||
| router.register("stages/identification", IdentificationStageViewSet) | router.register("stages/identification", IdentificationStageViewSet) | ||||||
| router.register("stages/invitation", InvitationStageViewSet) |  | ||||||
| router.register("stages/invitation/invitations", InvitationViewSet) | router.register("stages/invitation/invitations", InvitationViewSet) | ||||||
|  | router.register("stages/invitation/stages", InvitationStageViewSet) | ||||||
| router.register("stages/password", PasswordStageViewSet) | router.register("stages/password", PasswordStageViewSet) | ||||||
| router.register("stages/prompt/prompts", PromptViewSet) | router.register("stages/prompt/prompts", PromptViewSet) | ||||||
| router.register("stages/prompt/stages", PromptStageViewSet) | router.register("stages/prompt/stages", PromptStageViewSet) | ||||||
|  | |||||||
| @ -19,3 +19,5 @@ class GroupViewSet(ModelViewSet): | |||||||
|  |  | ||||||
|     queryset = Group.objects.all() |     queryset = Group.objects.all() | ||||||
|     serializer_class = GroupSerializer |     serializer_class = GroupSerializer | ||||||
|  |     search_fields = ["name", "is_superuser"] | ||||||
|  |     filterset_fields = ["name", "is_superuser"] | ||||||
|  | |||||||
| @ -1,9 +1,16 @@ | |||||||
| """PropertyMapping API Views""" | """PropertyMapping API Views""" | ||||||
|  | from django.shortcuts import reverse | ||||||
|  | from drf_yasg2.utils import swagger_auto_schema | ||||||
|  | from rest_framework.decorators import action | ||||||
|  | from rest_framework.request import Request | ||||||
|  | from rest_framework.response import Response | ||||||
| from rest_framework.serializers import ModelSerializer, SerializerMethodField | from rest_framework.serializers import ModelSerializer, SerializerMethodField | ||||||
| from rest_framework.viewsets import ReadOnlyModelViewSet | from rest_framework.viewsets import ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.utils import MetaNameSerializer | from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | ||||||
| from authentik.core.models import PropertyMapping | from authentik.core.models import PropertyMapping | ||||||
|  | from authentik.lib.templatetags.authentik_utils import verbose_name | ||||||
|  | from authentik.lib.utils.reflection import all_subclasses | ||||||
|  |  | ||||||
|  |  | ||||||
| class PropertyMappingSerializer(ModelSerializer, MetaNameSerializer): | class PropertyMappingSerializer(ModelSerializer, MetaNameSerializer): | ||||||
| @ -47,3 +54,19 @@ class PropertyMappingViewSet(ReadOnlyModelViewSet): | |||||||
|  |  | ||||||
|     def get_queryset(self): |     def get_queryset(self): | ||||||
|         return PropertyMapping.objects.select_subclasses() |         return PropertyMapping.objects.select_subclasses() | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) | ||||||
|  |     @action(detail=False) | ||||||
|  |     def types(self, request: Request) -> Response: | ||||||
|  |         """Get all creatable property-mapping types""" | ||||||
|  |         data = [] | ||||||
|  |         for subclass in all_subclasses(self.queryset.model): | ||||||
|  |             data.append( | ||||||
|  |                 { | ||||||
|  |                     "name": verbose_name(subclass), | ||||||
|  |                     "description": subclass.__doc__, | ||||||
|  |                     "link": reverse("authentik_admin:property-mapping-create") | ||||||
|  |                     + f"?type={subclass.__name__}", | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         return Response(TypeCreateSerializer(data, many=True).data) | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ from rest_framework.response import Response | |||||||
| from rest_framework.serializers import ModelSerializer, Serializer | from rest_framework.serializers import ModelSerializer, Serializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.users import UserSerializer | ||||||
| from authentik.core.models import Token | from authentik.core.models import Token | ||||||
| from authentik.events.models import Event, EventAction | from authentik.events.models import Event, EventAction | ||||||
|  |  | ||||||
| @ -16,10 +17,21 @@ from authentik.events.models import Event, EventAction | |||||||
| class TokenSerializer(ModelSerializer): | class TokenSerializer(ModelSerializer): | ||||||
|     """Token Serializer""" |     """Token Serializer""" | ||||||
|  |  | ||||||
|  |     user = UserSerializer() | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = Token |         model = Token | ||||||
|         fields = ["pk", "identifier", "intent", "user", "description"] |         fields = [ | ||||||
|  |             "pk", | ||||||
|  |             "identifier", | ||||||
|  |             "intent", | ||||||
|  |             "user", | ||||||
|  |             "description", | ||||||
|  |             "expires", | ||||||
|  |             "expiring", | ||||||
|  |         ] | ||||||
|  |         depth = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
| class TokenViewSerializer(Serializer): | class TokenViewSerializer(Serializer): | ||||||
| @ -40,6 +52,19 @@ class TokenViewSet(ModelViewSet): | |||||||
|     lookup_field = "identifier" |     lookup_field = "identifier" | ||||||
|     queryset = Token.filter_not_expired() |     queryset = Token.filter_not_expired() | ||||||
|     serializer_class = TokenSerializer |     serializer_class = TokenSerializer | ||||||
|  |     search_fields = [ | ||||||
|  |         "identifier", | ||||||
|  |         "intent", | ||||||
|  |         "user__username", | ||||||
|  |         "description", | ||||||
|  |     ] | ||||||
|  |     filterset_fields = [ | ||||||
|  |         "identifier", | ||||||
|  |         "intent", | ||||||
|  |         "user__username", | ||||||
|  |         "description", | ||||||
|  |     ] | ||||||
|  |     ordering = ["expires"] | ||||||
|  |  | ||||||
|     @swagger_auto_schema(responses={200: TokenViewSerializer(many=False)}) |     @swagger_auto_schema(responses={200: TokenViewSerializer(many=False)}) | ||||||
|     @action(detail=True) |     @action(detail=True) | ||||||
|  | |||||||
| @ -28,7 +28,17 @@ class UserSerializer(ModelSerializer): | |||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = User |         model = User | ||||||
|         fields = ["pk", "username", "name", "is_superuser", "email", "avatar"] |         fields = [ | ||||||
|  |             "pk", | ||||||
|  |             "username", | ||||||
|  |             "name", | ||||||
|  |             "is_active", | ||||||
|  |             "last_login", | ||||||
|  |             "is_superuser", | ||||||
|  |             "email", | ||||||
|  |             "avatar", | ||||||
|  |             "attributes", | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserViewSet(ModelViewSet): | class UserViewSet(ModelViewSet): | ||||||
| @ -36,6 +46,8 @@ class UserViewSet(ModelViewSet): | |||||||
|  |  | ||||||
|     queryset = User.objects.none() |     queryset = User.objects.none() | ||||||
|     serializer_class = UserSerializer |     serializer_class = UserSerializer | ||||||
|  |     search_fields = ["username", "name", "is_active"] | ||||||
|  |     filterset_fields = ["username", "name", "is_active"] | ||||||
|  |  | ||||||
|     def get_queryset(self): |     def get_queryset(self): | ||||||
|         return User.objects.all().exclude(pk=get_anonymous_user().pk) |         return User.objects.all().exclude(pk=get_anonymous_user().pk) | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| """API Utilities""" | """API Utilities""" | ||||||
| from django.db.models import Model | from django.db.models import Model | ||||||
| from rest_framework.fields import CharField | from rest_framework.fields import CharField, IntegerField | ||||||
| from rest_framework.serializers import Serializer, SerializerMethodField | from rest_framework.serializers import Serializer, SerializerMethodField | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -37,3 +37,15 @@ class TypeCreateSerializer(Serializer): | |||||||
|  |  | ||||||
|     def update(self, instance: Model, validated_data: dict) -> Model: |     def update(self, instance: Model, validated_data: dict) -> Model: | ||||||
|         raise NotImplementedError |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class CacheSerializer(Serializer): | ||||||
|  |     """Generic cache stats for an object""" | ||||||
|  |  | ||||||
|  |     count = IntegerField(read_only=True) | ||||||
|  |  | ||||||
|  |     def create(self, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |     def update(self, instance: Model, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  | |||||||
| @ -24,9 +24,7 @@ | |||||||
|                 </div> |                 </div> | ||||||
|             </section> |             </section> | ||||||
|             <section slot="page-2" data-tab-title="{% trans 'Tokens' %}" class="pf-c-page__main-section pf-m-no-padding-mobile"> |             <section slot="page-2" data-tab-title="{% trans 'Tokens' %}" class="pf-c-page__main-section pf-m-no-padding-mobile"> | ||||||
|                 <ak-site-shell url="{% url 'authentik_core:user-tokens' %}"> |                 <ak-token-user-list></ak-token-user-list> | ||||||
|                     <div slot="body"></div> |  | ||||||
|                 </ak-site-shell> |  | ||||||
|             </section> |             </section> | ||||||
|             {% user_stages as user_stages_loc %} |             {% user_stages as user_stages_loc %} | ||||||
|             {% for stage, stage_link in user_stages_loc.items %} |             {% for stage, stage_link in user_stages_loc.items %} | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ urlpatterns = [ | |||||||
|     # User views |     # User views | ||||||
|     path("-/user/", user.UserSettingsView.as_view(), name="user-settings"), |     path("-/user/", user.UserSettingsView.as_view(), name="user-settings"), | ||||||
|     path("-/user/details/", user.UserDetailsView.as_view(), name="user-details"), |     path("-/user/details/", user.UserDetailsView.as_view(), name="user-details"), | ||||||
|     path("-/user/tokens/", user.TokenListView.as_view(), name="user-tokens"), |  | ||||||
|     path( |     path( | ||||||
|         "-/user/tokens/create/", |         "-/user/tokens/create/", | ||||||
|         user.TokenCreateView.as_view(), |         user.TokenCreateView.as_view(), | ||||||
|  | |||||||
| @ -6,20 +6,15 @@ from django.contrib.auth.mixins import ( | |||||||
|     PermissionRequiredMixin as DjangoPermissionRequiredMixin, |     PermissionRequiredMixin as DjangoPermissionRequiredMixin, | ||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.db.models.query import QuerySet |  | ||||||
| from django.http.response import HttpResponse | from django.http.response import HttpResponse | ||||||
| from django.urls import reverse_lazy | from django.urls import reverse_lazy | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import ListView, UpdateView | from django.views.generic import UpdateView | ||||||
| from django.views.generic.base import TemplateView | from django.views.generic.base import TemplateView | ||||||
| from guardian.mixins import PermissionListMixin, PermissionRequiredMixin | from guardian.mixins import PermissionRequiredMixin | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
|  |  | ||||||
| from authentik.admin.views.utils import ( | from authentik.admin.views.utils import DeleteMessageView | ||||||
|     DeleteMessageView, |  | ||||||
|     SearchListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
| ) |  | ||||||
| from authentik.core.forms.token import UserTokenForm | from authentik.core.forms.token import UserTokenForm | ||||||
| from authentik.core.forms.users import UserDetailForm | from authentik.core.forms.users import UserDetailForm | ||||||
| from authentik.core.models import Token, TokenIntents | from authentik.core.models import Token, TokenIntents | ||||||
| @ -54,30 +49,6 @@ class UserDetailsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView): | |||||||
|         return kwargs |         return kwargs | ||||||
|  |  | ||||||
|  |  | ||||||
| class TokenListView( |  | ||||||
|     LoginRequiredMixin, |  | ||||||
|     PermissionListMixin, |  | ||||||
|     UserPaginateListMixin, |  | ||||||
|     SearchListMixin, |  | ||||||
|     ListView, |  | ||||||
| ): |  | ||||||
|     """Show list of all tokens""" |  | ||||||
|  |  | ||||||
|     model = Token |  | ||||||
|     ordering = "expires" |  | ||||||
|     permission_required = "authentik_core.view_token" |  | ||||||
|  |  | ||||||
|     template_name = "user/token_list.html" |  | ||||||
|     search_fields = [ |  | ||||||
|         "identifier", |  | ||||||
|         "intent", |  | ||||||
|         "description", |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|     def get_queryset(self) -> QuerySet: |  | ||||||
|         return super().get_queryset().filter(intent=TokenIntents.INTENT_API) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TokenCreateView( | class TokenCreateView( | ||||||
|     SuccessMessageMixin, |     SuccessMessageMixin, | ||||||
|     LoginRequiredMixin, |     LoginRequiredMixin, | ||||||
|  | |||||||
							
								
								
									
										0
									
								
								authentik/flows/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								authentik/flows/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										35
									
								
								authentik/flows/api/bindings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								authentik/flows/api/bindings.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | """Flow Binding API Views""" | ||||||
|  | from rest_framework.serializers import ModelSerializer | ||||||
|  | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.flows.api.stages import StageSerializer | ||||||
|  | from authentik.flows.models import FlowStageBinding | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class FlowStageBindingSerializer(ModelSerializer): | ||||||
|  |     """FlowStageBinding Serializer""" | ||||||
|  |  | ||||||
|  |     stage_obj = StageSerializer(read_only=True, source="stage") | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         model = FlowStageBinding | ||||||
|  |         fields = [ | ||||||
|  |             "pk", | ||||||
|  |             "policybindingmodel_ptr_id", | ||||||
|  |             "target", | ||||||
|  |             "stage", | ||||||
|  |             "stage_obj", | ||||||
|  |             "evaluate_on_plan", | ||||||
|  |             "re_evaluate_policies", | ||||||
|  |             "order", | ||||||
|  |             "policies", | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class FlowStageBindingViewSet(ModelViewSet): | ||||||
|  |     """FlowStageBinding Viewset""" | ||||||
|  |  | ||||||
|  |     queryset = FlowStageBinding.objects.all() | ||||||
|  |     serializer_class = FlowStageBindingSerializer | ||||||
|  |     filterset_fields = "__all__" | ||||||
| @ -3,11 +3,10 @@ from dataclasses import dataclass | |||||||
| 
 | 
 | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.db.models import Model | from django.db.models import Model | ||||||
| from django.shortcuts import get_object_or_404, reverse | from django.shortcuts import get_object_or_404 | ||||||
| from drf_yasg2.utils import swagger_auto_schema | from drf_yasg2.utils import swagger_auto_schema | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.mixins import ListModelMixin |  | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework.serializers import ( | from rest_framework.serializers import ( | ||||||
| @ -16,13 +15,11 @@ from rest_framework.serializers import ( | |||||||
|     Serializer, |     Serializer, | ||||||
|     SerializerMethodField, |     SerializerMethodField, | ||||||
| ) | ) | ||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
| 
 | 
 | ||||||
| from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | from authentik.core.api.utils import CacheSerializer | ||||||
| from authentik.flows.models import Flow, FlowStageBinding, Stage | from authentik.flows.models import Flow | ||||||
| from authentik.flows.planner import cache_key | from authentik.flows.planner import cache_key | ||||||
| from authentik.lib.templatetags.authentik_utils import verbose_name |  | ||||||
| from authentik.lib.utils.reflection import all_subclasses |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class FlowSerializer(ModelSerializer): | class FlowSerializer(ModelSerializer): | ||||||
| @ -84,6 +81,12 @@ class FlowViewSet(ModelViewSet): | |||||||
|     search_fields = ["name", "slug", "designation", "title"] |     search_fields = ["name", "slug", "designation", "title"] | ||||||
|     filterset_fields = ["flow_uuid", "name", "slug", "designation"] |     filterset_fields = ["flow_uuid", "name", "slug", "designation"] | ||||||
| 
 | 
 | ||||||
|  |     @swagger_auto_schema(responses={200: CacheSerializer(many=False)}) | ||||||
|  |     @action(detail=False) | ||||||
|  |     def cached(self, request: Request) -> Response: | ||||||
|  |         """Info about cached flows""" | ||||||
|  |         return Response(data={"count": len(cache.keys("flow_*"))}) | ||||||
|  | 
 | ||||||
|     @swagger_auto_schema(responses={200: FlowDiagramSerializer()}) |     @swagger_auto_schema(responses={200: FlowDiagramSerializer()}) | ||||||
|     @action(detail=True, methods=["get"]) |     @action(detail=True, methods=["get"]) | ||||||
|     def diagram(self, request: Request, slug: str) -> Response: |     def diagram(self, request: Request, slug: str) -> Response: | ||||||
| @ -155,85 +158,3 @@ class FlowViewSet(ModelViewSet): | |||||||
|                     ) |                     ) | ||||||
|         diagram = "\n".join([str(x) for x in header + body + footer]) |         diagram = "\n".join([str(x) for x in header + body + footer]) | ||||||
|         return Response({"diagram": diagram}) |         return Response({"diagram": diagram}) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class StageSerializer(ModelSerializer, MetaNameSerializer): |  | ||||||
|     """Stage Serializer""" |  | ||||||
| 
 |  | ||||||
|     object_type = SerializerMethodField() |  | ||||||
| 
 |  | ||||||
|     def get_object_type(self, obj): |  | ||||||
|         """Get object type so that we know which API Endpoint to use to get the full object""" |  | ||||||
|         return obj._meta.object_name.lower().replace("stage", "") |  | ||||||
| 
 |  | ||||||
|     class Meta: |  | ||||||
| 
 |  | ||||||
|         model = Stage |  | ||||||
|         fields = ["pk", "name", "object_type", "verbose_name", "verbose_name_plural"] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class StageViewSet(ReadOnlyModelViewSet): |  | ||||||
|     """Stage Viewset""" |  | ||||||
| 
 |  | ||||||
|     queryset = Stage.objects.all() |  | ||||||
|     serializer_class = StageSerializer |  | ||||||
| 
 |  | ||||||
|     def get_queryset(self): |  | ||||||
|         return Stage.objects.select_subclasses() |  | ||||||
| 
 |  | ||||||
|     @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) |  | ||||||
|     @action(detail=False) |  | ||||||
|     def types(self, request: Request) -> Response: |  | ||||||
|         """Get all creatable stage types""" |  | ||||||
|         data = [] |  | ||||||
|         for subclass in all_subclasses(self.queryset.model, False): |  | ||||||
|             data.append( |  | ||||||
|                 { |  | ||||||
|                     "name": verbose_name(subclass), |  | ||||||
|                     "description": subclass.__doc__, |  | ||||||
|                     "link": reverse("authentik_admin:stage-create") |  | ||||||
|                     + f"?type={subclass.__name__}", |  | ||||||
|                 } |  | ||||||
|             ) |  | ||||||
|         data = sorted(data, key=lambda x: x["name"]) |  | ||||||
|         return Response(TypeCreateSerializer(data, many=True).data) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class FlowStageBindingSerializer(ModelSerializer): |  | ||||||
|     """FlowStageBinding Serializer""" |  | ||||||
| 
 |  | ||||||
|     stage_obj = StageSerializer(read_only=True, source="stage") |  | ||||||
| 
 |  | ||||||
|     class Meta: |  | ||||||
| 
 |  | ||||||
|         model = FlowStageBinding |  | ||||||
|         fields = [ |  | ||||||
|             "pk", |  | ||||||
|             "policybindingmodel_ptr_id", |  | ||||||
|             "target", |  | ||||||
|             "stage", |  | ||||||
|             "stage_obj", |  | ||||||
|             "evaluate_on_plan", |  | ||||||
|             "re_evaluate_policies", |  | ||||||
|             "order", |  | ||||||
|             "policies", |  | ||||||
|         ] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class FlowStageBindingViewSet(ModelViewSet): |  | ||||||
|     """FlowStageBinding Viewset""" |  | ||||||
| 
 |  | ||||||
|     queryset = FlowStageBinding.objects.all() |  | ||||||
|     serializer_class = FlowStageBindingSerializer |  | ||||||
|     filterset_fields = "__all__" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class FlowCacheViewSet(ListModelMixin, GenericViewSet): |  | ||||||
|     """Info about cached flows""" |  | ||||||
| 
 |  | ||||||
|     queryset = Flow.objects.none() |  | ||||||
|     serializer_class = Serializer |  | ||||||
| 
 |  | ||||||
|     def list(self, request: Request) -> Response: |  | ||||||
|         """Info about cached flows""" |  | ||||||
|         return Response(data={"pagination": {"count": len(cache.keys("flow_*"))}}) |  | ||||||
							
								
								
									
										66
									
								
								authentik/flows/api/stages.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								authentik/flows/api/stages.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | """Flow Stage API Views""" | ||||||
|  | from django.shortcuts import reverse | ||||||
|  | from drf_yasg2.utils import swagger_auto_schema | ||||||
|  | from rest_framework.decorators import action | ||||||
|  | from rest_framework.request import Request | ||||||
|  | from rest_framework.response import Response | ||||||
|  | from rest_framework.serializers import ModelSerializer, SerializerMethodField | ||||||
|  | from rest_framework.viewsets import ReadOnlyModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | ||||||
|  | from authentik.flows.api.flows import FlowSerializer | ||||||
|  | from authentik.flows.models import Stage | ||||||
|  | from authentik.lib.templatetags.authentik_utils import verbose_name | ||||||
|  | from authentik.lib.utils.reflection import all_subclasses | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StageSerializer(ModelSerializer, MetaNameSerializer): | ||||||
|  |     """Stage Serializer""" | ||||||
|  |  | ||||||
|  |     object_type = SerializerMethodField() | ||||||
|  |     flow_set = FlowSerializer(many=True, required=False) | ||||||
|  |  | ||||||
|  |     def get_object_type(self, obj: Stage) -> str: | ||||||
|  |         """Get object type so that we know which API Endpoint to use to get the full object""" | ||||||
|  |         return obj._meta.object_name.lower().replace("stage", "") | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         model = Stage | ||||||
|  |         fields = [ | ||||||
|  |             "pk", | ||||||
|  |             "name", | ||||||
|  |             "object_type", | ||||||
|  |             "verbose_name", | ||||||
|  |             "verbose_name_plural", | ||||||
|  |             "flow_set", | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StageViewSet(ReadOnlyModelViewSet): | ||||||
|  |     """Stage Viewset""" | ||||||
|  |  | ||||||
|  |     queryset = Stage.objects.all().select_related("flow_set") | ||||||
|  |     serializer_class = StageSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |  | ||||||
|  |     def get_queryset(self): | ||||||
|  |         return Stage.objects.select_subclasses() | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) | ||||||
|  |     @action(detail=False) | ||||||
|  |     def types(self, request: Request) -> Response: | ||||||
|  |         """Get all creatable stage types""" | ||||||
|  |         data = [] | ||||||
|  |         for subclass in all_subclasses(self.queryset.model, False): | ||||||
|  |             data.append( | ||||||
|  |                 { | ||||||
|  |                     "name": verbose_name(subclass), | ||||||
|  |                     "description": subclass.__doc__, | ||||||
|  |                     "link": reverse("authentik_admin:stage-create") | ||||||
|  |                     + f"?type={subclass.__name__}", | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         data = sorted(data, key=lambda x: x["name"]) | ||||||
|  |         return Response(TypeCreateSerializer(data, many=True).data) | ||||||
| @ -118,7 +118,7 @@ class Flow(SerializerModel, PolicyBindingModel): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def serializer(self) -> BaseSerializer: |     def serializer(self) -> BaseSerializer: | ||||||
|         from authentik.flows.api import FlowSerializer |         from authentik.flows.api.flows import FlowSerializer | ||||||
|  |  | ||||||
|         return FlowSerializer |         return FlowSerializer | ||||||
|  |  | ||||||
| @ -189,12 +189,12 @@ class FlowStageBinding(SerializerModel, PolicyBindingModel): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def serializer(self) -> BaseSerializer: |     def serializer(self) -> BaseSerializer: | ||||||
|         from authentik.flows.api import FlowStageBindingSerializer |         from authentik.flows.api.bindings import FlowStageBindingSerializer | ||||||
|  |  | ||||||
|         return FlowStageBindingSerializer |         return FlowStageBindingSerializer | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
|         return f"{self.target} #{self.order} -> {self.stage}" |         return f"{self.target} #{self.order}" | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ from django.shortcuts import reverse | |||||||
| from rest_framework.test import APITestCase | from rest_framework.test import APITestCase | ||||||
|  |  | ||||||
| from authentik.core.models import User | from authentik.core.models import User | ||||||
| from authentik.flows.api import StageSerializer, StageViewSet | from authentik.flows.api.stages import StageSerializer, StageViewSet | ||||||
| from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage | from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage | ||||||
| from authentik.policies.dummy.models import DummyPolicy | from authentik.policies.dummy.models import DummyPolicy | ||||||
| from authentik.policies.models import PolicyBinding | from authentik.policies.models import PolicyBinding | ||||||
|  | |||||||
| @ -22,6 +22,8 @@ def get_attrs(obj: SerializerModel) -> dict[str, Any]: | |||||||
|         "verbose_name", |         "verbose_name", | ||||||
|         "verbose_name_plural", |         "verbose_name_plural", | ||||||
|         "object_type", |         "object_type", | ||||||
|  |         "flow_set", | ||||||
|  |         "promptstage_set", | ||||||
|     ) |     ) | ||||||
|     for to_remove_name in to_remove: |     for to_remove_name in to_remove: | ||||||
|         if to_remove_name in data: |         if to_remove_name in data: | ||||||
|  | |||||||
| @ -65,6 +65,7 @@ class FlowImporter: | |||||||
|             return value |             return value | ||||||
|  |  | ||||||
|         for key, value in attrs.items(): |         for key, value in attrs.items(): | ||||||
|  |             try: | ||||||
|                 if isinstance(value, dict): |                 if isinstance(value, dict): | ||||||
|                     for idx, _inner_key in enumerate(value): |                     for idx, _inner_key in enumerate(value): | ||||||
|                         value[_inner_key] = updater(value[_inner_key]) |                         value[_inner_key] = updater(value[_inner_key]) | ||||||
| @ -73,6 +74,8 @@ class FlowImporter: | |||||||
|                         attrs[key][idx] = updater(_inner_value) |                         attrs[key][idx] = updater(_inner_value) | ||||||
|                 else: |                 else: | ||||||
|                     attrs[key] = updater(value) |                     attrs[key] = updater(value) | ||||||
|  |             except TypeError: | ||||||
|  |                 continue | ||||||
|         return attrs |         return attrs | ||||||
|  |  | ||||||
|     def __query_from_identifier(self, attrs: dict[str, Any]) -> Q: |     def __query_from_identifier(self, attrs: dict[str, Any]) -> Q: | ||||||
|  | |||||||
| @ -1,7 +1,19 @@ | |||||||
| """Outpost API Views""" | """Outpost API Views""" | ||||||
| from rest_framework.serializers import ModelSerializer | from dataclasses import asdict | ||||||
|  |  | ||||||
|  | from django.db.models.base import Model | ||||||
|  | from django.shortcuts import reverse | ||||||
|  | from drf_yasg2.utils import swagger_auto_schema | ||||||
|  | from rest_framework.decorators import action | ||||||
|  | from rest_framework.fields import BooleanField, CharField, SerializerMethodField | ||||||
|  | from rest_framework.request import Request | ||||||
|  | from rest_framework.response import Response | ||||||
|  | from rest_framework.serializers import ModelSerializer, Serializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | ||||||
|  | from authentik.lib.templatetags.authentik_utils import verbose_name | ||||||
|  | from authentik.lib.utils.reflection import all_subclasses | ||||||
| from authentik.outposts.models import ( | from authentik.outposts.models import ( | ||||||
|     DockerServiceConnection, |     DockerServiceConnection, | ||||||
|     KubernetesServiceConnection, |     KubernetesServiceConnection, | ||||||
| @ -9,32 +21,81 @@ from authentik.outposts.models import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class ServiceConnectionSerializer(ModelSerializer): | class ServiceConnectionSerializer(ModelSerializer, MetaNameSerializer): | ||||||
|     """ServiceConnection Serializer""" |     """ServiceConnection Serializer""" | ||||||
|  |  | ||||||
|  |     object_type = SerializerMethodField() | ||||||
|  |  | ||||||
|  |     def get_object_type(self, obj: OutpostServiceConnection) -> str: | ||||||
|  |         """Get object type so that we know which API Endpoint to use to get the full object""" | ||||||
|  |         return obj._meta.object_name.lower().replace("serviceconnection", "") | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = OutpostServiceConnection |         model = OutpostServiceConnection | ||||||
|         fields = ["pk", "name"] |         fields = [ | ||||||
|  |             "pk", | ||||||
|  |             "name", | ||||||
|  |             "local", | ||||||
|  |             "object_type", | ||||||
|  |             "verbose_name", | ||||||
|  |             "verbose_name_plural", | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ServiceConnectionStateSerializer(Serializer): | ||||||
|  |     """Serializer for Service connection state""" | ||||||
|  |  | ||||||
|  |     healthy = BooleanField(read_only=True) | ||||||
|  |     version = CharField(read_only=True) | ||||||
|  |  | ||||||
|  |     def create(self, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |     def update(self, instance: Model, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
| class ServiceConnectionViewSet(ModelViewSet): | class ServiceConnectionViewSet(ModelViewSet): | ||||||
|     """ServiceConnection Viewset""" |     """ServiceConnection Viewset""" | ||||||
|  |  | ||||||
|     queryset = OutpostServiceConnection.objects.all() |     queryset = OutpostServiceConnection.objects.select_subclasses() | ||||||
|     serializer_class = ServiceConnectionSerializer |     serializer_class = ServiceConnectionSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) | ||||||
|  |     @action(detail=False) | ||||||
|  |     def types(self, request: Request) -> Response: | ||||||
|  |         """Get all creatable service connection types""" | ||||||
|  |         data = [] | ||||||
|  |         for subclass in all_subclasses(self.queryset.model): | ||||||
|  |             data.append( | ||||||
|  |                 { | ||||||
|  |                     "name": verbose_name(subclass), | ||||||
|  |                     "description": subclass.__doc__, | ||||||
|  |                     "link": reverse("authentik_admin:outpost-service-connection-create") | ||||||
|  |                     + f"?type={subclass.__name__}", | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         return Response(TypeCreateSerializer(data, many=True).data) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: ServiceConnectionStateSerializer(many=False)}) | ||||||
|  |     @action(detail=True) | ||||||
|  |     # pylint: disable=unused-argument, invalid-name | ||||||
|  |     def state(self, request: Request, pk: str) -> Response: | ||||||
|  |         """Get the service connection's state""" | ||||||
|  |         connection = self.get_object() | ||||||
|  |         return Response(asdict(connection.state)) | ||||||
|  |  | ||||||
|  |  | ||||||
| class DockerServiceConnectionSerializer(ModelSerializer): | class DockerServiceConnectionSerializer(ServiceConnectionSerializer): | ||||||
|     """DockerServiceConnection Serializer""" |     """DockerServiceConnection Serializer""" | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = DockerServiceConnection |         model = DockerServiceConnection | ||||||
|         fields = [ |         fields = ServiceConnectionSerializer.Meta.fields + [ | ||||||
|             "pk", |  | ||||||
|             "name", |  | ||||||
|             "local", |  | ||||||
|             "url", |             "url", | ||||||
|             "tls_verification", |             "tls_verification", | ||||||
|             "tls_authentication", |             "tls_authentication", | ||||||
| @ -48,13 +109,13 @@ class DockerServiceConnectionViewSet(ModelViewSet): | |||||||
|     serializer_class = DockerServiceConnectionSerializer |     serializer_class = DockerServiceConnectionSerializer | ||||||
|  |  | ||||||
|  |  | ||||||
| class KubernetesServiceConnectionSerializer(ModelSerializer): | class KubernetesServiceConnectionSerializer(ServiceConnectionSerializer): | ||||||
|     """KubernetesServiceConnection Serializer""" |     """KubernetesServiceConnection Serializer""" | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = KubernetesServiceConnection |         model = KubernetesServiceConnection | ||||||
|         fields = ["pk", "name", "local", "kubeconfig"] |         fields = ServiceConnectionSerializer.Meta.fields + ["kubeconfig"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class KubernetesServiceConnectionViewSet(ModelViewSet): | class KubernetesServiceConnectionViewSet(ModelViewSet): | ||||||
|  | |||||||
| @ -1,17 +1,25 @@ | |||||||
| """policy API Views""" | """policy API Views""" | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.core.exceptions import ObjectDoesNotExist | from django.core.exceptions import ObjectDoesNotExist | ||||||
| from rest_framework.mixins import ListModelMixin | from django.shortcuts import reverse | ||||||
|  | from drf_yasg2.utils import swagger_auto_schema | ||||||
|  | from rest_framework.decorators import action | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework.serializers import ( | from rest_framework.serializers import ( | ||||||
|     ModelSerializer, |     ModelSerializer, | ||||||
|     PrimaryKeyRelatedField, |     PrimaryKeyRelatedField, | ||||||
|     Serializer, |  | ||||||
|     SerializerMethodField, |     SerializerMethodField, | ||||||
| ) | ) | ||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.utils import ( | ||||||
|  |     CacheSerializer, | ||||||
|  |     MetaNameSerializer, | ||||||
|  |     TypeCreateSerializer, | ||||||
|  | ) | ||||||
|  | from authentik.lib.templatetags.authentik_utils import verbose_name | ||||||
|  | from authentik.lib.utils.reflection import all_subclasses | ||||||
| from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel | from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -45,20 +53,27 @@ class PolicyBindingModelForeignKey(PrimaryKeyRelatedField): | |||||||
|         return correct_model.pk |         return correct_model.pk | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicySerializer(ModelSerializer): | class PolicySerializer(ModelSerializer, MetaNameSerializer): | ||||||
|     """Policy Serializer""" |     """Policy Serializer""" | ||||||
|  |  | ||||||
|     _resolve_inheritance: bool |     _resolve_inheritance: bool | ||||||
|  |  | ||||||
|     object_type = SerializerMethodField() |     object_type = SerializerMethodField() | ||||||
|  |     bound_to = SerializerMethodField() | ||||||
|  |  | ||||||
|     def __init__(self, *args, resolve_inheritance: bool = True, **kwargs): |     def __init__(self, *args, resolve_inheritance: bool = True, **kwargs): | ||||||
|         super().__init__(*args, **kwargs) |         super().__init__(*args, **kwargs) | ||||||
|         self._resolve_inheritance = resolve_inheritance |         self._resolve_inheritance = resolve_inheritance | ||||||
|  |  | ||||||
|     def get_object_type(self, obj): |     def get_object_type(self, obj: Policy) -> str: | ||||||
|         """Get object type so that we know which API Endpoint to use to get the full object""" |         """Get object type so that we know which API Endpoint to use to get the full object""" | ||||||
|         return obj._meta.object_name.lower().replace("provider", "") |         return obj._meta.object_name.lower().replace("policy", "") | ||||||
|  |  | ||||||
|  |     def get_bound_to(self, obj: Policy) -> int: | ||||||
|  |         """Return objects policy is bound to""" | ||||||
|  |         if not obj.bindings.exists() and not obj.promptstage_set.exists(): | ||||||
|  |             return 0 | ||||||
|  |         return obj.bindings.count() | ||||||
|  |  | ||||||
|     def to_representation(self, instance: Policy): |     def to_representation(self, instance: Policy): | ||||||
|         # pyright: reportGeneralTypeIssues=false |         # pyright: reportGeneralTypeIssues=false | ||||||
| @ -71,7 +86,15 @@ class PolicySerializer(ModelSerializer): | |||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = Policy |         model = Policy | ||||||
|         fields = ["pk", "name", "execution_logging", "object_type"] |         fields = [ | ||||||
|  |             "pk", | ||||||
|  |             "name", | ||||||
|  |             "execution_logging", | ||||||
|  |             "object_type", | ||||||
|  |             "verbose_name", | ||||||
|  |             "verbose_name_plural", | ||||||
|  |             "bound_to", | ||||||
|  |         ] | ||||||
|         depth = 3 |         depth = 3 | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -84,9 +107,34 @@ class PolicyViewSet(ReadOnlyModelViewSet): | |||||||
|         "bindings": ["isnull"], |         "bindings": ["isnull"], | ||||||
|         "promptstage": ["isnull"], |         "promptstage": ["isnull"], | ||||||
|     } |     } | ||||||
|  |     search_fields = ["name"] | ||||||
|  |  | ||||||
|     def get_queryset(self): |     def get_queryset(self): | ||||||
|         return Policy.objects.select_subclasses() |         return Policy.objects.select_subclasses().prefetch_related( | ||||||
|  |             "bindings", "promptstage_set" | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) | ||||||
|  |     @action(detail=False) | ||||||
|  |     def types(self, request: Request) -> Response: | ||||||
|  |         """Get all creatable policy types""" | ||||||
|  |         data = [] | ||||||
|  |         for subclass in all_subclasses(self.queryset.model): | ||||||
|  |             data.append( | ||||||
|  |                 { | ||||||
|  |                     "name": verbose_name(subclass), | ||||||
|  |                     "description": subclass.__doc__, | ||||||
|  |                     "link": reverse("authentik_admin:policy-create") | ||||||
|  |                     + f"?type={subclass.__name__}", | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         return Response(TypeCreateSerializer(data, many=True).data) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: CacheSerializer(many=False)}) | ||||||
|  |     @action(detail=False) | ||||||
|  |     def cached(self, request: Request) -> Response: | ||||||
|  |         """Info about cached policies""" | ||||||
|  |         return Response(data={"count": len(cache.keys("policy_*"))}) | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyBindingSerializer(ModelSerializer): | class PolicyBindingSerializer(ModelSerializer): | ||||||
| @ -124,14 +172,3 @@ class PolicyBindingViewSet(ModelViewSet): | |||||||
|     serializer_class = PolicyBindingSerializer |     serializer_class = PolicyBindingSerializer | ||||||
|     filterset_fields = ["policy", "target", "enabled", "order", "timeout"] |     filterset_fields = ["policy", "target", "enabled", "order", "timeout"] | ||||||
|     search_fields = ["policy__name"] |     search_fields = ["policy__name"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyCacheViewSet(ListModelMixin, GenericViewSet): |  | ||||||
|     """Info about cached policies""" |  | ||||||
|  |  | ||||||
|     queryset = Policy.objects.none() |  | ||||||
|     serializer_class = Serializer |  | ||||||
|  |  | ||||||
|     def list(self, request: Request) -> Response: |  | ||||||
|         """Info about cached policies""" |  | ||||||
|         return Response(data={"pagination": {"count": len(cache.keys("policy_*"))}}) |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """AuthenticatorStaticStage API Views""" | """AuthenticatorStaticStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_static.models import AuthenticatorStaticStage | from authentik.stages.authenticator_static.models import AuthenticatorStaticStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """AuthenticatorTOTPStage API Views""" | """AuthenticatorTOTPStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage | from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """AuthenticatorValidateStage API Views""" | """AuthenticatorValidateStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage | from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ class ValidationForm(forms.Form): | |||||||
|         label=_("Please enter the token from your device."), |         label=_("Please enter the token from your device."), | ||||||
|         widget=forms.TextInput( |         widget=forms.TextInput( | ||||||
|             attrs={ |             attrs={ | ||||||
|                 "autocomplete": "off", |                 "autocomplete": "one-time-code", | ||||||
|                 "placeholder": "123456", |                 "placeholder": "123456", | ||||||
|                 "autofocus": "autofocus", |                 "autofocus": "autofocus", | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """AuthenticateWebAuthnStage API Views""" | """AuthenticateWebAuthnStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage | from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """CaptchaStage API Views""" | """CaptchaStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.captcha.models import CaptchaStage | from authentik.stages.captcha.models import CaptchaStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """ConsentStage API Views""" | """ConsentStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.consent.models import ConsentStage | from authentik.stages.consent.models import ConsentStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """DummyStage API Views""" | """DummyStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.dummy.models import DummyStage | from authentik.stages.dummy.models import DummyStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """EmailStage API Views""" | """EmailStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.email.models import EmailStage, get_template_choices | from authentik.stages.email.models import EmailStage, get_template_choices | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """Identification Stage API Views""" | """Identification Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.identification.models import IdentificationStage | from authentik.stages.identification.models import IdentificationStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.invitation.models import Invitation, InvitationStage | from authentik.stages.invitation.models import Invitation, InvitationStage | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -34,7 +34,9 @@ class InvitationSerializer(ModelSerializer): | |||||||
|             "pk", |             "pk", | ||||||
|             "expires", |             "expires", | ||||||
|             "fixed_data", |             "fixed_data", | ||||||
|  |             "created_by", | ||||||
|         ] |         ] | ||||||
|  |         depth = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
| class InvitationViewSet(ModelViewSet): | class InvitationViewSet(ModelViewSet): | ||||||
| @ -42,6 +44,9 @@ class InvitationViewSet(ModelViewSet): | |||||||
|  |  | ||||||
|     queryset = Invitation.objects.all() |     queryset = Invitation.objects.all() | ||||||
|     serializer_class = InvitationSerializer |     serializer_class = InvitationSerializer | ||||||
|  |     order = ["-expires"] | ||||||
|  |     search_fields = ["created_by__username", "expires"] | ||||||
|  |     filterset_fields = ["created_by__username", "expires"] | ||||||
|  |  | ||||||
|     def perform_create(self, serializer: InvitationSerializer): |     def perform_create(self, serializer: InvitationSerializer): | ||||||
|         serializer.instance.created_by = self.request.user |         serializer.instance.created_by = self.request.user | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """PasswordStage API Views""" | """PasswordStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.password.models import PasswordStage | from authentik.stages.password.models import PasswordStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ from rest_framework.serializers import CharField, ModelSerializer | |||||||
| from rest_framework.validators import UniqueValidator | from rest_framework.validators import UniqueValidator | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.prompt.models import Prompt, PromptStage | from authentik.stages.prompt.models import Prompt, PromptStage | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -31,6 +31,8 @@ class PromptStageViewSet(ModelViewSet): | |||||||
| class PromptSerializer(ModelSerializer): | class PromptSerializer(ModelSerializer): | ||||||
|     """Prompt Serializer""" |     """Prompt Serializer""" | ||||||
|  |  | ||||||
|  |     promptstage_set = StageSerializer(many=True, required=False) | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = Prompt |         model = Prompt | ||||||
| @ -42,11 +44,13 @@ class PromptSerializer(ModelSerializer): | |||||||
|             "required", |             "required", | ||||||
|             "placeholder", |             "placeholder", | ||||||
|             "order", |             "order", | ||||||
|  |             "promptstage_set", | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PromptViewSet(ModelViewSet): | class PromptViewSet(ModelViewSet): | ||||||
|     """Prompt Viewset""" |     """Prompt Viewset""" | ||||||
|  |  | ||||||
|     queryset = Prompt.objects.all() |     queryset = Prompt.objects.all().prefetch_related("promptstage_set") | ||||||
|     serializer_class = PromptSerializer |     serializer_class = PromptSerializer | ||||||
|  |     filterset_fields = ["field_key", "label", "type", "placeholder"] | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """User Delete Stage API Views""" | """User Delete Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_delete.models import UserDeleteStage | from authentik.stages.user_delete.models import UserDeleteStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """Login Stage API Views""" | """Login Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_login.models import UserLoginStage | from authentik.stages.user_login.models import UserLoginStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """Logout Stage API Views""" | """Logout Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_logout.models import UserLogoutStage | from authentik.stages.user_logout.models import UserLogoutStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| """User Write Stage API Views""" | """User Write Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_write.models import UserWriteStage | from authentik.stages.user_write.models import UserWriteStage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| trigger: | trigger: | ||||||
|   - master |   - master | ||||||
|  |   - next | ||||||
|  |  | ||||||
| resources: | resources: | ||||||
|   - repo: self |   - repo: self | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| trigger: | trigger: | ||||||
|   - master |   - master | ||||||
|  |   - next | ||||||
|  |  | ||||||
| variables: | variables: | ||||||
|   ${{ if startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}: |   ${{ if startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}: | ||||||
|  | |||||||
							
								
								
									
										1421
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										1421
									
								
								swagger.yaml
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,5 +1,6 @@ | |||||||
| trigger: | trigger: | ||||||
|   - master |   - master | ||||||
|  |   - next | ||||||
|  |  | ||||||
| variables: | variables: | ||||||
|   ${{ if startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}: |   ${{ if startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}: | ||||||
|  | |||||||
							
								
								
									
										134
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										134
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -144,30 +144,16 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/browser": { |         "@sentry/browser": { | ||||||
|             "version": "6.1.0", |             "version": "6.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.2.0.tgz", | ||||||
|             "integrity": "sha512-t3y2TLXDWgvfknyH8eKj/9mghJfSEqItFyp74zPu1Src6kOPjkd4Sa7o4+bdkNgA8dIIOrDAhRUbB2sq4sWMCA==", |             "integrity": "sha512-4r3paHcHXLemj471BtNDhUs2kvJxk5XDRplz1dbC/LHXN5PWEXP4anhGILxOlxqi4y33r53PIZu3xXFjznaVZA==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/core": "6.1.0", |                 "@sentry/core": "6.2.0", | ||||||
|                 "@sentry/types": "6.1.0", |                 "@sentry/types": "6.2.0", | ||||||
|                 "@sentry/utils": "6.1.0", |                 "@sentry/utils": "6.2.0", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/types": { |  | ||||||
|                     "version": "6.1.0", |  | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.1.0.tgz", |  | ||||||
|                     "integrity": "sha512-kIaN52Fw5K+2mKRaHE2YluJ+F/qMGSUzZXIFDNdC6OUMXQ4TM8gZTrITXs8CLDm7cK8iCqFCtzKOjKK6KyOKAg==" |  | ||||||
|                 }, |  | ||||||
|                 "@sentry/utils": { |  | ||||||
|                     "version": "6.1.0", |  | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.1.0.tgz", |  | ||||||
|                     "integrity": "sha512-6JAplzUOS6bEwfX0PDRZBbYRvn9EN22kZfcL0qGHtM9L0QQ5ybjbbVwOpbXgRkiZx++dQbzLFtelxnDhsbFG+Q==", |  | ||||||
|                     "requires": { |  | ||||||
|                         "@sentry/types": "6.1.0", |  | ||||||
|                         "tslib": "^1.9.3" |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 "tslib": { |                 "tslib": { | ||||||
|                     "version": "1.14.1", |                     "version": "1.14.1", | ||||||
|                     "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", |                     "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||||
| @ -176,51 +162,17 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/core": { |         "@sentry/core": { | ||||||
|             "version": "6.1.0", |             "version": "6.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.0.tgz", | ||||||
|             "integrity": "sha512-57mXkp3NoyxRycXrL+Ec6bYS6UYJZp9tYX0lUp5Ry2M0FxDZ3Q4drkjr8MIQOhBaQXP2ukSX4QTVLGMPm60zMw==", |             "integrity": "sha512-oTr2b25l+0bv/+d6IgMamPuGleWV7OgJb0NFfd+WZhw6UDRgr7CdEJy2gW6tK8SerwXgPHdn4ervxsT3WIBiXw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/hub": "6.1.0", |                 "@sentry/hub": "6.2.0", | ||||||
|                 "@sentry/minimal": "6.1.0", |                 "@sentry/minimal": "6.2.0", | ||||||
|                 "@sentry/types": "6.1.0", |                 "@sentry/types": "6.2.0", | ||||||
|                 "@sentry/utils": "6.1.0", |                 "@sentry/utils": "6.2.0", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/hub": { |  | ||||||
|                     "version": "6.1.0", |  | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.1.0.tgz", |  | ||||||
|                     "integrity": "sha512-JnBSCgNg3VHiMojUl5tCHU8iWPVuE+qqENIzG9A722oJms1kKWBvWl+yQzhWBNdgk5qeAY3F5UzKWJZkbJ6xow==", |  | ||||||
|                     "requires": { |  | ||||||
|                         "@sentry/types": "6.1.0", |  | ||||||
|                         "@sentry/utils": "6.1.0", |  | ||||||
|                         "tslib": "^1.9.3" |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 "@sentry/minimal": { |  | ||||||
|                     "version": "6.1.0", |  | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.1.0.tgz", |  | ||||||
|                     "integrity": "sha512-g6sfNKenL7wnsr/tibp8nFiMv/XRH0s0Pt4p151npmNI+SmjuUz3GGYEXk8ChCyaKldYKilkNOFdVXJxUf5gZw==", |  | ||||||
|                     "requires": { |  | ||||||
|                         "@sentry/hub": "6.1.0", |  | ||||||
|                         "@sentry/types": "6.1.0", |  | ||||||
|                         "tslib": "^1.9.3" |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 "@sentry/types": { |  | ||||||
|                     "version": "6.1.0", |  | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.1.0.tgz", |  | ||||||
|                     "integrity": "sha512-kIaN52Fw5K+2mKRaHE2YluJ+F/qMGSUzZXIFDNdC6OUMXQ4TM8gZTrITXs8CLDm7cK8iCqFCtzKOjKK6KyOKAg==" |  | ||||||
|                 }, |  | ||||||
|                 "@sentry/utils": { |  | ||||||
|                     "version": "6.1.0", |  | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.1.0.tgz", |  | ||||||
|                     "integrity": "sha512-6JAplzUOS6bEwfX0PDRZBbYRvn9EN22kZfcL0qGHtM9L0QQ5ybjbbVwOpbXgRkiZx++dQbzLFtelxnDhsbFG+Q==", |  | ||||||
|                     "requires": { |  | ||||||
|                         "@sentry/types": "6.1.0", |  | ||||||
|                         "tslib": "^1.9.3" |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 "tslib": { |                 "tslib": { | ||||||
|                     "version": "1.14.1", |                     "version": "1.14.1", | ||||||
|                     "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", |                     "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||||
| @ -229,12 +181,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/hub": { |         "@sentry/hub": { | ||||||
|             "version": "6.1.0", |             "version": "6.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.0.tgz", | ||||||
|             "integrity": "sha512-JnBSCgNg3VHiMojUl5tCHU8iWPVuE+qqENIzG9A722oJms1kKWBvWl+yQzhWBNdgk5qeAY3F5UzKWJZkbJ6xow==", |             "integrity": "sha512-BDTEFK8vlJydWXp/KMX0stvv73V7od224iLi+w3k7BcPwMKXBuURBXPU8d5XIC4G8nwg8X6cnDvwL+zBBlBbkg==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/types": "6.1.0", |                 "@sentry/types": "6.2.0", | ||||||
|                 "@sentry/utils": "6.1.0", |                 "@sentry/utils": "6.2.0", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -246,12 +198,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/minimal": { |         "@sentry/minimal": { | ||||||
|             "version": "6.1.0", |             "version": "6.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.0.tgz", | ||||||
|             "integrity": "sha512-g6sfNKenL7wnsr/tibp8nFiMv/XRH0s0Pt4p151npmNI+SmjuUz3GGYEXk8ChCyaKldYKilkNOFdVXJxUf5gZw==", |             "integrity": "sha512-haxsx8/ZafhZUaGeeMtY7bJt9HbDlqeiaXrRMp1CxGtd0ZRQwHt60imEjl6IH1I73SEWxNfqScGsX2s3HzztMg==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/hub": "6.1.0", |                 "@sentry/hub": "6.2.0", | ||||||
|                 "@sentry/types": "6.1.0", |                 "@sentry/types": "6.2.0", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -263,14 +215,14 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/tracing": { |         "@sentry/tracing": { | ||||||
|             "version": "6.1.0", |             "version": "6.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.0.tgz", | ||||||
|             "integrity": "sha512-s6a4Ra3hHn4awiNz4fOEK6TCV2w2iLcxdppijcYEB7S/1rJpmqZgHWDicqufbOmVMOLmyKLEQ7w+pZq3TR3WgQ==", |             "integrity": "sha512-pzgM1dePPJysVnzaFCMp+BKtjM5q46HZeyShiR+KcQYvneD3fmUPJigDkkcsB2DcrY3mFvDcswjoqxaTIW7ZBQ==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/hub": "6.1.0", |                 "@sentry/hub": "6.2.0", | ||||||
|                 "@sentry/minimal": "6.1.0", |                 "@sentry/minimal": "6.2.0", | ||||||
|                 "@sentry/types": "6.1.0", |                 "@sentry/types": "6.2.0", | ||||||
|                 "@sentry/utils": "6.1.0", |                 "@sentry/utils": "6.2.0", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -282,16 +234,16 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/types": { |         "@sentry/types": { | ||||||
|             "version": "6.1.0", |             "version": "6.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.0.tgz", | ||||||
|             "integrity": "sha512-kIaN52Fw5K+2mKRaHE2YluJ+F/qMGSUzZXIFDNdC6OUMXQ4TM8gZTrITXs8CLDm7cK8iCqFCtzKOjKK6KyOKAg==" |             "integrity": "sha512-vN4P/a+QqAuVfWFB9G3nQ7d6bgnM9jd/RLVi49owMuqvM24pv5mTQHUk2Hk4S3k7ConrHFl69E7xH6Dv5VpQnQ==" | ||||||
|         }, |         }, | ||||||
|         "@sentry/utils": { |         "@sentry/utils": { | ||||||
|             "version": "6.1.0", |             "version": "6.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.0.tgz", | ||||||
|             "integrity": "sha512-6JAplzUOS6bEwfX0PDRZBbYRvn9EN22kZfcL0qGHtM9L0QQ5ybjbbVwOpbXgRkiZx++dQbzLFtelxnDhsbFG+Q==", |             "integrity": "sha512-YToUC7xYf2E/pIluI7upYTlj8fKXOtdwoOBkcQZifHgX/dP+qDaHibbBFe5PyZwdmU2UiLnWFsBr0gjo0QFo1g==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/types": "6.1.0", |                 "@sentry/types": "6.2.0", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -1682,9 +1634,9 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "graceful-fs": { |         "graceful-fs": { | ||||||
|             "version": "4.2.4", |             "version": "4.2.6", | ||||||
|             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", |             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", | ||||||
|             "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" |             "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" | ||||||
|         }, |         }, | ||||||
|         "has-flag": { |         "has-flag": { | ||||||
|             "version": "3.0.0", |             "version": "3.0.0", | ||||||
| @ -2729,9 +2681,9 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "rollup-plugin-copy": { |         "rollup-plugin-copy": { | ||||||
|             "version": "3.3.0", |             "version": "3.4.0", | ||||||
|             "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz", |             "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz", | ||||||
|             "integrity": "sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA==", |             "integrity": "sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@types/fs-extra": "^8.0.1", |                 "@types/fs-extra": "^8.0.1", | ||||||
|                 "colorette": "^1.1.0", |                 "colorette": "^1.1.0", | ||||||
|  | |||||||
| @ -12,8 +12,8 @@ | |||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "@fortawesome/fontawesome-free": "^5.15.2", |         "@fortawesome/fontawesome-free": "^5.15.2", | ||||||
|         "@patternfly/patternfly": "^4.87.3", |         "@patternfly/patternfly": "^4.87.3", | ||||||
|         "@sentry/browser": "^6.1.0", |         "@sentry/browser": "^6.2.0", | ||||||
|         "@sentry/tracing": "^6.1.0", |         "@sentry/tracing": "^6.2.0", | ||||||
|         "@types/chart.js": "^2.9.30", |         "@types/chart.js": "^2.9.30", | ||||||
|         "@types/codemirror": "0.0.108", |         "@types/codemirror": "0.0.108", | ||||||
|         "base64-js": "^1.5.1", |         "base64-js": "^1.5.1", | ||||||
| @ -24,7 +24,7 @@ | |||||||
|         "lit-element": "^2.4.0", |         "lit-element": "^2.4.0", | ||||||
|         "lit-html": "^1.3.0", |         "lit-html": "^1.3.0", | ||||||
|         "rollup": "^2.39.0", |         "rollup": "^2.39.0", | ||||||
|         "rollup-plugin-copy": "^3.3.0", |         "rollup-plugin-copy": "^3.4.0", | ||||||
|         "rollup-plugin-cssimport": "^1.0.2", |         "rollup-plugin-cssimport": "^1.0.2", | ||||||
|         "rollup-plugin-external-globals": "^0.6.1", |         "rollup-plugin-external-globals": "^0.6.1", | ||||||
|         "tslib": "^2.1.0" |         "tslib": "^2.1.0" | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | import { DefaultClient, AKResponse, QueryArguments, BaseInheritanceModel } from "./Client"; | ||||||
| import { TypeCreate } from "./Providers"; | import { TypeCreate } from "./Providers"; | ||||||
|  |  | ||||||
| export enum FlowDesignation { | export enum FlowDesignation { | ||||||
| @ -40,8 +40,8 @@ export class Flow { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     static cached(): Promise<number> { |     static cached(): Promise<number> { | ||||||
|         return DefaultClient.fetch<AKResponse<Flow>>(["flows", "cached"]).then(r => { |         return DefaultClient.fetch<{ count: number }>(["flows", "all", "cached"]).then(r => { | ||||||
|             return r.pagination.count; |             return r.count; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     static adminUrl(rest: string): string { |     static adminUrl(rest: string): string { | ||||||
| @ -49,16 +49,26 @@ export class Flow { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| export class Stage { | export class Stage implements BaseInheritanceModel { | ||||||
|     pk: string; |     pk: string; | ||||||
|     name: string; |     name: string; | ||||||
|     __type__: string; |     object_type: string; | ||||||
|     verbose_name: string; |     verbose_name: string; | ||||||
|  |     verbose_name_plural: string; | ||||||
|  |     flow_set: Flow[]; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         throw Error(); |         throw Error(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     static get(slug: string): Promise<Stage> { | ||||||
|  |         return DefaultClient.fetch<Stage>(["stages", "all", slug]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<AKResponse<Stage>> { | ||||||
|  |         return DefaultClient.fetch<AKResponse<Stage>>(["stages", "all"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |     static getTypes(): Promise<TypeCreate[]> { | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["stages", "all", "types"]); |         return DefaultClient.fetch<TypeCreate[]>(["stages", "all", "types"]); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,15 +1,28 @@ | |||||||
|  | import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||||
| import { EventContext } from "./Events"; | import { EventContext } from "./Events"; | ||||||
|  |  | ||||||
| export class Group { | export class Group { | ||||||
|  |  | ||||||
|     group_uuid: string; |     pk: string; | ||||||
|     name: string; |     name: string; | ||||||
|     is_superuser: boolean; |     is_superuser: boolean; | ||||||
|     attributes: EventContext; |     attributes: EventContext; | ||||||
|     parent?: Group; |     parent?: Group; | ||||||
|  |     users: number[]; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         throw Error(); |         throw Error(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     static get(pk: string): Promise<Group> { | ||||||
|  |         return DefaultClient.fetch<Group>(["core", "groups", pk]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<AKResponse<Group>> { | ||||||
|  |         return DefaultClient.fetch<AKResponse<Group>>(["core", "groups"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static adminUrl(rest: string): string { | ||||||
|  |         return `/administration/groups/${rest}`; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								web/src/api/Invitations.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								web/src/api/Invitations.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||||
|  | import { EventContext } from "./Events"; | ||||||
|  | import { User } from "./Users"; | ||||||
|  |  | ||||||
|  | export class Invitation { | ||||||
|  |  | ||||||
|  |     pk: string; | ||||||
|  |     expires: number; | ||||||
|  |     fixed_date: EventContext; | ||||||
|  |     created_by: User; | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         throw Error(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static get(pk: string): Promise<Invitation> { | ||||||
|  |         return DefaultClient.fetch<Invitation>(["stages", "invitation", "invitations", pk]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<AKResponse<Invitation>> { | ||||||
|  |         return DefaultClient.fetch<AKResponse<Invitation>>(["stages", "invitation", "invitations"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static adminUrl(rest: string): string { | ||||||
|  |         return `/administration/stages/invitations/${rest}`; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,5 +1,5 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||||
| import { Provider } from "./Providers"; | import { Provider, TypeCreate } from "./Providers"; | ||||||
|  |  | ||||||
| export interface OutpostHealth { | export interface OutpostHealth { | ||||||
|     last_seen: number; |     last_seen: number; | ||||||
| @ -38,3 +38,42 @@ export class Outpost { | |||||||
|         return `/administration/outposts/${rest}`; |         return `/administration/outposts/${rest}`; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface OutpostServiceConnectionState { | ||||||
|  |     version: string; | ||||||
|  |     healthy: boolean; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class OutpostServiceConnection { | ||||||
|  |     pk: string; | ||||||
|  |     name: string; | ||||||
|  |     local: boolean; | ||||||
|  |     object_type: string; | ||||||
|  |     verbose_name: string; | ||||||
|  |     verbose_name_plural: string; | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         throw Error(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static get(pk: string): Promise<OutpostServiceConnection> { | ||||||
|  |         return DefaultClient.fetch<OutpostServiceConnection>(["outposts", "service_connections", "all", pk]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<AKResponse<OutpostServiceConnection>> { | ||||||
|  |         return DefaultClient.fetch<AKResponse<OutpostServiceConnection>>(["outposts", "service_connections", "all"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static state(pk: string): Promise<OutpostServiceConnectionState> { | ||||||
|  |         return DefaultClient.fetch<OutpostServiceConnectionState>(["outposts", "service_connections", "all", pk, "state"]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static getTypes(): Promise<TypeCreate[]> { | ||||||
|  |         return DefaultClient.fetch<TypeCreate[]>(["outposts", "service_connections", "all", "types"]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static adminUrl(rest: string): string { | ||||||
|  |         return `/administration/outpost_service_connections/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,15 +1,18 @@ | |||||||
| import { DefaultClient, BaseInheritanceModel, AKResponse, QueryArguments } from "./Client"; | import { DefaultClient, BaseInheritanceModel, AKResponse, QueryArguments } from "./Client"; | ||||||
|  | import { TypeCreate } from "./Providers"; | ||||||
|  |  | ||||||
| export class Policy implements BaseInheritanceModel { | export class Policy implements BaseInheritanceModel { | ||||||
|     pk: string; |     pk: string; | ||||||
|     name: string; |     name: string; | ||||||
|  |     execution_logging: boolean; | ||||||
|  |     object_type: string; | ||||||
|  |     verbose_name: string; | ||||||
|  |     verbose_name_plural: string; | ||||||
|  |     bound_to: number; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         throw Error(); |         throw Error(); | ||||||
|     } |     } | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Policy> { |     static get(pk: string): Promise<Policy> { | ||||||
|         return DefaultClient.fetch<Policy>(["policies", "all", pk]); |         return DefaultClient.fetch<Policy>(["policies", "all", pk]); | ||||||
| @ -20,8 +23,16 @@ export class Policy implements BaseInheritanceModel { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     static cached(): Promise<number> { |     static cached(): Promise<number> { | ||||||
|         return DefaultClient.fetch<AKResponse<Policy>>(["policies", "cached"]).then(r => { |         return DefaultClient.fetch<{ count: number }>(["policies", "all", "cached"]).then(r => { | ||||||
|             return r.pagination.count; |             return r.count; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     static getTypes(): Promise<TypeCreate[]> { | ||||||
|  |         return DefaultClient.fetch<TypeCreate[]>(["policies", "all", "types"]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static adminUrl(rest: string): string { | ||||||
|  |         return `/administration/policies/${rest}`; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								web/src/api/Prompts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								web/src/api/Prompts.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||||
|  | import { Stage } from "./Flows"; | ||||||
|  |  | ||||||
|  | export class Prompt { | ||||||
|  |  | ||||||
|  |     pk: string; | ||||||
|  |     field_key: string; | ||||||
|  |     label: string; | ||||||
|  |     type: string; | ||||||
|  |     required: boolean; | ||||||
|  |     placeholder: string; | ||||||
|  |     order: number; | ||||||
|  |     promptstage_set: Stage[]; | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         throw Error(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static get(pk: string): Promise<Prompt> { | ||||||
|  |         return DefaultClient.fetch<Prompt>(["stages", "prompt", "prompts", pk]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<AKResponse<Prompt>> { | ||||||
|  |         return DefaultClient.fetch<AKResponse<Prompt>>(["stages", "prompt", "prompts"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static adminUrl(rest: string): string { | ||||||
|  |         return `/administration/stages/prompts/${rest}`; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,4 +1,5 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||||
|  | import { TypeCreate } from "./Providers"; | ||||||
|  |  | ||||||
| export class PropertyMapping { | export class PropertyMapping { | ||||||
|     pk: string; |     pk: string; | ||||||
| @ -20,6 +21,10 @@ export class PropertyMapping { | |||||||
|         return DefaultClient.fetch<AKResponse<PropertyMapping>>(["propertymappings", "all"], filter); |         return DefaultClient.fetch<AKResponse<PropertyMapping>>(["propertymappings", "all"], filter); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     static getTypes(): Promise<TypeCreate[]> { | ||||||
|  |         return DefaultClient.fetch<TypeCreate[]>(["propertymappings", "all", "types"]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |     static adminUrl(rest: string): string { | ||||||
|         return `/administration/property-mappings/${rest}`; |         return `/administration/property-mappings/${rest}`; | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								web/src/api/SystemTask.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								web/src/api/SystemTask.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | import { DefaultClient, QueryArguments } from "./Client"; | ||||||
|  |  | ||||||
|  | export enum TaskStatus { | ||||||
|  |     SUCCESSFUL = 1, | ||||||
|  |     WARNING = 2, | ||||||
|  |     ERROR = 4, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class SystemTask { | ||||||
|  |  | ||||||
|  |     task_name: string; | ||||||
|  |     task_description: string; | ||||||
|  |     task_finish_timestamp: number; | ||||||
|  |     status: TaskStatus; | ||||||
|  |     messages: string[]; | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         throw Error(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static get(task_name: string): Promise<SystemTask> { | ||||||
|  |         return DefaultClient.fetch<SystemTask>(["admin", "system_tasks", task_name]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<SystemTask[]> { | ||||||
|  |         return DefaultClient.fetch<SystemTask[]>(["admin", "system_tasks"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static retry(task_name: string): string { | ||||||
|  |         return DefaultClient.makeUrl(["admin", "system_tasks", task_name, "retry"]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -1,11 +1,47 @@ | |||||||
| import { DefaultClient } from "./Client"; | import { AKResponse, DefaultClient, QueryArguments } from "./Client"; | ||||||
|  | import { User } from "./Users"; | ||||||
|  |  | ||||||
| interface TokenResponse { | export enum TokenIntent { | ||||||
|     key: string; |     INTENT_VERIFICATION = "verification", | ||||||
|  |     INTENT_API = "api", | ||||||
|  |     INTENT_RECOVERY = "recovery", | ||||||
| } | } | ||||||
|  |  | ||||||
| export function tokenByIdentifier(identifier: string): Promise<string> { | export class Token { | ||||||
|     return DefaultClient.fetch<TokenResponse>(["core", "tokens", identifier, "view_key"]).then( |  | ||||||
|  |     pk: string; | ||||||
|  |     identifier: string; | ||||||
|  |     intent: TokenIntent; | ||||||
|  |     user: User; | ||||||
|  |     description: string; | ||||||
|  |  | ||||||
|  |     expires: number; | ||||||
|  |     expiring: boolean; | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         throw Error(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static get(pk: string): Promise<User> { | ||||||
|  |         return DefaultClient.fetch<User>(["core", "tokens", pk]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<AKResponse<Token>> { | ||||||
|  |         return DefaultClient.fetch<AKResponse<Token>>(["core", "tokens"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static adminUrl(rest: string): string { | ||||||
|  |         return `/administration/tokens/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static userUrl(rest: string): string { | ||||||
|  |         return `/-/user/tokens/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static getKey(identifier: string): Promise<string> { | ||||||
|  |         return DefaultClient.fetch<{ key: string }>(["core", "tokens", identifier, "view_key"]).then( | ||||||
|             (r) => r.key |             (r) => r.key | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { DefaultClient, AKResponse } from "./Client"; | import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||||
|  |  | ||||||
| let _globalMePromise: Promise<User>; | let _globalMePromise: Promise<User>; | ||||||
|  |  | ||||||
| @ -9,11 +9,25 @@ export class User { | |||||||
|     is_superuser: boolean; |     is_superuser: boolean; | ||||||
|     email: boolean; |     email: boolean; | ||||||
|     avatar: string; |     avatar: string; | ||||||
|  |     is_active: boolean; | ||||||
|  |     last_login: number; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         throw Error(); |         throw Error(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     static get(pk: string): Promise<User> { | ||||||
|  |         return DefaultClient.fetch<User>(["core", "users", pk]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static list(filter?: QueryArguments): Promise<AKResponse<User>> { | ||||||
|  |         return DefaultClient.fetch<AKResponse<User>>(["core", "users"], filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static adminUrl(rest: string): string { | ||||||
|  |         return `/administration/users/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     static me(): Promise<User> { |     static me(): Promise<User> { | ||||||
|         if (!_globalMePromise) { |         if (!_globalMePromise) { | ||||||
|             _globalMePromise = DefaultClient.fetch<User>(["core", "users", "me"]); |             _globalMePromise = DefaultClient.fetch<User>(["core", "users", "me"]); | ||||||
|  | |||||||
| @ -85,10 +85,6 @@ select[multiple] { | |||||||
|     z-index: auto !important; |     z-index: auto !important; | ||||||
| } | } | ||||||
|  |  | ||||||
| .pf-c-page__main { |  | ||||||
|     display: block; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @media (prefers-color-scheme: dark) { | @media (prefers-color-scheme: dark) { | ||||||
|     :root { |     :root { | ||||||
|         --ak-dark-foreground: #fafafa; |         --ak-dark-foreground: #fafafa; | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu | |||||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||||
| import { tokenByIdentifier } from "../../api/Tokens"; | import { Token } from "../../api/Tokens"; | ||||||
| import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||||
|  |  | ||||||
| @customElement("ak-token-copy-button") | @customElement("ak-token-copy-button") | ||||||
| @ -35,7 +35,7 @@ export class TokenCopyButton extends LitElement { | |||||||
|             }, 1500); |             }, 1500); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         tokenByIdentifier(this.identifier).then((token) => { |         Token.getKey(this.identifier).then((token) => { | ||||||
|             navigator.clipboard.writeText(token).then(() => { |             navigator.clipboard.writeText(token).then(() => { | ||||||
|                 this.buttonClass = SUCCESS_CLASS; |                 this.buttonClass = SUCCESS_CLASS; | ||||||
|                 setTimeout(() => { |                 setTimeout(() => { | ||||||
|  | |||||||
| @ -47,6 +47,7 @@ export class MessageContainer extends LitElement { | |||||||
|         this.messageSocket = new WebSocket(wsUrl); |         this.messageSocket = new WebSocket(wsUrl); | ||||||
|         this.messageSocket.addEventListener("open", () => { |         this.messageSocket.addEventListener("open", () => { | ||||||
|             console.debug(`authentik/messages: connected to ${wsUrl}`); |             console.debug(`authentik/messages: connected to ${wsUrl}`); | ||||||
|  |             this.retryDelay = 200; | ||||||
|         }); |         }); | ||||||
|         this.messageSocket.addEventListener("close", (e) => { |         this.messageSocket.addEventListener("close", (e) => { | ||||||
|             console.debug(`authentik/messages: closed ws connection: ${e}`); |             console.debug(`authentik/messages: closed ws connection: ${e}`); | ||||||
|  | |||||||
| @ -1,8 +1,13 @@ | |||||||
| import { customElement, html, LitElement, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element"; | ||||||
|  | import { COMMON_STYLES } from "../../common/styles"; | ||||||
|  |  | ||||||
| @customElement("ak-notification-trigger") | @customElement("ak-notification-trigger") | ||||||
| export class NotificationRule extends LitElement { | export class NotificationRule extends LitElement { | ||||||
|  |  | ||||||
|  |     static get styles(): CSSResult[] { | ||||||
|  |         return COMMON_STYLES; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         super(); |         super(); | ||||||
|         this.addEventListener("click", () => { |         this.addEventListener("click", () => { | ||||||
| @ -16,7 +21,8 @@ export class NotificationRule extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     render(): TemplateResult { |     render(): TemplateResult { | ||||||
|         return html`<slot></slot>`; |         // TODO: Show icon with red dot when unread notifications exist | ||||||
|  |         return html`<i class="fas fa-bell pf-c-dropdown__toggle-icon" aria-hidden="true"></i>`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								web/src/elements/router/Router404.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								web/src/elements/router/Router404.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | import { gettext } from "django"; | ||||||
|  | import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||||
|  | import { COMMON_STYLES } from "../../common/styles"; | ||||||
|  |  | ||||||
|  | @customElement("ak-router-404") | ||||||
|  | export class Router404 extends LitElement { | ||||||
|  |  | ||||||
|  |     @property() | ||||||
|  |     url = ""; | ||||||
|  |  | ||||||
|  |     static get styles(): CSSResult[] { | ||||||
|  |         return COMMON_STYLES; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     render(): TemplateResult { | ||||||
|  |         return html`<div class="pf-c-empty-state pf-m-full-height"> | ||||||
|  |             <div class="pf-c-empty-state__content"> | ||||||
|  |                 <i class="fas fa-question-circle pf-c-empty-state__icon" aria-hidden="true"></i> | ||||||
|  |                 <h1 class="pf-c-title pf-m-lg">${gettext("Not found")}</h1> | ||||||
|  |                 <div class="pf-c-empty-state__body"> | ||||||
|  |                     ${gettext(`The url '${this.url}' was not found.`)} | ||||||
|  |                 </div> | ||||||
|  |                 <a href="#/" class="pf-c-button pf-m-primary" type="button">${gettext("Return home")}</a> | ||||||
|  |             </div> | ||||||
|  |         </div>`; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -10,6 +10,7 @@ import { ROUTES } from "../../routes"; | |||||||
| import { RouteMatch } from "./RouteMatch"; | import { RouteMatch } from "./RouteMatch"; | ||||||
|  |  | ||||||
| import "../../pages/generic/SiteShell"; | import "../../pages/generic/SiteShell"; | ||||||
|  | import "./Router404"; | ||||||
|  |  | ||||||
| @customElement("ak-router-outlet") | @customElement("ak-router-outlet") | ||||||
| export class RouterOutlet extends LitElement { | export class RouterOutlet extends LitElement { | ||||||
| @ -28,6 +29,11 @@ export class RouterOutlet extends LitElement { | |||||||
|                 :host { |                 :host { | ||||||
|                     height: 100vh; |                     height: 100vh; | ||||||
|                 } |                 } | ||||||
|  |                 *:first-child { | ||||||
|  |                     height: 100%; | ||||||
|  |                     display: flex; | ||||||
|  |                     flex-direction: column; | ||||||
|  |                 } | ||||||
|             `, |             `, | ||||||
|         ].concat(...COMMON_STYLES); |         ].concat(...COMMON_STYLES); | ||||||
|     } |     } | ||||||
| @ -62,12 +68,12 @@ export class RouterOutlet extends LitElement { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         if (!matchedRoute) { |         if (!matchedRoute) { | ||||||
|             console.debug(`authentik/router: route "${activeUrl}" not defined, defaulting to shell`); |             console.debug(`authentik/router: route "${activeUrl}" not defined`); | ||||||
|             const route = new Route( |             const route = new Route( | ||||||
|                 RegExp(""), |                 RegExp(""), | ||||||
|                 html`<ak-site-shell url=${activeUrl}> |                 html`<div class="pf-c-page__main"> | ||||||
|                     <div slot="body"></div> |                     <ak-router-404 url=${activeUrl}></ak-router-404> | ||||||
|                 </ak-site-shell>` |                 </div>` | ||||||
|             ); |             ); | ||||||
|             matchedRoute = new RouteMatch(route); |             matchedRoute = new RouteMatch(route); | ||||||
|             matchedRoute.arguments = route.url.exec(activeUrl)?.groups || {}; |             matchedRoute.arguments = route.url.exec(activeUrl)?.groups || {}; | ||||||
| @ -77,7 +83,6 @@ export class RouterOutlet extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     render(): TemplateResult | undefined { |     render(): TemplateResult | undefined { | ||||||
|         // TODO: Render 404 when current Route is empty |  | ||||||
|         return this.current?.render(); |         return this.current?.render(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ export class SidebarItem { | |||||||
|         this.condition = async () => true; |         this.condition = async () => true; | ||||||
|         this.activeMatchers = []; |         this.activeMatchers = []; | ||||||
|         if (this.path) { |         if (this.path) { | ||||||
|             this.activeMatchers.push(new RegExp(`^${this.path}`)); |             this.activeMatchers.push(new RegExp(`^${this.path}$`)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
| @ -37,11 +37,11 @@ export class SidebarUser extends LitElement { | |||||||
|     render(): TemplateResult { |     render(): TemplateResult { | ||||||
|         return html` |         return html` | ||||||
|             <a href="#/-/user/" class="pf-c-nav__link user-avatar" id="user-settings"> |             <a href="#/-/user/" class="pf-c-nav__link user-avatar" id="user-settings"> | ||||||
|                 ${until(User.me().then(u => { |                 ${until(User.me().then((u) => { | ||||||
|         return html`<img class="pf-c-avatar" src="${u.avatar}" alt="" />`;}), html``)} |                     return html`<img class="pf-c-avatar" src="${u.avatar}" alt="" />`; | ||||||
|  |                 }), html``)} | ||||||
|             </a> |             </a> | ||||||
|             <ak-notification-trigger class="pf-c-nav__link user-notifications"> |             <ak-notification-trigger class="pf-c-nav__link user-notifications"> | ||||||
|                 <i class="fas fa-bell pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |  | ||||||
|             </ak-notification-trigger> |             </ak-notification-trigger> | ||||||
|             <a href="/flows/-/default/invalidation/" class="pf-c-nav__link user-logout" id="logout"> |             <a href="/flows/-/default/invalidation/" class="pf-c-nav__link user-logout" id="logout"> | ||||||
|                 <i class="fas fa-sign-out-alt" aria-hidden="true"></i> |                 <i class="fas fa-sign-out-alt" aria-hidden="true"></i> | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ export class TablePagination extends LitElement { | |||||||
|                         <button |                         <button | ||||||
|                             class="pf-c-button pf-m-plain" |                             class="pf-c-button pf-m-plain" | ||||||
|                             @click=${() => { this.pageChangeHandler(this.pages?.next || 0); }} |                             @click=${() => { this.pageChangeHandler(this.pages?.next || 0); }} | ||||||
|                             ?disabled="${(this.pages?.next || 0) < 0}" |                             ?disabled="${(this.pages?.next || 0) <= 0}" | ||||||
|                             aria-label="${gettext("Go to next page")}" |                             aria-label="${gettext("Go to next page")}" | ||||||
|                         > |                         > | ||||||
|                             <i class="fas fa-angle-right" aria-hidden="true"></i> |                             <i class="fas fa-angle-right" aria-hidden="true"></i> | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | |||||||
|     new SidebarItem("Library", "/library"), |     new SidebarItem("Library", "/library"), | ||||||
|     new SidebarItem("Monitor").children( |     new SidebarItem("Monitor").children( | ||||||
|         new SidebarItem("Overview", "/administration/overview"), |         new SidebarItem("Overview", "/administration/overview"), | ||||||
|         new SidebarItem("System Tasks", "/administration/tasks/"), |         new SidebarItem("System Tasks", "/administration/system-tasks"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return User.me().then(u => u.is_superuser); | ||||||
|     }), |     }), | ||||||
| @ -20,37 +20,37 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | |||||||
|         return User.me().then(u => u.is_superuser); |         return User.me().then(u => u.is_superuser); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Resources").children( |     new SidebarItem("Resources").children( | ||||||
|         new SidebarItem("Applications", "/applications").activeWhen( |         new SidebarItem("Applications", "/core/applications").activeWhen( | ||||||
|             `^/applications/(?<slug>${SLUG_REGEX})$` |             `^/core/applications/(?<slug>${SLUG_REGEX})$` | ||||||
|         ), |         ), | ||||||
|         new SidebarItem("Sources", "/sources").activeWhen( |         new SidebarItem("Sources", "/core/sources").activeWhen( | ||||||
|             `^/sources/(?<slug>${SLUG_REGEX})$`, |             `^/core/sources/(?<slug>${SLUG_REGEX})$`, | ||||||
|         ), |         ), | ||||||
|         new SidebarItem("Providers", "/providers"), |         new SidebarItem("Providers", "/core/providers"), | ||||||
|         new SidebarItem("Outposts", "/outposts"), |         new SidebarItem("Outposts", "/outpost/outposts"), | ||||||
|         new SidebarItem("Outpost Service Connections", "/administration/outpost_service_connections/"), |         new SidebarItem("Outpost Service Connections", "/outpost/service-connections"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return User.me().then(u => u.is_superuser); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Customisation").children( |     new SidebarItem("Customisation").children( | ||||||
|         new SidebarItem("Policies", "/administration/policies/"), |         new SidebarItem("Policies", "/policy/policies"), | ||||||
|         new SidebarItem("Property Mappings", "/property-mappings"), |         new SidebarItem("Property Mappings", "/core/property-mappings"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return User.me().then(u => u.is_superuser); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Flows").children( |     new SidebarItem("Flows").children( | ||||||
|         new SidebarItem("Flows", "/flows").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})$`), |         new SidebarItem("Flows", "/flow/flows").activeWhen(`^/flow/flows/(?<slug>${SLUG_REGEX})$`), | ||||||
|         new SidebarItem("Stages", "/administration/stages/"), |         new SidebarItem("Stages", "/flow/stages"), | ||||||
|         new SidebarItem("Prompts", "/administration/stages_prompts/"), |         new SidebarItem("Prompts", "/flow/stages/prompts"), | ||||||
|         new SidebarItem("Invitations", "/administration/stages/invitations/"), |         new SidebarItem("Invitations", "/flow/stages/invitations"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return User.me().then(u => u.is_superuser); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Identity & Cryptography").children( |     new SidebarItem("Identity & Cryptography").children( | ||||||
|         new SidebarItem("User", "/administration/users/"), |         new SidebarItem("User", "/identity/users"), | ||||||
|         new SidebarItem("Groups", "/administration/groups/"), |         new SidebarItem("Groups", "/identity/groups"), | ||||||
|         new SidebarItem("Certificates", "/crypto/certificates"), |         new SidebarItem("Certificates", "/crypto/certificates"), | ||||||
|         new SidebarItem("Tokens", "/administration/tokens/"), |         new SidebarItem("Tokens", "/core/tokens"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return User.me().then(u => u.is_superuser); | ||||||
|     }), |     }), | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ import "./pages/admin-overview/AdminOverviewPage"; | |||||||
| import "./pages/admin-overview/TopApplicationsTable"; | import "./pages/admin-overview/TopApplicationsTable"; | ||||||
| import "./pages/applications/ApplicationListPage"; | import "./pages/applications/ApplicationListPage"; | ||||||
| import "./pages/applications/ApplicationViewPage"; | import "./pages/applications/ApplicationViewPage"; | ||||||
|  | import "./pages/tokens/UserTokenList"; | ||||||
| import "./pages/LibraryPage"; | import "./pages/LibraryPage"; | ||||||
|  |  | ||||||
| import "./elements/stages/authenticator_webauthn/WebAuthnRegister"; | import "./elements/stages/authenticator_webauthn/WebAuthnRegister"; | ||||||
|  | |||||||
| @ -14,6 +14,10 @@ export class LibraryApplication extends LitElement { | |||||||
|     static get styles(): CSSResult[] { |     static get styles(): CSSResult[] { | ||||||
|         return COMMON_STYLES.concat( |         return COMMON_STYLES.concat( | ||||||
|             css` |             css` | ||||||
|  |                 :host, | ||||||
|  |                 main { | ||||||
|  |                     height: 100%; | ||||||
|  |                 } | ||||||
|                 a { |                 a { | ||||||
|                     height: 100%; |                     height: 100%; | ||||||
|                 } |                 } | ||||||
|  | |||||||
| @ -36,7 +36,7 @@ export class AdminOverviewPage extends LitElement { | |||||||
|                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;"> |                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;"> | ||||||
|                     <ak-top-applications-table></ak-top-applications-table> |                     <ak-top-applications-table></ak-top-applications-table> | ||||||
|                 </ak-aggregate-card> |                 </ak-aggregate-card> | ||||||
|                 <ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/providers/"> |                 <ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/core/providers/"> | ||||||
|                 </ak-admin-status-card-provider> |                 </ak-admin-status-card-provider> | ||||||
|                 <ak-admin-status-card-policy-unbound class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-infrastructure" header="Policies" headerLink="#/administration/policies/"> |                 <ak-admin-status-card-policy-unbound class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-infrastructure" header="Policies" headerLink="#/administration/policies/"> | ||||||
|                 </ak-admin-status-card-policy-unbound> |                 </ak-admin-status-card-policy-unbound> | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ export class ApplicationListPage extends TablePage<Application> { | |||||||
|             item.meta_icon ? |             item.meta_icon ? | ||||||
|                 html`<img class="app-icon pf-c-avatar" src="${item.meta_icon}" alt="${gettext("Application Icon")}">` : |                 html`<img class="app-icon pf-c-avatar" src="${item.meta_icon}" alt="${gettext("Application Icon")}">` : | ||||||
|                 html`<i class="pf-icon pf-icon-arrow"></i>`, |                 html`<i class="pf-icon pf-icon-arrow"></i>`, | ||||||
|             html`<a href="#/applications/${item.slug}"> |             html`<a href="#/core/applications/${item.slug}"> | ||||||
|                 <div> |                 <div> | ||||||
|                     ${item.name} |                     ${item.name} | ||||||
|                 </div> |                 </div> | ||||||
|  | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer