web/admin: filter out service accounts by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		
							
								
								
									
										8
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								Makefile
									
									
									
									
									
								
							| @ -22,7 +22,7 @@ lint: | |||||||
| 	bandit -r authentik tests lifecycle -x node_modules | 	bandit -r authentik tests lifecycle -x node_modules | ||||||
| 	pylint authentik tests lifecycle | 	pylint authentik tests lifecycle | ||||||
|  |  | ||||||
| gen: coverage | gen: | ||||||
| 	./manage.py generate_swagger -o swagger.yaml -f yaml | 	./manage.py generate_swagger -o swagger.yaml -f yaml | ||||||
|  |  | ||||||
| local-stack: | local-stack: | ||||||
| @ -31,7 +31,5 @@ local-stack: | |||||||
| 	docker-compose up -d | 	docker-compose up -d | ||||||
| 	docker-compose run --rm server migrate | 	docker-compose run --rm server migrate | ||||||
|  |  | ||||||
| build-static: | run: | ||||||
| 	docker-compose -f scripts/ci.docker-compose.yml up -d | 	go run -v cmd/server/main.go | ||||||
| 	docker build -t beryju/authentik-static -f static.Dockerfile --network=scripts_default . |  | ||||||
| 	docker-compose -f scripts/ci.docker-compose.yml down -v |  | ||||||
|  | |||||||
| @ -1,14 +1,18 @@ | |||||||
| """User API Views""" | """User API Views""" | ||||||
|  | from json import loads | ||||||
|  |  | ||||||
| from django.http.response import Http404 | from django.http.response import Http404 | ||||||
| from django.urls import reverse_lazy | from django.urls import reverse_lazy | ||||||
| from django.utils.http import urlencode | from django.utils.http import urlencode | ||||||
|  | from django_filters.filters import CharFilter | ||||||
|  | from django_filters.filterset import FilterSet | ||||||
| from drf_yasg.utils import swagger_auto_schema, swagger_serializer_method | from drf_yasg.utils import swagger_auto_schema, swagger_serializer_method | ||||||
| from guardian.utils import get_anonymous_user | from guardian.utils import get_anonymous_user | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.fields import CharField, JSONField, SerializerMethodField | from rest_framework.fields import CharField, JSONField, SerializerMethodField | ||||||
| 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 BooleanField, ModelSerializer | from rest_framework.serializers import BooleanField, ModelSerializer, ValidationError | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | ||||||
| @ -84,13 +88,42 @@ class UserMetricsSerializer(PassiveSerializer): | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class UsersFilter(FilterSet): | ||||||
|  |     """Filter for users""" | ||||||
|  |  | ||||||
|  |     attributes = CharFilter( | ||||||
|  |         field_name="attributes", | ||||||
|  |         lookup_expr="", | ||||||
|  |         label="Attributes", | ||||||
|  |         method="filter_attributes", | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     # pylint: disable=unused-argument | ||||||
|  |     def filter_attributes(self, queryset, name, value): | ||||||
|  |         """Filter attributes by query args""" | ||||||
|  |         try: | ||||||
|  |             value = loads(value) | ||||||
|  |         except ValueError: | ||||||
|  |             raise ValidationError(detail="filter: failed to parse JSON") | ||||||
|  |         if not isinstance(value, dict): | ||||||
|  |             raise ValidationError(detail="filter: value must be key:value mapping") | ||||||
|  |         qs = {} | ||||||
|  |         for key, _value in value.items(): | ||||||
|  |             qs[f"attributes__{key}"] = _value | ||||||
|  |         return queryset.filter(**qs) | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         model = User | ||||||
|  |         fields = ["username", "name", "is_active", "attributes"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserViewSet(ModelViewSet): | class UserViewSet(ModelViewSet): | ||||||
|     """User Viewset""" |     """User Viewset""" | ||||||
|  |  | ||||||
|     queryset = User.objects.none() |     queryset = User.objects.none() | ||||||
|     serializer_class = UserSerializer |     serializer_class = UserSerializer | ||||||
|     search_fields = ["username", "name", "is_active"] |     search_fields = ["username", "name", "is_active"] | ||||||
|     filterset_fields = ["username", "name", "is_active"] |     filterset_class = UsersFilter | ||||||
|  |  | ||||||
|     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,7 @@ | |||||||
| package web | package web | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"net/http" | ||||||
| 	"net/http/httputil" | 	"net/http/httputil" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| ) | ) | ||||||
| @ -9,5 +10,11 @@ func (ws *WebServer) configureProxy() { | |||||||
| 	// Reverse proxy to the application server | 	// Reverse proxy to the application server | ||||||
| 	u, _ := url.Parse("http://localhost:8000") | 	u, _ := url.Parse("http://localhost:8000") | ||||||
| 	rp := httputil.NewSingleHostReverseProxy(u) | 	rp := httputil.NewSingleHostReverseProxy(u) | ||||||
|  | 	rp.ErrorHandler = ws.proxyErrorHandler | ||||||
| 	ws.m.PathPrefix("/").Handler(rp) | 	ws.m.PathPrefix("/").Handler(rp) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request, err error) { | ||||||
|  | 	ws.log.WithError(err).Warning("proxy error") | ||||||
|  | 	rw.WriteHeader(http.StatusBadGateway) | ||||||
|  | } | ||||||
|  | |||||||
| @ -1994,6 +1994,11 @@ paths: | |||||||
|           description: '' |           description: '' | ||||||
|           required: false |           required: false | ||||||
|           type: string |           type: string | ||||||
|  |         - name: attributes | ||||||
|  |           in: query | ||||||
|  |           description: '' | ||||||
|  |           required: false | ||||||
|  |           type: string | ||||||
|         - name: ordering |         - name: ordering | ||||||
|           in: query |           in: query | ||||||
|           description: Which field to use when ordering the results. |           description: Which field to use when ordering the results. | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ export class PlexSourceForm extends Form<PlexSource> { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     async doAuth(): Promise<void> { |     async doAuth(): Promise<void> { | ||||||
|         const authInfo = await PlexAPIClient.getPin(this.source?.clientId); |         const authInfo = await PlexAPIClient.getPin(this.source?.clientId || ""); | ||||||
|         const authWindow = popupCenterScreen(authInfo.authUrl, "plex auth", 550, 700); |         const authWindow = popupCenterScreen(authInfo.authUrl, "plex auth", 550, 700); | ||||||
|         PlexAPIClient.pinPoll(this.source?.clientId || "", authInfo.pin.id).then(token => { |         PlexAPIClient.pinPoll(this.source?.clientId || "", authInfo.pin.id).then(token => { | ||||||
|             authWindow?.close(); |             authWindow?.close(); | ||||||
|  | |||||||
| @ -35,12 +35,18 @@ export class UserListPage extends TablePage<User> { | |||||||
|     @property() |     @property() | ||||||
|     order = "last_login"; |     order = "last_login"; | ||||||
|  |  | ||||||
|  |     @property({ type: Boolean }) | ||||||
|  |     hideServiceAccounts = true; | ||||||
|  |  | ||||||
|     apiEndpoint(page: number): Promise<AKResponse<User>> { |     apiEndpoint(page: number): Promise<AKResponse<User>> { | ||||||
|         return new CoreApi(DEFAULT_CONFIG).coreUsersList({ |         return new CoreApi(DEFAULT_CONFIG).coreUsersList({ | ||||||
|             ordering: this.order, |             ordering: this.order, | ||||||
|             page: page, |             page: page, | ||||||
|             pageSize: PAGE_SIZE, |             pageSize: PAGE_SIZE, | ||||||
|             search: this.search || "", |             search: this.search || "", | ||||||
|  |             attributes: this.hideServiceAccounts ? JSON.stringify({ | ||||||
|  |                 "goauthentik.io/user/service-account__isnull": "true" | ||||||
|  |             }) : undefined | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -163,4 +169,29 @@ export class UserListPage extends TablePage<User> { | |||||||
|         `; |         `; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     renderToolbarAfter(): TemplateResult { | ||||||
|  |         return html`  | ||||||
|  |         <div class="pf-c-toolbar__group pf-m-filter-group"> | ||||||
|  |             <div class="pf-c-toolbar__item pf-m-search-filter"> | ||||||
|  |                 <div class="pf-c-input-group"> | ||||||
|  |                     <div class="pf-c-check"> | ||||||
|  |                         <input class="pf-c-check__input" | ||||||
|  |                             type="checkbox" | ||||||
|  |                             id="hide-service-accounts" | ||||||
|  |                             name="hide-service-accounts" | ||||||
|  |                             ?checked=${this.hideServiceAccounts} | ||||||
|  |                             @change=${() => { | ||||||
|  |                                 this.hideServiceAccounts = !this.hideServiceAccounts; | ||||||
|  |                                 this.page = 1; | ||||||
|  |                                 this.fetch(); | ||||||
|  |                             }} /> | ||||||
|  |                         <label class="pf-c-check__label" for="hide-service-accounts"> | ||||||
|  |                             ${t`Hide service-accounts`} | ||||||
|  |                         </label> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div>`; | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer