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 | ||||
| 	pylint authentik tests lifecycle | ||||
|  | ||||
| gen: coverage | ||||
| gen: | ||||
| 	./manage.py generate_swagger -o swagger.yaml -f yaml | ||||
|  | ||||
| local-stack: | ||||
| @ -31,7 +31,5 @@ local-stack: | ||||
| 	docker-compose up -d | ||||
| 	docker-compose run --rm server migrate | ||||
|  | ||||
| build-static: | ||||
| 	docker-compose -f scripts/ci.docker-compose.yml up -d | ||||
| 	docker build -t beryju/authentik-static -f static.Dockerfile --network=scripts_default . | ||||
| 	docker-compose -f scripts/ci.docker-compose.yml down -v | ||||
| run: | ||||
| 	go run -v cmd/server/main.go | ||||
|  | ||||
| @ -1,14 +1,18 @@ | ||||
| """User API Views""" | ||||
| from json import loads | ||||
|  | ||||
| from django.http.response import Http404 | ||||
| from django.urls import reverse_lazy | ||||
| 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 guardian.utils import get_anonymous_user | ||||
| from rest_framework.decorators import action | ||||
| from rest_framework.fields import CharField, JSONField, SerializerMethodField | ||||
| from rest_framework.request import Request | ||||
| 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 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): | ||||
|     """User Viewset""" | ||||
|  | ||||
|     queryset = User.objects.none() | ||||
|     serializer_class = UserSerializer | ||||
|     search_fields = ["username", "name", "is_active"] | ||||
|     filterset_fields = ["username", "name", "is_active"] | ||||
|     filterset_class = UsersFilter | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         return User.objects.all().exclude(pk=get_anonymous_user().pk) | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package web | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/http/httputil" | ||||
| 	"net/url" | ||||
| ) | ||||
| @ -9,5 +10,11 @@ func (ws *WebServer) configureProxy() { | ||||
| 	// Reverse proxy to the application server | ||||
| 	u, _ := url.Parse("http://localhost:8000") | ||||
| 	rp := httputil.NewSingleHostReverseProxy(u) | ||||
| 	rp.ErrorHandler = ws.proxyErrorHandler | ||||
| 	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: '' | ||||
|           required: false | ||||
|           type: string | ||||
|         - name: attributes | ||||
|           in: query | ||||
|           description: '' | ||||
|           required: false | ||||
|           type: string | ||||
|         - name: ordering | ||||
|           in: query | ||||
|           description: Which field to use when ordering the results. | ||||
|  | ||||
| @ -56,7 +56,7 @@ export class PlexSourceForm extends Form<PlexSource> { | ||||
|     }; | ||||
|  | ||||
|     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); | ||||
|         PlexAPIClient.pinPoll(this.source?.clientId || "", authInfo.pin.id).then(token => { | ||||
|             authWindow?.close(); | ||||
|  | ||||
| @ -35,12 +35,18 @@ export class UserListPage extends TablePage<User> { | ||||
|     @property() | ||||
|     order = "last_login"; | ||||
|  | ||||
|     @property({ type: Boolean }) | ||||
|     hideServiceAccounts = true; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<User>> { | ||||
|         return new CoreApi(DEFAULT_CONFIG).coreUsersList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             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