diff --git a/backend/internal/controller/oidc_controller.go b/backend/internal/controller/oidc_controller.go index 023e8fcb..2489337e 100644 --- a/backend/internal/controller/oidc_controller.go +++ b/backend/internal/controller/oidc_controller.go @@ -377,7 +377,7 @@ func (oc *OidcController) getClientHandler(c *gin.Context) { // @Param limit query int false "Number of items per page" default(10) // @Param sort_column query string false "Column to sort by" default("name") // @Param sort_direction query string false "Sort direction (asc or desc)" default("asc") -// @Success 200 {object} dto.Paginated[dto.OidcClientDto] +// @Success 200 {object} dto.Paginated[dto.OidcClientWithAllowedGroupsCountDto] // @Security BearerAuth // @Router /api/oidc/clients [get] func (oc *OidcController) listClientsHandler(c *gin.Context) { @@ -394,13 +394,23 @@ func (oc *OidcController) listClientsHandler(c *gin.Context) { return } - var clientsDto []dto.OidcClientDto - if err := dto.MapStructList(clients, &clientsDto); err != nil { - _ = c.Error(err) - return + // Map the user groups to DTOs + var clientsDto = make([]dto.OidcClientWithAllowedGroupsCountDto, len(clients)) + for i, client := range clients { + var clientDto dto.OidcClientWithAllowedGroupsCountDto + if err := dto.MapStruct(client, &clientDto); err != nil { + _ = c.Error(err) + return + } + clientDto.AllowedUserGroupsCount, err = oc.oidcService.GetAllowedGroupsCountOfClient(c, client.ID) + if err != nil { + _ = c.Error(err) + return + } + clientsDto[i] = clientDto } - c.JSON(http.StatusOK, dto.Paginated[dto.OidcClientDto]{ + c.JSON(http.StatusOK, dto.Paginated[dto.OidcClientWithAllowedGroupsCountDto]{ Data: clientsDto, Pagination: pagination, }) diff --git a/backend/internal/dto/oidc_dto.go b/backend/internal/dto/oidc_dto.go index e4244769..edbcf53b 100644 --- a/backend/internal/dto/oidc_dto.go +++ b/backend/internal/dto/oidc_dto.go @@ -19,6 +19,11 @@ type OidcClientWithAllowedUserGroupsDto struct { AllowedUserGroups []UserGroupDtoWithUserCount `json:"allowedUserGroups"` } +type OidcClientWithAllowedGroupsCountDto struct { + OidcClientDto + AllowedUserGroupsCount int64 `json:"allowedUserGroupsCount"` +} + type OidcClientCreateDto struct { Name string `json:"name" binding:"required,max=50"` CallbackURLs []string `json:"callbackURLs" binding:"required"` diff --git a/backend/internal/service/oidc_service.go b/backend/internal/service/oidc_service.go index 1dea7785..30d82fd4 100644 --- a/backend/internal/service/oidc_service.go +++ b/backend/internal/service/oidc_service.go @@ -512,24 +512,32 @@ func (s *OidcService) getClientInternal(ctx context.Context, clientID string, tx return client, nil } -func (s *OidcService) ListClients(ctx context.Context, searchTerm string, sortedPaginationRequest utils.SortedPaginationRequest) ([]model.OidcClient, utils.PaginationResponse, error) { +func (s *OidcService) ListClients(ctx context.Context, name string, sortedPaginationRequest utils.SortedPaginationRequest) ([]model.OidcClient, utils.PaginationResponse, error) { var clients []model.OidcClient query := s.db. WithContext(ctx). Preload("CreatedBy"). Model(&model.OidcClient{}) - if searchTerm != "" { - searchPattern := "%" + searchTerm + "%" - query = query.Where("name LIKE ?", searchPattern) + + if name != "" { + query = query.Where("name LIKE ?", "%"+name+"%") } - pagination, err := utils.PaginateAndSort(sortedPaginationRequest, query, &clients) - if err != nil { - return nil, utils.PaginationResponse{}, err + // As allowedUserGroupsCount is not a column, we need to manually sort it + isValidSortDirection := sortedPaginationRequest.Sort.Direction == "asc" || sortedPaginationRequest.Sort.Direction == "desc" + if sortedPaginationRequest.Sort.Column == "allowedUserGroupsCount" && isValidSortDirection { + query = query.Select("oidc_clients.*, COUNT(oidc_clients_allowed_user_groups.oidc_client_id)"). + Joins("LEFT JOIN oidc_clients_allowed_user_groups ON oidc_clients.id = oidc_clients_allowed_user_groups.oidc_client_id"). + Group("oidc_clients.id"). + Order("COUNT(oidc_clients_allowed_user_groups.oidc_client_id) " + sortedPaginationRequest.Sort.Direction) + + response, err := utils.Paginate(sortedPaginationRequest.Pagination.Page, sortedPaginationRequest.Pagination.Limit, query, &clients) + return clients, response, err } - return clients, pagination, nil + response, err := utils.PaginateAndSort(sortedPaginationRequest, query, &clients) + return clients, response, err } func (s *OidcService) CreateClient(ctx context.Context, input dto.OidcClientCreateDto, userID string) (model.OidcClient, error) { @@ -1166,6 +1174,17 @@ func (s *OidcService) GetDeviceCodeInfo(ctx context.Context, userCode string, us }, nil } +func (s *OidcService) GetAllowedGroupsCountOfClient(ctx context.Context, id string) (int64, error) { + var client model.OidcClient + err := s.db.WithContext(ctx).Where("id = ?", id).First(&client).Error + if err != nil { + return 0, err + } + + count := s.db.WithContext(ctx).Model(&client).Association("AllowedUserGroups").Count() + return count, nil +} + func (s *OidcService) createRefreshToken(ctx context.Context, clientID string, userID string, scope string, tx *gorm.DB) (string, error) { refreshToken, err := utils.GenerateRandomAlphanumericString(40) if err != nil { diff --git a/frontend/messages/en-US.json b/frontend/messages/en-US.json index 6f9ad843..6176f371 100644 --- a/frontend/messages/en-US.json +++ b/frontend/messages/en-US.json @@ -346,5 +346,7 @@ "authorize_device": "Authorize Device", "the_device_has_been_authorized": "The device has been authorized.", "enter_code_displayed_in_previous_step": "Enter the code that was displayed in the previous step.", - "authorize": "Authorize" + "authorize": "Authorize", + "oidc_allowed_group_count": "Allowed Group Count", + "unrestricted": "Unrestricted" } diff --git a/frontend/src/lib/services/oidc-service.ts b/frontend/src/lib/services/oidc-service.ts index fadec312..947ab4d4 100644 --- a/frontend/src/lib/services/oidc-service.ts +++ b/frontend/src/lib/services/oidc-service.ts @@ -1,10 +1,11 @@ import type { AuthorizeResponse, - OidcDeviceCodeInfo, OidcClient, OidcClientCreate, OidcClientMetaData, - OidcClientWithAllowedUserGroups + OidcClientWithAllowedUserGroups, + OidcClientWithAllowedUserGroupsCount, + OidcDeviceCodeInfo } from '$lib/types/oidc.type'; import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type'; import APIService from './api-service'; @@ -43,7 +44,7 @@ class OidcService extends APIService { const res = await this.api.get('/oidc/clients', { params: options }); - return res.data as Paginated; + return res.data as Paginated; } async createClient(client: OidcClientCreate) { diff --git a/frontend/src/lib/types/oidc.type.ts b/frontend/src/lib/types/oidc.type.ts index 6ea82d63..7c82e218 100644 --- a/frontend/src/lib/types/oidc.type.ts +++ b/frontend/src/lib/types/oidc.type.ts @@ -17,6 +17,10 @@ export type OidcClientWithAllowedUserGroups = OidcClient & { allowedUserGroups: UserGroup[]; }; +export type OidcClientWithAllowedUserGroupsCount = OidcClient & { + allowedUserGroupsCount: number; +}; + export type OidcClientCreate = Omit; export type OidcClientCreateWithLogo = OidcClientCreate & { diff --git a/frontend/src/routes/settings/admin/oidc-clients/oidc-client-list.svelte b/frontend/src/routes/settings/admin/oidc-clients/oidc-client-list.svelte index ef1cfbd6..7cbc059b 100644 --- a/frontend/src/routes/settings/admin/oidc-clients/oidc-client-list.svelte +++ b/frontend/src/routes/settings/admin/oidc-clients/oidc-client-list.svelte @@ -5,7 +5,7 @@ import * as Table from '$lib/components/ui/table'; import { m } from '$lib/paraglide/messages'; import OIDCService from '$lib/services/oidc-service'; - import type { OidcClient } from '$lib/types/oidc.type'; + import type { OidcClient, OidcClientWithAllowedUserGroupsCount } from '$lib/types/oidc.type'; import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type'; import { axiosErrorToast } from '$lib/utils/error-util'; import { LucidePencil, LucideTrash } from '@lucide/svelte'; @@ -15,7 +15,7 @@ clients = $bindable(), requestOptions }: { - clients: Paginated; + clients: Paginated; requestOptions: SearchPaginationSortRequest; } = $props(); @@ -49,6 +49,7 @@ columns={[ { label: m.logo() }, { label: m.name(), sortColumn: 'name' }, + { label: m.oidc_allowed_group_count(), sortColumn: 'allowedUserGroupsCount' }, { label: m.actions(), hidden: true } ]} > @@ -67,6 +68,11 @@ {/if} {item.name} + {item.allowedUserGroupsCount > 0 + ? item.allowedUserGroupsCount + : m.unrestricted()}