mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-02-04 15:39:45 +00:00
feat: show allowed group count on oidc client list (#567)
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
@@ -377,7 +377,7 @@ func (oc *OidcController) getClientHandler(c *gin.Context) {
|
|||||||
// @Param limit query int false "Number of items per page" default(10)
|
// @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_column query string false "Column to sort by" default("name")
|
||||||
// @Param sort_direction query string false "Sort direction (asc or desc)" default("asc")
|
// @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
|
// @Security BearerAuth
|
||||||
// @Router /api/oidc/clients [get]
|
// @Router /api/oidc/clients [get]
|
||||||
func (oc *OidcController) listClientsHandler(c *gin.Context) {
|
func (oc *OidcController) listClientsHandler(c *gin.Context) {
|
||||||
@@ -394,13 +394,23 @@ func (oc *OidcController) listClientsHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientsDto []dto.OidcClientDto
|
// Map the user groups to DTOs
|
||||||
if err := dto.MapStructList(clients, &clientsDto); err != nil {
|
var clientsDto = make([]dto.OidcClientWithAllowedGroupsCountDto, len(clients))
|
||||||
_ = c.Error(err)
|
for i, client := range clients {
|
||||||
return
|
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,
|
Data: clientsDto,
|
||||||
Pagination: pagination,
|
Pagination: pagination,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ type OidcClientWithAllowedUserGroupsDto struct {
|
|||||||
AllowedUserGroups []UserGroupDtoWithUserCount `json:"allowedUserGroups"`
|
AllowedUserGroups []UserGroupDtoWithUserCount `json:"allowedUserGroups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OidcClientWithAllowedGroupsCountDto struct {
|
||||||
|
OidcClientDto
|
||||||
|
AllowedUserGroupsCount int64 `json:"allowedUserGroupsCount"`
|
||||||
|
}
|
||||||
|
|
||||||
type OidcClientCreateDto struct {
|
type OidcClientCreateDto struct {
|
||||||
Name string `json:"name" binding:"required,max=50"`
|
Name string `json:"name" binding:"required,max=50"`
|
||||||
CallbackURLs []string `json:"callbackURLs" binding:"required"`
|
CallbackURLs []string `json:"callbackURLs" binding:"required"`
|
||||||
|
|||||||
@@ -512,24 +512,32 @@ func (s *OidcService) getClientInternal(ctx context.Context, clientID string, tx
|
|||||||
return client, nil
|
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
|
var clients []model.OidcClient
|
||||||
|
|
||||||
query := s.db.
|
query := s.db.
|
||||||
WithContext(ctx).
|
WithContext(ctx).
|
||||||
Preload("CreatedBy").
|
Preload("CreatedBy").
|
||||||
Model(&model.OidcClient{})
|
Model(&model.OidcClient{})
|
||||||
if searchTerm != "" {
|
|
||||||
searchPattern := "%" + searchTerm + "%"
|
if name != "" {
|
||||||
query = query.Where("name LIKE ?", searchPattern)
|
query = query.Where("name LIKE ?", "%"+name+"%")
|
||||||
}
|
}
|
||||||
|
|
||||||
pagination, err := utils.PaginateAndSort(sortedPaginationRequest, query, &clients)
|
// As allowedUserGroupsCount is not a column, we need to manually sort it
|
||||||
if err != nil {
|
isValidSortDirection := sortedPaginationRequest.Sort.Direction == "asc" || sortedPaginationRequest.Sort.Direction == "desc"
|
||||||
return nil, utils.PaginationResponse{}, err
|
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) {
|
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
|
}, 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) {
|
func (s *OidcService) createRefreshToken(ctx context.Context, clientID string, userID string, scope string, tx *gorm.DB) (string, error) {
|
||||||
refreshToken, err := utils.GenerateRandomAlphanumericString(40)
|
refreshToken, err := utils.GenerateRandomAlphanumericString(40)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -346,5 +346,7 @@
|
|||||||
"authorize_device": "Authorize Device",
|
"authorize_device": "Authorize Device",
|
||||||
"the_device_has_been_authorized": "The device has been authorized.",
|
"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.",
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type {
|
import type {
|
||||||
AuthorizeResponse,
|
AuthorizeResponse,
|
||||||
OidcDeviceCodeInfo,
|
|
||||||
OidcClient,
|
OidcClient,
|
||||||
OidcClientCreate,
|
OidcClientCreate,
|
||||||
OidcClientMetaData,
|
OidcClientMetaData,
|
||||||
OidcClientWithAllowedUserGroups
|
OidcClientWithAllowedUserGroups,
|
||||||
|
OidcClientWithAllowedUserGroupsCount,
|
||||||
|
OidcDeviceCodeInfo
|
||||||
} from '$lib/types/oidc.type';
|
} from '$lib/types/oidc.type';
|
||||||
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
||||||
import APIService from './api-service';
|
import APIService from './api-service';
|
||||||
@@ -43,7 +44,7 @@ class OidcService extends APIService {
|
|||||||
const res = await this.api.get('/oidc/clients', {
|
const res = await this.api.get('/oidc/clients', {
|
||||||
params: options
|
params: options
|
||||||
});
|
});
|
||||||
return res.data as Paginated<OidcClient>;
|
return res.data as Paginated<OidcClientWithAllowedUserGroupsCount>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createClient(client: OidcClientCreate) {
|
async createClient(client: OidcClientCreate) {
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ export type OidcClientWithAllowedUserGroups = OidcClient & {
|
|||||||
allowedUserGroups: UserGroup[];
|
allowedUserGroups: UserGroup[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OidcClientWithAllowedUserGroupsCount = OidcClient & {
|
||||||
|
allowedUserGroupsCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type OidcClientCreate = Omit<OidcClient, 'id' | 'logoURL' | 'hasLogo'>;
|
export type OidcClientCreate = Omit<OidcClient, 'id' | 'logoURL' | 'hasLogo'>;
|
||||||
|
|
||||||
export type OidcClientCreateWithLogo = OidcClientCreate & {
|
export type OidcClientCreateWithLogo = OidcClientCreate & {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import * as Table from '$lib/components/ui/table';
|
import * as Table from '$lib/components/ui/table';
|
||||||
import { m } from '$lib/paraglide/messages';
|
import { m } from '$lib/paraglide/messages';
|
||||||
import OIDCService from '$lib/services/oidc-service';
|
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 type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
||||||
import { axiosErrorToast } from '$lib/utils/error-util';
|
import { axiosErrorToast } from '$lib/utils/error-util';
|
||||||
import { LucidePencil, LucideTrash } from '@lucide/svelte';
|
import { LucidePencil, LucideTrash } from '@lucide/svelte';
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
clients = $bindable(),
|
clients = $bindable(),
|
||||||
requestOptions
|
requestOptions
|
||||||
}: {
|
}: {
|
||||||
clients: Paginated<OidcClient>;
|
clients: Paginated<OidcClientWithAllowedUserGroupsCount>;
|
||||||
requestOptions: SearchPaginationSortRequest;
|
requestOptions: SearchPaginationSortRequest;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
columns={[
|
columns={[
|
||||||
{ label: m.logo() },
|
{ label: m.logo() },
|
||||||
{ label: m.name(), sortColumn: 'name' },
|
{ label: m.name(), sortColumn: 'name' },
|
||||||
|
{ label: m.oidc_allowed_group_count(), sortColumn: 'allowedUserGroupsCount' },
|
||||||
{ label: m.actions(), hidden: true }
|
{ label: m.actions(), hidden: true }
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -67,6 +68,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell class="font-medium">{item.name}</Table.Cell>
|
<Table.Cell class="font-medium">{item.name}</Table.Cell>
|
||||||
|
<Table.Cell class="font-medium"
|
||||||
|
>{item.allowedUserGroupsCount > 0
|
||||||
|
? item.allowedUserGroupsCount
|
||||||
|
: m.unrestricted()}</Table.Cell
|
||||||
|
>
|
||||||
<Table.Cell class="flex justify-end gap-1">
|
<Table.Cell class="flex justify-end gap-1">
|
||||||
<Button
|
<Button
|
||||||
href="/settings/admin/oidc-clients/{item.id}"
|
href="/settings/admin/oidc-clients/{item.id}"
|
||||||
|
|||||||
Reference in New Issue
Block a user