web/admin: show matching user reputation scores in user details (#10276)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		 Marc 'risson' Schmitt
					Marc 'risson' Schmitt
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							8915904cc7
						
					
				
				
					commit
					98c8402f11
				
			| @ -1,6 +1,8 @@ | ||||
| """Reputation policy API Views""" | ||||
|  | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
| from django_filters.filters import BaseInFilter, CharFilter | ||||
| from django_filters.filterset import FilterSet | ||||
| from rest_framework import mixins | ||||
| from rest_framework.exceptions import ValidationError | ||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet | ||||
| @ -11,6 +13,10 @@ from authentik.policies.api.policies import PolicySerializer | ||||
| from authentik.policies.reputation.models import Reputation, ReputationPolicy | ||||
|  | ||||
|  | ||||
| class CharInFilter(BaseInFilter, CharFilter): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class ReputationPolicySerializer(PolicySerializer): | ||||
|     """Reputation Policy Serializer""" | ||||
|  | ||||
| @ -38,6 +44,16 @@ class ReputationPolicyViewSet(UsedByMixin, ModelViewSet): | ||||
|     ordering = ["name"] | ||||
|  | ||||
|  | ||||
| class ReputationFilter(FilterSet): | ||||
|     """Filter for reputation""" | ||||
|  | ||||
|     identifier_in = CharInFilter(field_name="identifier", lookup_expr="in") | ||||
|  | ||||
|     class Meta: | ||||
|         model = Reputation | ||||
|         fields = ["identifier", "ip", "score"] | ||||
|  | ||||
|  | ||||
| class ReputationSerializer(ModelSerializer): | ||||
|     """Reputation Serializer""" | ||||
|  | ||||
| @ -66,5 +82,5 @@ class ReputationViewSet( | ||||
|     queryset = Reputation.objects.all() | ||||
|     serializer_class = ReputationSerializer | ||||
|     search_fields = ["identifier", "ip", "score"] | ||||
|     filterset_fields = ["identifier", "ip", "score"] | ||||
|     filterset_class = ReputationFilter | ||||
|     ordering = ["ip"] | ||||
|  | ||||
| @ -13080,6 +13080,15 @@ paths: | ||||
|         name: identifier | ||||
|         schema: | ||||
|           type: string | ||||
|       - in: query | ||||
|         name: identifier_in | ||||
|         schema: | ||||
|           type: array | ||||
|           items: | ||||
|             type: string | ||||
|         description: Multiple values may be separated by commas. | ||||
|         explode: false | ||||
|         style: form | ||||
|       - in: query | ||||
|         name: ip | ||||
|         schema: | ||||
|  | ||||
| @ -36,11 +36,13 @@ import "@goauthentik/elements/oauth/UserRefreshTokenList"; | ||||
| import "@goauthentik/elements/rbac/ObjectPermissionsPage"; | ||||
| import "@goauthentik/elements/user/SessionList"; | ||||
| import "@goauthentik/elements/user/UserConsentList"; | ||||
| import "@goauthentik/elements/user/UserReputationList"; | ||||
| import "@goauthentik/elements/user/sources/SourceSettings"; | ||||
|  | ||||
| import { msg, str } from "@lit/localize"; | ||||
| import { TemplateResult, css, html, nothing } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators.js"; | ||||
| import { ifDefined } from "lit/directives/if-defined.js"; | ||||
|  | ||||
| import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; | ||||
| import PFButton from "@patternfly/patternfly/components/Button/button.css"; | ||||
| @ -274,6 +276,21 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) { | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </section> | ||||
|                 <section | ||||
|                     slot="page-reputation" | ||||
|                     data-tab-title="${msg("Reputation scores")}" | ||||
|                     class="pf-c-page__main-section pf-m-no-padding-mobile" | ||||
|                 > | ||||
|                     <div class="pf-c-card"> | ||||
|                         <div class="pf-c-card__body"> | ||||
|                             <ak-user-reputation-list | ||||
|                                 targetUsername=${user.username} | ||||
|                                 targetEmail=${ifDefined(user.email)} | ||||
|                             > | ||||
|                             </ak-user-reputation-list> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </section> | ||||
|                 <section | ||||
|                     slot="page-consent" | ||||
|                     data-tab-title="${msg("Explicit Consent")}" | ||||
|  | ||||
							
								
								
									
										83
									
								
								web/src/elements/user/UserReputationList.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								web/src/elements/user/UserReputationList.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import { uiConfig } from "@goauthentik/common/ui/config"; | ||||
| import { getRelativeTime } from "@goauthentik/common/utils"; | ||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||
| import { PaginatedResponse } from "@goauthentik/elements/table/Table"; | ||||
| import { Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||
| import getUnicodeFlagIcon from "country-flag-icons/unicode"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, html } from "lit"; | ||||
| import { customElement, property } from "lit/decorators.js"; | ||||
|  | ||||
| import { PoliciesApi, Reputation } from "@goauthentik/api"; | ||||
|  | ||||
| @customElement("ak-user-reputation-list") | ||||
| export class UserReputationList extends Table<Reputation> { | ||||
|     @property() | ||||
|     targetUsername!: string; | ||||
|  | ||||
|     @property() | ||||
|     targetEmail!: string | undefined; | ||||
|  | ||||
|     async apiEndpoint(page: number): Promise<PaginatedResponse<Reputation>> { | ||||
|         const identifiers = [this.targetUsername]; | ||||
|         if (this.targetEmail !== undefined) { | ||||
|             identifiers.push(this.targetEmail); | ||||
|         } | ||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresList({ | ||||
|             identifierIn: identifiers, | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             pageSize: (await uiConfig()).pagination.perPage, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     checkbox = true; | ||||
|     clearOnRefresh = true; | ||||
|     order = "identifier"; | ||||
|  | ||||
|     columns(): TableColumn[] { | ||||
|         return [ | ||||
|             new TableColumn(msg("Identifier"), "identifier"), | ||||
|             new TableColumn(msg("IP"), "ip"), | ||||
|             new TableColumn(msg("Score"), "score"), | ||||
|             new TableColumn(msg("Updated"), "updated"), | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     renderToolbarSelected(): TemplateResult { | ||||
|         const disabled = this.selectedElements.length < 1; | ||||
|         return html`<ak-forms-delete-bulk | ||||
|             objectLabel=${msg("Reputation score(s)")} | ||||
|             .objects=${this.selectedElements} | ||||
|             .usedBy=${(item: Reputation) => { | ||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresUsedByList({ | ||||
|                     reputationUuid: item.pk || "", | ||||
|                 }); | ||||
|             }} | ||||
|             .delete=${(item: Reputation) => { | ||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresDestroy({ | ||||
|                     reputationUuid: item.pk || "", | ||||
|                 }); | ||||
|             }} | ||||
|         > | ||||
|             <button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger"> | ||||
|                 ${msg("Delete")} | ||||
|             </button> | ||||
|         </ak-forms-delete-bulk>`; | ||||
|     } | ||||
|  | ||||
|     row(item: Reputation): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.identifier}`, | ||||
|             html`${item.ipGeoData?.country | ||||
|                 ? html` ${getUnicodeFlagIcon(item.ipGeoData.country)} ` | ||||
|                 : html``} | ||||
|             ${item.ip}`, | ||||
|             html`${item.score}`, | ||||
|             html`<div>${getRelativeTime(item.updated)}</div> | ||||
|                 <small>${item.updated.toLocaleString()}</small>`, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user