diff --git a/demo/admin/src/App.tsx b/demo/admin/src/App.tsx index b9337384a3..4464e82ce1 100644 --- a/demo/admin/src/App.tsx +++ b/demo/admin/src/App.tsx @@ -20,7 +20,6 @@ import { SiteConfig, SitePreview, SitesConfigProvider, - UserPermissionsPage, } from "@comet/cms-admin"; import { css, Global } from "@emotion/react"; import { createApolloClient } from "@src/common/apollo/createApolloClient"; @@ -215,10 +214,6 @@ class App extends React.Component { path={`${match.path}/product-tags`} component={ProductTagsPage} /> - diff --git a/demo/admin/src/common/MasterMenu.tsx b/demo/admin/src/common/MasterMenu.tsx index a588e2d180..6fcc8fd1af 100644 --- a/demo/admin/src/common/MasterMenu.tsx +++ b/demo/admin/src/common/MasterMenu.tsx @@ -83,11 +83,6 @@ const MasterMenu: React.FC = () => { } /> } /> - } - /> ); }; diff --git a/demo/api/schema.gql b/demo/api/schema.gql index 5f46b83ac6..30f54ad720 100644 --- a/demo/api/schema.gql +++ b/demo/api/schema.gql @@ -127,38 +127,6 @@ type FilenameResponse { isOccupied: Boolean! } -type CurrentUserPermission { - permission: String! -} - -type User { - id: String! - name: String! - email: String! - language: String! -} - -type UserPermission { - id: ID! - source: UserPermissionSource - permission: String! - validFrom: DateTime - validTo: DateTime - reason: String - requestedBy: String - approvedBy: String -} - -enum UserPermissionSource { - MANUAL - BY_RULE -} - -type PaginatedUserList { - nodes: [User!]! - totalCount: Int! -} - type Link implements DocumentInterface { id: ID! updatedAt: DateTime! @@ -585,13 +553,6 @@ input RedirectScopeInput { type Query { currentUser: CurrentUser! - userPermissionsUserById(id: String!): User! - userPermissionsUsers(offset: Int! = 0, limit: Int! = 25, search: String, filter: UserFilter, sort: [UserSort!]): PaginatedUserList! - userPermissionsPermissionList(userId: String!): [UserPermission!]! - userPermissionsPermission(id: ID!, userId: String): UserPermission! - userPermissionsAvailablePermissions: [String!]! - userPermissionsContentScopes(userId: String!, skipManual: Boolean): [JSONObject!]! - userPermissionsAvailableContentScopes: [JSONObject!]! buildTemplates: [BuildTemplate!]! builds(limit: Float): [Build!]! autoBuildStatus: AutoBuildStatus! @@ -637,35 +598,6 @@ type Query { productTags(offset: Int! = 0, limit: Int! = 25, search: String, filter: ProductTagFilter, sort: [ProductTagSort!]): PaginatedProductTags! } -input UserFilter { - name: StringFilter - email: StringFilter - status: StringFilter - language: StringFilter - and: [UserFilter!] - or: [UserFilter!] -} - -input StringFilter { - contains: String - startsWith: String - endsWith: String - equal: String - notEqual: String -} - -input UserSort { - field: UserSortField! - direction: SortDirection! = ASC -} - -enum UserSortField { - name - email - status - language -} - enum SortDirection { ASC DESC @@ -697,6 +629,14 @@ input RedirectFilter { or: [RedirectFilter!] } +input StringFilter { + contains: String + startsWith: String + endsWith: String + equal: String + notEqual: String +} + input BooleanFilter { equal: Boolean } @@ -884,10 +824,6 @@ enum ProductTagSortField { type Mutation { currentUserSignOut: String! - userPermissionsCreatePermission(userId: String!, input: UserPermissionInput!): UserPermission! - userPermissionsUpdatePermission(id: String!, input: UserPermissionInput!): UserPermission! - userPermissionsDeletePermission(id: ID!): Boolean! - userPermissionsUpdateContentScopes(userId: String!, input: UserContentScopesInput!): [JSONObject!]! createBuilds(input: CreateBuildsInput!): Boolean! saveLink(linkId: ID!, input: LinkInput!, attachedPageTreeNodeId: ID!, lastUpdatedAt: DateTime): Link! savePage(pageId: ID!, input: PageInput!, attachedPageTreeNodeId: ID!, lastUpdatedAt: DateTime): Page! @@ -938,19 +874,6 @@ type Mutation { deleteProductTag(id: ID!): Boolean! } -input UserPermissionInput { - permission: String! - validFrom: DateTime - validTo: DateTime - reason: String - requestedBy: String - approvedBy: String -} - -input UserContentScopesInput { - contentScopes: [JSONObject!]! = [] -} - input CreateBuildsInput { names: [String!]! } diff --git a/demo/api/src/app.module.ts b/demo/api/src/app.module.ts index 1e6f64495c..07420c6dfa 100644 --- a/demo/api/src/app.module.ts +++ b/demo/api/src/app.module.ts @@ -18,7 +18,6 @@ import { PageTreeService, PublicUploadModule, RedirectsModule, - UserPermissionsModule, } from "@comet/cms-api"; import { ApolloDriver } from "@nestjs/apollo"; import { DynamicModule, Module } from "@nestjs/common"; @@ -32,7 +31,6 @@ import { PredefinedPage } from "@src/predefined-page/entities/predefined-page.en import { Request } from "express"; import { AuthModule } from "./auth/auth.module"; -import { UserService } from "./auth/user.service"; import { DamScope } from "./dam/dto/dam-scope"; import { DamFile } from "./dam/entities/dam-file.entity"; import { DamFolder } from "./dam/entities/dam-folder.entity"; @@ -84,19 +82,6 @@ export class AppModule { return user.domains.includes(requestScope.domain); }, }), - UserPermissionsModule.forRootAsync({ - useFactory: (userService: UserService) => ({ - availablePermissions: ["news", "products"], - availableContentScopes: [ - { domain: "main", language: "de" }, - { domain: "main", language: "en" }, - { domain: "secondary", language: "en" }, - ], - userService, - }), - inject: [UserService], - imports: [AuthModule], - }), BlocksModule.forRoot({ imports: [PagesModule], useFactory: (pageTreeService: PageTreeService, filesService: FilesService, imagesService: ImagesService) => { diff --git a/demo/api/src/auth/auth.module.ts b/demo/api/src/auth/auth.module.ts index ed849aead8..594f6d6e0e 100644 --- a/demo/api/src/auth/auth.module.ts +++ b/demo/api/src/auth/auth.module.ts @@ -3,7 +3,6 @@ import { Module } from "@nestjs/common"; import { APP_GUARD } from "@nestjs/core"; import { CurrentUser } from "./current-user"; -import { UserService } from "./user.service"; @Module({ providers: [ @@ -24,8 +23,6 @@ import { UserService } from "./user.service"; provide: APP_GUARD, useClass: createCometAuthGuard(["static-authed-user"]), }, - UserService, ], - exports: [UserService], }) export class AuthModule {} diff --git a/demo/api/src/auth/permission.interface.ts b/demo/api/src/auth/permission.interface.ts deleted file mode 100644 index 4f34da4aab..0000000000 --- a/demo/api/src/auth/permission.interface.ts +++ /dev/null @@ -1,8 +0,0 @@ -declare module "@comet/cms-api" { - interface Permission { - news: string; - products: string; - } -} - -export {}; diff --git a/demo/api/src/auth/user.service.ts b/demo/api/src/auth/user.service.ts deleted file mode 100644 index 86e6e4d9af..0000000000 --- a/demo/api/src/auth/user.service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ContentScopesForUser, FindUsersArgs, PermissionsForUser, User, UserPermissions, UserPermissionsUserService, Users } from "@comet/cms-api"; -import { Injectable } from "@nestjs/common"; - -const staticUsers: User[] = [ - { - id: "1", - name: "Admin", - email: "demo@comet-dxp.com", - language: "en", - }, - { - id: "2", - name: "Non-Admin", - email: "test@test.com", - language: "en", - }, -]; - -@Injectable() -export class UserService implements UserPermissionsUserService { - getUser(id: string): User { - const index = parseInt(id) - 1; - if (staticUsers[index]) return staticUsers[index]; - throw new Error("User not found"); - } - findUsers(args: FindUsersArgs): Users { - const search = args.search?.toLowerCase(); - const users = staticUsers.filter((user) => !search || user.name.toLowerCase().includes(search) || user.email.toLowerCase().includes(search)); - return [users, users.length]; - } - getPermissionsForUser(user: User): PermissionsForUser { - if (user.email.endsWith("@comet-dxp.com")) { - return UserPermissions.allPermissions; - } else { - return [{ permission: "news" }]; - } - } - getContentScopesForUser(user: User): ContentScopesForUser { - if (user.email.endsWith("@comet-dxp.com")) { - return UserPermissions.allContentScopes; - } else { - return [{ domain: "main", language: "en" }]; - } - } -} diff --git a/packages/admin/cms-admin/src/index.ts b/packages/admin/cms-admin/src/index.ts index f3f698ba4e..41e63bcab8 100644 --- a/packages/admin/cms-admin/src/index.ts +++ b/packages/admin/cms-admin/src/index.ts @@ -86,7 +86,6 @@ export type { SiteConfig } from "./sitesConfig/SitesConfigContext"; export { SitesConfigProvider } from "./sitesConfig/SitesConfigProvider"; export { useSiteConfig } from "./sitesConfig/useSiteConfig"; export { useSitesConfig } from "./sitesConfig/useSitesConfig"; -export { UserPermissionsPage } from "./userPermissions/UserPermissionsPage"; // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports import emotionStyled from "@emotion/styled"; // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports diff --git a/packages/admin/cms-admin/src/userPermissions/UserGrid.tsx b/packages/admin/cms-admin/src/userPermissions/UserGrid.tsx deleted file mode 100644 index 5ccf87d086..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/UserGrid.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { gql, useQuery } from "@apollo/client"; -import { - GridFilterButton, - muiGridFilterToGql, - muiGridSortToGql, - StackSwitchApiContext, - Toolbar, - ToolbarActions, - ToolbarAutomaticTitleItem, - ToolbarItem, - useDataGridRemote, - usePersistentColumnState, -} from "@comet/admin"; -import { Edit } from "@comet/admin-icons"; -import { IconButton, Typography } from "@mui/material"; -import { styled } from "@mui/material/styles"; -import { DataGrid, GridColDef, GridToolbarQuickFilter } from "@mui/x-data-grid"; -import React from "react"; -import { useIntl } from "react-intl"; - -import { GQLUserForGridFragment, GQLUserGridQuery, GQLUserGridQueryVariables } from "./UserGrid.generated"; - -export const UserGrid: React.FC = () => { - const dataGridProps = { ...useDataGridRemote(), ...usePersistentColumnState("UserGrid") }; - const intl = useIntl(); - const stackApi = React.useContext(StackSwitchApiContext); - - const columns: GridColDef[] = [ - { - field: "name", - flex: 1, - pinnable: false, - headerName: intl.formatMessage({ id: "comet.userPermissions.name", defaultMessage: "Name" }), - renderCell: ({ row }) => ( - - {row.name} - - ), - }, - { - field: "email", - flex: 1, - pinnable: false, - headerName: intl.formatMessage({ id: "comet.userPermissions.email", defaultMessage: "E-Mail" }), - }, - { - field: "language", - flex: 0.5, - pinnable: false, - headerName: intl.formatMessage({ id: "comet.userPermissions.language", defaultMessage: "Language" }), - renderCell: ({ row }) => row.language.toUpperCase(), - }, - { - field: "actions", - headerName: "", - sortable: false, - pinnable: false, - filterable: false, - renderCell: (params) => ( - { - stackApi.activatePage("edit", params.id.toString()); - }} - > - - - ), - }, - ]; - - const { data, loading, error } = useQuery( - gql` - query UserGrid($offset: Int, $limit: Int, $filter: UserFilter, $sort: [UserSort!], $search: String) { - users: userPermissionsUsers(offset: $offset, limit: $limit, filter: $filter, sort: $sort, search: $search) { - nodes { - ...UserForGrid - } - totalCount - } - } - fragment UserForGrid on User { - id - name - email - language - } - `, - { - variables: { - ...muiGridFilterToGql(columns, dataGridProps.filterModel), - offset: dataGridProps.page * dataGridProps.pageSize, - limit: dataGridProps.pageSize, - sort: muiGridSortToGql(dataGridProps.sortModel), - }, - }, - ); - - if (error) throw new Error(error.message); - - return ( - - {...dataGridProps} - rows={data?.users.nodes ?? []} - columns={columns} - rowCount={data?.users.totalCount ?? 0} - loading={loading} - components={{ - Toolbar: () => ( - - - - - - - - - - ), - }} - /> - ); -}; - -const NameBox = styled("div")({ - fontWeight: "bold", - fontSize: "small", -}); diff --git a/packages/admin/cms-admin/src/userPermissions/UserPermissionsPage.tsx b/packages/admin/cms-admin/src/userPermissions/UserPermissionsPage.tsx deleted file mode 100644 index 08cb620c36..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/UserPermissionsPage.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { MainContent, Stack, StackPage, StackSwitch } from "@comet/admin"; -import * as React from "react"; -import { FormattedMessage } from "react-intl"; - -import { UserPage } from "./user/UserPage"; -import { UserGrid } from "./UserGrid"; - -export const UserPermissionsPage = (): React.ReactElement => ( - }> - - - - - - - {(userId) => } - - -); diff --git a/packages/admin/cms-admin/src/userPermissions/user/UserPage.tsx b/packages/admin/cms-admin/src/userPermissions/user/UserPage.tsx deleted file mode 100644 index 31086a16b0..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/user/UserPage.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { gql, useQuery } from "@apollo/client"; -import { Loading, MainContent, RouterTab, RouterTabs, Toolbar, ToolbarBackButton, ToolbarTitleItem } from "@comet/admin"; -import { Box } from "@mui/material"; -import { styled } from "@mui/material/styles"; -import * as React from "react"; -import { useIntl } from "react-intl"; - -import { UserBasicData } from "./basicData/UserBasicData"; -import { ContentScopeGrid } from "./permissions/ContentScopeGrid"; -import { PermissionGrid } from "./permissions/PermissionGrid"; -import { GQLUserPageQuery, GQLUserPageQueryVariables } from "./UserPage.generated"; - -export const UserPage: React.FC<{ userId: string }> = ({ userId }) => { - const { data, error, loading } = useQuery( - gql` - query UserPage($id: String!) { - user: userPermissionsUserById(id: $id) { - name - email - } - } - `, - { - variables: { id: userId }, - }, - ); - const intl = useIntl(); - - if (error) { - throw new Error(error.message); - } - - if (loading || !data) { - return ; - } - - return ( - <> - - - - {data.user.name} - {data.user.email} - - - - - - - - - - - - - - - - ); -}; - -const TitleText = styled("div")` - font-weight: ${({ theme }) => theme.typography.fontWeightRegular}; - font-size: 18px; - line-height: 21px; - color: ${({ theme }) => theme.palette.text.primary}; -`; - -const SupportText = styled("div")` - font-weight: ${({ theme }) => theme.typography.fontWeightRegular}; - font-size: 14px; - line-height: 20px; - color: ${({ theme }) => theme.palette.text.secondary}; -`; diff --git a/packages/admin/cms-admin/src/userPermissions/user/basicData/UserBasicData.tsx b/packages/admin/cms-admin/src/userPermissions/user/basicData/UserBasicData.tsx deleted file mode 100644 index dfa0cd98e7..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/user/basicData/UserBasicData.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { gql, useQuery } from "@apollo/client"; -import { Field, FinalForm, FinalFormInput, Loading, ToolbarFillSpace, ToolbarTitleItem } from "@comet/admin"; -import { Card, CardContent, Toolbar } from "@mui/material"; -import { styled } from "@mui/material/styles"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -import { GQLUserBasicDataQuery, GQLUserBasicDataQueryVariables } from "./UserBasicData.generated"; - -export const UserBasicData: React.FC<{ - id: string; -}> = ({ id }) => { - const { data, error, loading } = useQuery( - gql` - query UserBasicData($id: String!) { - user: userPermissionsUserById(id: $id) { - id - name - email - language - } - } - `, - { - variables: { id }, - }, - ); - - if (error) { - throw new Error(error.message); - } - - if (loading || !data) { - return ; - } - - return ( - - - - - - - - - { - /* do nothing */ - }} - > - } - /> - } - /> - } - /> - - - - ); -}; - -const CardToolbar = styled(Toolbar)` - top: 0px; - border-bottom: 1px solid ${({ theme }) => theme.palette.grey[100]}; -`; diff --git a/packages/admin/cms-admin/src/userPermissions/user/permissions/ContentScopeGrid.tsx b/packages/admin/cms-admin/src/userPermissions/user/permissions/ContentScopeGrid.tsx deleted file mode 100644 index 2b3779dd43..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/user/permissions/ContentScopeGrid.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { gql, useApolloClient, useQuery } from "@apollo/client"; -import { Field, FinalForm, FinalFormCheckbox, Loading, SaveButton, ToolbarActions, ToolbarFillSpace, ToolbarTitleItem } from "@comet/admin"; -import { Card, CardContent, Toolbar } from "@mui/material"; -import { styled } from "@mui/material/styles"; -import isEqual from "lodash.isequal"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -import { camelCaseToHumanReadable } from "../../utils/camelCaseToHumanReadable"; -import { - GQLContentScopesQuery, - GQLContentScopesQueryVariables, - GQLUpdateContentScopesMutation, - GQLUpdateContentScopesMutationVariables, -} from "./ContentScopeGrid.generated"; - -type FormValues = { - contentScopes: string[]; -}; -type ContentScope = { - [key: string]: string; -}; - -export const ContentScopeGrid: React.FC<{ - userId: string; -}> = ({ userId }) => { - const client = useApolloClient(); - - const submit = async (data: FormValues) => { - await client.mutate({ - mutation: gql` - mutation UpdateContentScopes($userId: String!, $input: UserContentScopesInput!) { - userPermissionsUpdateContentScopes(userId: $userId, input: $input) - } - `, - variables: { - userId, - input: { - contentScopes: data.contentScopes.map((contentScope) => JSON.parse(contentScope)), - }, - }, - refetchQueries: ["ContentScopes"], - }); - }; - - const { data, error } = useQuery( - gql` - query ContentScopes($userId: String!) { - availableContentScopes: userPermissionsAvailableContentScopes - userContentScopes: userPermissionsContentScopes(userId: $userId) - userContentScopesSkipManual: userPermissionsContentScopes(userId: $userId, skipManual: true) - } - `, - { - variables: { userId }, - }, - ); - - if (error) throw new Error(error.message); - - if (!data) { - return ; - } - - return ( - - - mode="edit" - onSubmit={submit} - onAfterSubmit={() => null} - initialValues={{ contentScopes: data.userContentScopes.map((cs) => JSON.stringify(cs)) }} - > - - - - - - - - - - - - - {data.availableContentScopes.map((contentScope: ContentScope) => ( - isEqual(cs, contentScope))} - key={JSON.stringify(contentScope)} - name="contentScopes" - fullWidth - variant="horizontal" - type="checkbox" - component={FinalFormCheckbox} - value={JSON.stringify(contentScope)} - label={Object.entries(contentScope).map(([scope, value]) => ( - <> - :{" "} - -
- - ))} - /> - ))} -
- -
- ); -}; - -const CardToolbar = styled(Toolbar)` - top: 0px; - border-bottom: 1px solid ${({ theme }) => theme.palette.grey[100]}; -`; diff --git a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionDialog.tsx b/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionDialog.tsx deleted file mode 100644 index 7586e5971d..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionDialog.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import { gql, useApolloClient, useQuery } from "@apollo/client"; -import { CancelButton, Field, FinalForm, FinalFormInput, FinalFormSelect, FormSection, Loading, SaveButton } from "@comet/admin"; -import { FinalFormDatePicker } from "@comet/admin-date-time"; -import { Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -import { camelCaseToHumanReadable } from "../../utils/camelCaseToHumanReadable"; -import { - GQLAvailablePermissionsQuery, - GQLAvailablePermissionsQueryVariables, - GQLCreateUserPermissionMutation, - GQLCreateUserPermissionMutationVariables, - GQLPermissionQuery, - GQLPermissionQueryVariables, - GQLUpdateUserPermissionMutation, - GQLUpdateUserPermissionMutationVariables, - GQLUserPermissionDialogFragment, - namedOperations, -} from "./PermissionDialog.generated"; - -interface FormProps { - userId: string; - permissionId: string | "add"; - handleDialogClose: () => void; -} -export const PermissionDialog: React.FC = ({ userId, permissionId, handleDialogClose }) => { - const client = useApolloClient(); - const submit = async (submitData: GQLUserPermissionDialogFragment) => { - const { source, __typename, ...data } = submitData; // Remove source and __typename from data - - if (permissionId && permissionId !== "add") { - await client.mutate({ - mutation: gql` - mutation UpdateUserPermission($id: String!, $input: UserPermissionInput!) { - userPermissionsUpdatePermission(id: $id, input: $input) { - id - } - } - `, - variables: { id: permissionId, input: data }, - refetchQueries: [namedOperations.Query.Permission, "Permissions"], - }); - } else { - await client.mutate({ - mutation: gql` - mutation CreateUserPermission($userId: String!, $input: UserPermissionInput!) { - userPermissionsCreatePermission(userId: $userId, input: $input) { - id - } - } - `, - variables: { userId, input: { ...data } }, - refetchQueries: [namedOperations.Query.Permission, "Permissions"], - }); - } - handleDialogClose(); - }; - - const { data, error } = useQuery( - gql` - query Permission($permissionId: ID!, $userId: String) { - userPermission: userPermissionsPermission(id: $permissionId, userId: $userId) { - ...UserPermissionDialog - } - } - fragment UserPermissionDialog on UserPermission { - permission - source - validFrom - validTo - reason - requestedBy - approvedBy - } - `, - { - skip: permissionId === "add", - variables: { permissionId, userId }, - }, - ); - const { data: availablePermissionsData, error: availablePermissionsError } = useQuery< - GQLAvailablePermissionsQuery, - GQLAvailablePermissionsQueryVariables - >( - gql` - query AvailablePermissions { - availablePermissions: userPermissionsAvailablePermissions - } - `, - ); - - if (error) { - throw new Error(error.message); - } - if (availablePermissionsError) { - throw new Error(availablePermissionsError.message); - } - if (!availablePermissionsData || (permissionId !== "add" && !data)) { - return ; - } - - const initialValues = data - ? { - ...data.userPermission, - validFrom: data.userPermission.validFrom ? new Date(data.userPermission.validFrom) : null, - validTo: data.userPermission.validTo ? new Date(data.userPermission.validTo) : null, - } - : {}; - - const disabled = data && data.userPermission.source === "BY_RULE"; - - return ( - - - mode={permissionId ? "edit" : "add"} - onSubmit={submit} - onAfterSubmit={() => null} - initialValues={initialValues} - render={({ values }) => ( - <> - - - - - ( - - )} - disabled={disabled} - label={} - /> - } - fullWidth - component={FinalFormDatePicker} - disabled={disabled} - clearable={true} - formatDateOptions={{ month: "short", day: "numeric", year: "numeric" }} - /> - } - fullWidth - component={FinalFormDatePicker} - disabled={disabled} - clearable={true} - formatDateOptions={{ month: "short", day: "numeric", year: "numeric" }} - /> - }> - } - /> - } - /> - } - /> - - - - - - - {!disabled && } - - - )} - /> - - ); -}; diff --git a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionGrid.tsx b/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionGrid.tsx deleted file mode 100644 index 5c35f93d49..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionGrid.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import { gql, useQuery } from "@apollo/client"; -import { TableDeleteButton, ToolbarActions, ToolbarFillSpace, ToolbarTitleItem } from "@comet/admin"; -import { Add, Delete, Edit, Info, Reject } from "@comet/admin-icons"; -import { Button, Card, Chip, IconButton, Typography } from "@mui/material"; -import { styled } from "@mui/material/styles"; -import { DataGrid, GridColDef, GridToolbarContainer } from "@mui/x-data-grid"; -import { differenceInDays, parseISO } from "date-fns"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import { camelCaseToHumanReadable } from "../../utils/camelCaseToHumanReadable"; -import { PermissionDialog } from "./PermissionDialog"; -import { GQLPermissionForGridFragment, GQLPermissionsQuery, GQLPermissionsQueryVariables, namedOperations } from "./PermissionGrid.generated"; - -export const PermissionGrid: React.FC<{ - userId: string; -}> = ({ userId }) => { - const intl = useIntl(); - const [permissionId, setPermissionId] = React.useState(null); - - const { data, loading, error } = useQuery( - gql` - query Permissions($userId: String!) { - permissions: userPermissionsPermissionList(userId: $userId) { - ...PermissionForGrid - } - } - fragment PermissionForGrid on UserPermission { - id - permission - source - validFrom - validTo - reason - requestedBy - approvedBy - } - `, - { - variables: { - userId, - }, - }, - ); - - const columns: GridColDef[] = [ - { - field: "name", - flex: 1, - pinnable: false, - headerName: intl.formatMessage({ id: "comet.userPermissions.permission", defaultMessage: "Permission" }), - renderCell: ({ row }) => ( - <> - - - - - ), - }, - { - field: "source", - width: 100, - pinnable: false, - headerName: intl.formatMessage({ id: "comet.userPermissions.source", defaultMessage: "Source" }), - }, - { - field: "validityPeriod", - width: 200, - pinnable: false, - headerName: intl.formatMessage({ id: "comet.userPermissions.validityPeriod", defaultMessage: "Validity period" }), - renderCell: ({ row }) => - `${row.validFrom ? new Date(row.validFrom).toLocaleDateString() : "∞"} - ${ - row.validTo ? new Date(row.validTo).toLocaleDateString() : "∞" - }`, - }, - { - field: "status", - flex: 1, - filterable: false, - pinnable: false, - headerName: intl.formatMessage({ id: "comet.userPermissions.status", defaultMessage: "Status" }), - renderCell: ({ row }) => ( -
- {row.validTo && differenceInDays(parseISO(row.validTo), new Date()) < 0 && ( - } - color="error" - label={} - /> - )} - {row.validTo && - differenceInDays(parseISO(row.validTo), new Date()) >= 0 && - differenceInDays(parseISO(row.validTo), new Date()) < 30 && ( - } - color="warning" - label={} - /> - )} -
- ), - }, - { - field: "edit", - width: 60, - headerName: "", - sortable: false, - pinnable: false, - filterable: false, - renderCell: ({ row }) => ( - { - setPermissionId(row.id); - }} - > - - - ), - }, - { - field: "delete", - width: 60, - headerName: "", - sortable: false, - pinnable: false, - filterable: false, - renderCell: ({ row }) => - row.source !== "BY_RULE" && ( - } - mutation={gql` - mutation DeletePermission($id: ID!) { - userPermissionsDeletePermission(id: $id) - } - `} - selectedId={`${row.id}`} - text="" - refetchQueries={[namedOperations.Query.Permissions]} - /> - ), - }, - ]; - - if (error) throw new Error(error.message); - - return ( - - - autoHeight={true} - rows={data?.permissions ?? []} - columns={columns} - rowCount={data?.permissions.length ?? 0} - loading={loading} - getRowHeight={() => "auto"} - sx={{ "&.MuiDataGrid-root .MuiDataGrid-cell": { py: "8px" } }} - components={{ - Toolbar: () => ( - - - - - - - - - - ), - }} - /> - {permissionId && setPermissionId(null)} />} - - ); -}; - -const GridToolbar = styled(GridToolbarContainer)` - padding: 10px; - border-bottom: 1px solid ${({ theme }) => theme.palette.grey[100]}; -`; diff --git a/packages/admin/cms-admin/src/userPermissions/utils/camelCaseToHumanReadable.ts b/packages/admin/cms-admin/src/userPermissions/utils/camelCaseToHumanReadable.ts deleted file mode 100644 index c32142ca45..0000000000 --- a/packages/admin/cms-admin/src/userPermissions/utils/camelCaseToHumanReadable.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function camelCaseToHumanReadable(s: string) { - const words = s.match(/[A-Za-z][a-z]*/g) || []; - return words.map((word) => word.charAt(0).toUpperCase() + word.substring(1)).join(" "); -} diff --git a/packages/api/cms-api/generate-schema.ts b/packages/api/cms-api/generate-schema.ts index bc171fdb58..967cf883bc 100644 --- a/packages/api/cms-api/generate-schema.ts +++ b/packages/api/cms-api/generate-schema.ts @@ -3,11 +3,9 @@ import { NestFactory } from "@nestjs/core"; import { Field, GraphQLSchemaBuilderModule, GraphQLSchemaFactory, ObjectType } from "@nestjs/graphql"; import { writeFile } from "fs/promises"; import { printSchema } from "graphql"; -import { GraphQLJSONObject } from "graphql-type-json"; import { BuildsResolver, - ContentScope, createAuthResolver, createPageTreeResolver, createRedirectsResolver, @@ -31,10 +29,6 @@ import { createFilesResolver } from "./src/dam/files/files.resolver"; import { createFoldersResolver } from "./src/dam/files/folders.resolver"; import { RedirectInputFactory } from "./src/redirects/dto/redirect-input.factory"; import { RedirectEntityFactory } from "./src/redirects/entities/redirect-entity.factory"; -import { CurrentUserPermission } from "./src/user-permissions/dto/current-user"; -import { UserResolver } from "./src/user-permissions/user.resolver"; -import { UserContentScopesResolver } from "./src/user-permissions/user-content-scopes.resolver"; -import { UserPermissionResolver } from "./src/user-permissions/user-permission.resolver"; @ObjectType() class PageTreeNode extends PageTreeNodeBase { @@ -72,10 +66,6 @@ class CurrentUser implements CurrentUserInterface { role: string; @Field(() => [CurrentUserRight], { nullable: true }) rights: CurrentUserRightInterface[]; - @Field(() => [GraphQLJSONObject]) - contentScopes: ContentScope[]; - @Field(() => [CurrentUserPermission]) - permissions: CurrentUserPermission[]; } async function generateSchema(): Promise { @@ -122,9 +112,6 @@ async function generateSchema(): Promise { RedirectsDependenciesResolver, PageTreeDependentsResolver, FileDependentsResolver, - UserResolver, - UserPermissionResolver, - UserContentScopesResolver, ]); await writeFile("schema.gql", printSchema(schema)); diff --git a/packages/api/cms-api/package.json b/packages/api/cms-api/package.json index a0239209d2..2ecfae6d91 100644 --- a/packages/api/cms-api/package.json +++ b/packages/api/cms-api/package.json @@ -70,8 +70,7 @@ "rimraf": "^3.0.0", "slugify": "^1.6.4", "ts-morph": "^16.0.0", - "uuid": "^9.0.0", - "uuid-by-string": "^4.0.0" + "uuid": "^9.0.0" }, "devDependencies": { "@aws-sdk/client-s3": "^3.47.0", diff --git a/packages/api/cms-api/schema.gql b/packages/api/cms-api/schema.gql index 4fd8eb59ba..f8d2b13ec6 100644 --- a/packages/api/cms-api/schema.gql +++ b/packages/api/cms-api/schema.gql @@ -123,38 +123,6 @@ type FilenameResponse { isOccupied: Boolean! } -type CurrentUserPermission { - permission: String! -} - -type User { - id: String! - name: String! - email: String! - language: String! -} - -type UserPermission { - id: ID! - source: UserPermissionSource - permission: String! - validFrom: DateTime - validTo: DateTime - reason: String - requestedBy: String - approvedBy: String -} - -enum UserPermissionSource { - MANUAL - BY_RULE -} - -type PaginatedUserList { - nodes: [User!]! - totalCount: Int! -} - type PageTreeNode { id: ID! parentId: String @@ -210,8 +178,6 @@ type CurrentUser { language: String! role: String! rights: [CurrentUserRight!] - contentScopes: [JSONObject!]! - permissions: [CurrentUserPermission!]! } type Redirect implements DocumentInterface { @@ -338,13 +304,6 @@ type Query { pageTreeNodeSlugAvailable(scope: PageTreeNodeScopeInput!, parentId: ID, slug: String!): SlugAvailability! cronJobs: [CronJob!]! currentUser: CurrentUser! - userPermissionsUserById(id: String!): User! - userPermissionsUsers(offset: Int! = 0, limit: Int! = 25, search: String, filter: UserFilter, sort: [UserSort!]): PaginatedUserList! - userPermissionsPermissionList(userId: String!): [UserPermission!]! - userPermissionsPermission(id: ID!, userId: String): UserPermission! - userPermissionsAvailablePermissions: [String!]! - userPermissionsContentScopes(userId: String!, skipManual: Boolean): [JSONObject!]! - userPermissionsAvailableContentScopes: [JSONObject!]! } input RedirectScopeInput { @@ -454,27 +413,6 @@ enum SlugAvailability { Reserved } -input UserFilter { - name: StringFilter - email: StringFilter - status: StringFilter - language: StringFilter - and: [UserFilter!] - or: [UserFilter!] -} - -input UserSort { - field: UserSortField! - direction: SortDirection! = ASC -} - -enum UserSortField { - name - email - status - language -} - type Mutation { createBuilds(input: CreateBuildsInput!): Boolean! createRedirect(scope: RedirectScopeInput! = {}, input: RedirectInput!): Redirect! @@ -503,10 +441,6 @@ type Mutation { updatePageTreeNodeCategory(id: ID!, category: String!): PageTreeNode! createPageTreeNode(input: PageTreeNodeCreateInput!, scope: PageTreeNodeScopeInput!, category: String!): PageTreeNode! currentUserSignOut: String! - userPermissionsCreatePermission(userId: String!, input: UserPermissionInput!): UserPermission! - userPermissionsUpdatePermission(id: String!, input: UserPermissionInput!): UserPermission! - userPermissionsDeletePermission(id: ID!): Boolean! - userPermissionsUpdateContentScopes(userId: String!, input: UserContentScopesInput!): [JSONObject!]! } input CreateBuildsInput { @@ -596,16 +530,3 @@ input PageTreeNodeCreateInput { attachedDocument: AttachedDocumentInput! hideInMenu: Boolean } - -input UserPermissionInput { - permission: String! - validFrom: DateTime - validTo: DateTime - reason: String - requestedBy: String - approvedBy: String -} - -input UserContentScopesInput { - contentScopes: [JSONObject!]! = [] -} diff --git a/packages/api/cms-api/src/access-log/access-log.interceptor.ts b/packages/api/cms-api/src/access-log/access-log.interceptor.ts index 66959681c1..86f0fbebba 100644 --- a/packages/api/cms-api/src/access-log/access-log.interceptor.ts +++ b/packages/api/cms-api/src/access-log/access-log.interceptor.ts @@ -1,8 +1,8 @@ import { CallHandler, ExecutionContext, Inject, Injectable, Logger, NestInterceptor, Optional } from "@nestjs/common"; import { GqlExecutionContext } from "@nestjs/graphql"; import { GraphQLResolveInfo } from "graphql"; -import { CurrentUser } from "src/user-permissions/dto/current-user"; +import { CurrentUserInterface } from "../auth/current-user/current-user"; import { SHOULD_LOG_REQUEST } from "./access-log.constants"; import { ShouldLogRequest } from "./access-log.module"; @@ -79,7 +79,7 @@ export class AccessLogInterceptor implements NestInterceptor { return next.handle(); } - private pushUserToRequestData(user: CurrentUser, requestData: string[]) { + private pushUserToRequestData(user: CurrentUserInterface, requestData: string[]) { if (user) { requestData.push(`user: ${user.id} (${user.name})`); } diff --git a/packages/api/cms-api/src/access-log/access-log.module.ts b/packages/api/cms-api/src/access-log/access-log.module.ts index a2e3612cec..9650875423 100644 --- a/packages/api/cms-api/src/access-log/access-log.module.ts +++ b/packages/api/cms-api/src/access-log/access-log.module.ts @@ -2,11 +2,11 @@ import { DynamicModule, Global, Module } from "@nestjs/common"; import { APP_INTERCEPTOR } from "@nestjs/core"; import { Request } from "express"; -import { CurrentUser } from "../user-permissions/dto/current-user"; +import { CurrentUserInterface } from "../auth/current-user/current-user"; import { SHOULD_LOG_REQUEST } from "./access-log.constants"; import { AccessLogInterceptor } from "./access-log.interceptor"; -export type ShouldLogRequest = ({ user, req }: { user?: CurrentUser | true; req: Request }) => boolean; +export type ShouldLogRequest = ({ user, req }: { user?: CurrentUserInterface | true; req: Request }) => boolean; interface AccessLogModuleOptions { shouldLogRequest?: ShouldLogRequest; diff --git a/packages/api/cms-api/src/auth/current-user/current-user.ts b/packages/api/cms-api/src/auth/current-user/current-user.ts index 030f895ce2..6f0377b7ed 100644 --- a/packages/api/cms-api/src/auth/current-user/current-user.ts +++ b/packages/api/cms-api/src/auth/current-user/current-user.ts @@ -1,16 +1,10 @@ -import { ContentScope } from "../../user-permissions/interfaces/content-scope.interface"; - export interface CurrentUserInterface { id: string; name: string; email: string; language: string; - role?: string; + role: string; rights?: CurrentUserRightInterface[]; - permissions?: { - permission: string; - }[]; - contentScopes?: ContentScope[]; } export interface CurrentUserRightInterface { diff --git a/packages/api/cms-api/src/index.ts b/packages/api/cms-api/src/index.ts index 29fba18b16..e896afb9bf 100644 --- a/packages/api/cms-api/src/index.ts +++ b/packages/api/cms-api/src/index.ts @@ -39,6 +39,7 @@ export { BuildsService } from "./builds/builds.service"; export { AutoBuildStatus } from "./builds/dto/auto-build-status.object"; export { ChangesSinceLastBuild } from "./builds/entities/changes-since-last-build.entity"; export { SKIP_BUILD_METADATA_KEY, SkipBuild } from "./builds/skip-build.decorator"; +export { ContentScope } from "./common/decorators/content-scope.interface"; export { getRequestContextHeadersFromRequest, RequestContext, RequestContextInterface } from "./common/decorators/request-context.decorator"; export { ScopedEntity, ScopedEntityMeta } from "./common/decorators/scoped-entity.decorator"; export { SubjectEntity, SubjectEntityMeta, SubjectEntityOptions } from "./common/decorators/subject-entity.decorator"; @@ -143,19 +144,3 @@ export { RedirectsModule } from "./redirects/redirects.module"; export { createRedirectsResolver } from "./redirects/redirects.resolver"; export { RedirectsService } from "./redirects/redirects.service"; export { IsValidRedirectSource, IsValidRedirectSourceConstraint } from "./redirects/validators/isValidRedirectSource"; -export { CurrentUser } from "./user-permissions/dto/current-user"; -export { FindUsersArgs } from "./user-permissions/dto/paginated-user-list"; -export { User } from "./user-permissions/dto/user"; -export { ContentScope } from "./user-permissions/interfaces/content-scope.interface"; -export { Permission } from "./user-permissions/interfaces/user-permission.interface"; -export { UserPermissionsModule } from "./user-permissions/user-permissions.module"; -export { UserPermissionsService } from "./user-permissions/user-permissions.service"; -export { - ContentScopesForUser, - PermissionsForUser, - UserPermissions, - UserPermissionsOptions, - UserPermissionsOptionsFactory, - UserPermissionsUserService, - Users, -} from "./user-permissions/user-permissions.types"; diff --git a/packages/api/cms-api/src/mikro-orm/migrations/Migration20230831110518.ts b/packages/api/cms-api/src/mikro-orm/migrations/Migration20230831110518.ts deleted file mode 100644 index 34e85a89ef..0000000000 --- a/packages/api/cms-api/src/mikro-orm/migrations/Migration20230831110518.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Migration } from "@mikro-orm/migrations"; - -export class Migration20230831110518 extends Migration { - async up(): Promise { - this.addSql( - 'create table "UserContentScopes" ("userId" varchar(255) not null, "contentScopes" jsonb not null, constraint "UserContentScopes_pkey" primary key ("userId"));', - ); - this.addSql( - 'create table "UserPermission" ("id" uuid not null, "userId" varchar(255) not null, "permission" text not null, "configuration" jsonb null, "validFrom" date null, "validTo" date null, "reason" text null, "requestedBy" text null, "approvedBy" text null, constraint "UserPermission_pkey" primary key ("id"));', - ); - } - - async down(): Promise { - this.addSql('drop table if exists "UserContentScopes" cascade;'); - this.addSql('drop table if exists "UserPermission" cascade;'); - } -} diff --git a/packages/api/cms-api/src/mikro-orm/migrations/Migration20231206123505.ts b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231206123505.ts new file mode 100644 index 0000000000..05522ed1df --- /dev/null +++ b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231206123505.ts @@ -0,0 +1,15 @@ +import { Migration } from "@mikro-orm/migrations"; + +export class Migration20231206123505 extends Migration { + async up(): Promise { + const result = await this.execute( + `select count(*) from "${this.config.get("migrations").tableName}" where name = 'Migration20230831110518';`, + ); + if (result[0].count == 1) { + this.addSql('drop table if exists "UserContentScopes";'); + this.addSql('drop table if exists "UserPermission";'); + } + } + + async down(): Promise {} +} diff --git a/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts b/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts index 24c9e7f36b..11e78cdcc3 100644 --- a/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts +++ b/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts @@ -17,7 +17,7 @@ import { Migration20230302145445 } from "./migrations/Migration20230302145445"; import { Migration20230613150332 } from "./migrations/Migration20230613150332"; import { Migration20230802124224 } from "./migrations/Migration20230802124224"; import { Migration20230821090303 } from "./migrations/Migration20230821090303"; -import { Migration20230831110518 } from "./migrations/Migration20230831110518"; +import { Migration20231206123505 } from "./migrations/Migration20231206123505"; export const PG_UNIQUE_CONSTRAINT_VIOLATION = "23505"; @@ -72,7 +72,7 @@ export function createOrmConfig({ migrations, ...defaults }: MikroOrmNestjsOptio { name: "Migration20230613150332", class: Migration20230613150332 }, { name: "Migration20230802124224", class: Migration20230802124224 }, { name: "Migration20230821090303", class: Migration20230821090303 }, - { name: "Migration20230831110518", class: Migration20230831110518 }, + { name: "Migration20231206123505", class: Migration20231206123505 }, ...(migrations?.migrationsList || []), ].sort((migrationA, migrationB) => { if (migrationA.name < migrationB.name) { diff --git a/packages/api/cms-api/src/user-permissions/dto/current-user.ts b/packages/api/cms-api/src/user-permissions/dto/current-user.ts deleted file mode 100644 index f1537b27eb..0000000000 --- a/packages/api/cms-api/src/user-permissions/dto/current-user.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import { GraphQLJSONObject } from "graphql-type-json"; - -import { CurrentUserInterface } from "../../auth/current-user/current-user"; -import { ContentScope } from "../interfaces/content-scope.interface"; - -@ObjectType() -export class CurrentUserPermission { - @Field() - permission: string; -} - -@ObjectType() -export class CurrentUser implements CurrentUserInterface { - @Field() - id: string; - @Field() - name: string; - @Field() - email: string; - @Field() - language: string; - @Field(() => [GraphQLJSONObject]) - contentScopes: ContentScope[]; - @Field(() => [CurrentUserPermission]) - permissions: CurrentUserPermission[]; -} diff --git a/packages/api/cms-api/src/user-permissions/dto/paginated-user-list.ts b/packages/api/cms-api/src/user-permissions/dto/paginated-user-list.ts deleted file mode 100644 index 15d1455941..0000000000 --- a/packages/api/cms-api/src/user-permissions/dto/paginated-user-list.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { ArgsType, Field, InputType, registerEnumType } from "@nestjs/graphql"; -import { Type } from "class-transformer"; -import { IsEnum, IsOptional, IsString, ValidateNested } from "class-validator"; - -import { StringFilter } from "../../common/filter/string.filter"; -import { OffsetBasedPaginationArgs } from "../../common/pagination/offset-based.args"; -import { SortDirection } from "../../common/sorting/sort-direction.enum"; - -@InputType() -class UserFilter { - @Field(() => StringFilter, { nullable: true }) - @ValidateNested() - @Type(() => StringFilter) - name?: StringFilter; - - @Field(() => StringFilter, { nullable: true }) - @ValidateNested() - @Type(() => StringFilter) - email?: StringFilter; - - @Field(() => StringFilter, { nullable: true }) - @ValidateNested() - @Type(() => StringFilter) - status?: StringFilter; - - @Field(() => StringFilter, { nullable: true }) - @ValidateNested() - @Type(() => StringFilter) - language?: StringFilter; - - @Field(() => [UserFilter], { nullable: true }) - @Type(() => UserFilter) - @ValidateNested({ each: true }) - and?: UserFilter[]; - - @Field(() => [UserFilter], { nullable: true }) - @Type(() => UserFilter) - @ValidateNested({ each: true }) - or?: UserFilter[]; -} - -enum UserSortField { - name = "name", - email = "email", - status = "status", - language = "language", -} -registerEnumType(UserSortField, { - name: "UserSortField", -}); - -@InputType() -class UserSort { - @Field(() => UserSortField) - @IsEnum(UserSortField) - field: UserSortField; - - @Field(() => SortDirection, { defaultValue: SortDirection.ASC }) - @IsEnum(SortDirection) - direction: SortDirection = SortDirection.ASC; -} - -@ArgsType() -export class FindUsersArgs extends OffsetBasedPaginationArgs { - @Field({ nullable: true }) - @IsOptional() - @IsString() - search?: string; - - @Field(() => UserFilter, { nullable: true }) - @ValidateNested() - @Type(() => UserFilter) - filter?: UserFilter; - - @Field(() => [UserSort], { nullable: true }) - @ValidateNested({ each: true }) - @Type(() => UserSort) - sort?: UserSort[]; -} diff --git a/packages/api/cms-api/src/user-permissions/dto/user-content-scopes.input.ts b/packages/api/cms-api/src/user-permissions/dto/user-content-scopes.input.ts deleted file mode 100644 index 59ae5eec28..0000000000 --- a/packages/api/cms-api/src/user-permissions/dto/user-content-scopes.input.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Field, InputType } from "@nestjs/graphql"; -import { IsArray } from "class-validator"; -import { GraphQLJSONObject } from "graphql-type-json"; - -import { ContentScope } from "../interfaces/content-scope.interface"; - -@InputType() -export class UserContentScopesInput { - @Field(() => [GraphQLJSONObject], { defaultValue: [] }) - @IsArray() - contentScopes: ContentScope[] = []; -} diff --git a/packages/api/cms-api/src/user-permissions/dto/user-permission.input.ts b/packages/api/cms-api/src/user-permissions/dto/user-permission.input.ts deleted file mode 100644 index 43e25f2e5c..0000000000 --- a/packages/api/cms-api/src/user-permissions/dto/user-permission.input.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Field, InputType } from "@nestjs/graphql"; -import { IsDate, IsOptional, IsString } from "class-validator"; - -@InputType() -export class UserPermissionInput { - @Field() - @IsString() - permission: string; - - @Field(() => Date, { nullable: true }) - @IsDate() - @IsOptional() - validFrom?: Date; - - @Field(() => Date, { nullable: true }) - @IsDate() - @IsOptional() - validTo?: Date; - - @Field({ nullable: true }) - @IsString() - @IsOptional() - reason?: string; - - @Field({ nullable: true }) - @IsString() - @IsOptional() - requestedBy?: string; - - @Field({ nullable: true }) - @IsString() - @IsOptional() - approvedBy?: string; -} diff --git a/packages/api/cms-api/src/user-permissions/dto/user.ts b/packages/api/cms-api/src/user-permissions/dto/user.ts deleted file mode 100644 index a19fe0fc69..0000000000 --- a/packages/api/cms-api/src/user-permissions/dto/user.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Field, ObjectType } from "@nestjs/graphql"; - -@ObjectType() -export class User { - @Field() - id: string; - - @Field() - name: string; - - @Field() - email: string; - - @Field() - language: string; -} diff --git a/packages/api/cms-api/src/user-permissions/entities/user-content-scopes.entity.ts b/packages/api/cms-api/src/user-permissions/entities/user-content-scopes.entity.ts deleted file mode 100644 index fb7c8d16de..0000000000 --- a/packages/api/cms-api/src/user-permissions/entities/user-content-scopes.entity.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Entity, PrimaryKey, Property } from "@mikro-orm/core"; - -import { ContentScope } from "../interfaces/content-scope.interface"; - -@Entity() -export class UserContentScopes { - @PrimaryKey() - @Property() - userId: string; - - @Property({ type: "json" }) - contentScopes: ContentScope[] = []; -} diff --git a/packages/api/cms-api/src/user-permissions/entities/user-permission.entity.ts b/packages/api/cms-api/src/user-permissions/entities/user-permission.entity.ts deleted file mode 100644 index 2536d19a59..0000000000 --- a/packages/api/cms-api/src/user-permissions/entities/user-permission.entity.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { BaseEntity, Entity, PrimaryKey, Property } from "@mikro-orm/core"; -import { Field, ID, ObjectType, registerEnumType } from "@nestjs/graphql"; -/* eslint-disable @typescript-eslint/naming-convention */ -import { v4 } from "uuid"; - -export enum UserPermissionSource { - MANUAL = "manual", - BY_RULE = "by rule", -} -registerEnumType(UserPermissionSource, { - name: "UserPermissionSource", -}); - -@ObjectType() -@Entity() -export class UserPermission extends BaseEntity { - @Field(() => ID) - @PrimaryKey({ type: "uuid" }) - id: string = v4(); - - @Property() - userId: string; - - @Field(() => UserPermissionSource, { nullable: true }) - source: UserPermissionSource; - - @Field() - @Property({ columnType: "text" }) - permission: string; - - @Field(() => Date, { nullable: true }) - @Property({ columnType: "date", nullable: true }) - validFrom?: Date; - - @Field(() => Date, { nullable: true }) - @Property({ columnType: "date", nullable: true }) - validTo?: Date; - - @Field({ nullable: true }) - @Property({ columnType: "text", nullable: true }) - reason?: string; - - @Field({ nullable: true }) - @Property({ columnType: "text", nullable: true }) - requestedBy?: string; - - @Field({ nullable: true }) - @Property({ columnType: "text", nullable: true }) - approvedBy?: string; -} diff --git a/packages/api/cms-api/src/user-permissions/interfaces/content-scope.interface.ts b/packages/api/cms-api/src/user-permissions/interfaces/content-scope.interface.ts deleted file mode 100644 index cdca94f287..0000000000 --- a/packages/api/cms-api/src/user-permissions/interfaces/content-scope.interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ContentScope { - [key: string]: string; -} diff --git a/packages/api/cms-api/src/user-permissions/interfaces/user-permission.interface.ts b/packages/api/cms-api/src/user-permissions/interfaces/user-permission.interface.ts deleted file mode 100644 index b7ea2d323b..0000000000 --- a/packages/api/cms-api/src/user-permissions/interfaces/user-permission.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Permission { - dam?: string; - pageTree?: string; - userPermissions?: string; - system?: string; -} diff --git a/packages/api/cms-api/src/user-permissions/user-content-scopes.resolver.ts b/packages/api/cms-api/src/user-permissions/user-content-scopes.resolver.ts deleted file mode 100644 index 1f7e46827e..0000000000 --- a/packages/api/cms-api/src/user-permissions/user-content-scopes.resolver.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { EntityRepository } from "@mikro-orm/core"; -import { InjectRepository } from "@mikro-orm/nestjs"; -import { Args, Mutation, Query, Resolver } from "@nestjs/graphql"; -import { GraphQLJSONObject } from "graphql-type-json"; - -import { SkipBuild } from "../builds/skip-build.decorator"; -import { UserContentScopesInput } from "./dto/user-content-scopes.input"; -import { UserContentScopes } from "./entities/user-content-scopes.entity"; -import { ContentScope } from "./interfaces/content-scope.interface"; -import { UserPermissionsService } from "./user-permissions.service"; - -@Resolver() -export class UserContentScopesResolver { - constructor( - @InjectRepository(UserContentScopes) private readonly repository: EntityRepository, - private readonly userService: UserPermissionsService, - ) {} - - @Mutation(() => [GraphQLJSONObject]) - @SkipBuild() - async userPermissionsUpdateContentScopes( - @Args("userId", { type: () => String }) userId: string, - @Args("input", { type: () => UserContentScopesInput }) { contentScopes }: UserContentScopesInput, - ): Promise { - this.userService.checkContentScopes(contentScopes); - let entity = await this.repository.findOne({ userId }); - if (entity) { - entity = this.repository.assign(entity, { userId, contentScopes }); - } else { - entity = this.repository.create({ userId, contentScopes }); - } - await this.repository.persistAndFlush(entity); - return this.userService.getContentScopes(userId); - } - - @Query(() => [GraphQLJSONObject]) - async userPermissionsContentScopes( - @Args("userId", { type: () => String }) userId: string, - @Args("skipManual", { type: () => Boolean, nullable: true }) skipManual = false, - ): Promise { - return this.userService.getContentScopes(userId, skipManual); - } - - @Query(() => [GraphQLJSONObject]) - async userPermissionsAvailableContentScopes(): Promise { - return this.userService.getAvailableContentScopes(); - } -} diff --git a/packages/api/cms-api/src/user-permissions/user-permission.resolver.ts b/packages/api/cms-api/src/user-permissions/user-permission.resolver.ts deleted file mode 100644 index 4ec5e15cce..0000000000 --- a/packages/api/cms-api/src/user-permissions/user-permission.resolver.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { EntityRepository } from "@mikro-orm/core"; -import { InjectRepository } from "@mikro-orm/nestjs"; -import { Args, ArgsType, Field, ID, Mutation, Query, Resolver } from "@nestjs/graphql"; -import { IsString } from "class-validator"; - -import { SkipBuild } from "../builds/skip-build.decorator"; -import { UserPermissionInput } from "./dto/user-permission.input"; -import { UserPermission } from "./entities/user-permission.entity"; -import { UserPermissionsService } from "./user-permissions.service"; - -@ArgsType() -export class UserPermissionListArgs { - @Field() - @IsString() - userId: string; -} - -@Resolver(() => UserPermission) -export class UserPermissionResolver { - constructor( - private readonly userService: UserPermissionsService, - @InjectRepository(UserPermission) private readonly permissionRepository: EntityRepository, - ) {} - - @Query(() => [UserPermission]) - async userPermissionsPermissionList(@Args() args: UserPermissionListArgs): Promise { - return this.userService.getPermissions(args.userId); - } - - @Query(() => UserPermission) - async userPermissionsPermission( - @Args("id", { type: () => ID }) id: string, - @Args("userId", { type: () => String, nullable: true }) userId?: string, - ): Promise { - return this.getPermission(id, userId); - } - - @Mutation(() => UserPermission) - @SkipBuild() - async userPermissionsCreatePermission( - @Args("userId", { type: () => String }) userId: string, - @Args("input", { type: () => UserPermissionInput }) input: UserPermissionInput, - ): Promise { - const permission = new UserPermission(); - this.userService.getUser(userId); //validate user exists - permission.userId = userId; - permission.assign(input); - await this.permissionRepository.persistAndFlush(permission); - return permission; - } - - @Query(() => [String]) - async userPermissionsAvailablePermissions(): Promise { - return this.userService.getAvailablePermissions(); - } - - @Mutation(() => UserPermission) - @SkipBuild() - async userPermissionsUpdatePermission( - @Args("id", { type: () => String }) id: string, - @Args("input", { type: () => UserPermissionInput }) input: UserPermissionInput, - ): Promise { - const permission = await this.getPermission(id); - permission.assign(input); - await this.permissionRepository.persistAndFlush(permission); - return permission; - } - - @Mutation(() => Boolean) - @SkipBuild() - async userPermissionsDeletePermission(@Args("id", { type: () => ID }) id: string): Promise { - this.permissionRepository.removeAndFlush(await this.getPermission(id)); - return true; - } - - async getPermission(id: string, userId?: string): Promise { - const permission = await this.permissionRepository.findOne(id); - if (permission) return permission; - if (!userId) { - throw new Error(`Permission not found: ${id}`); - } - for (const p of await this.userService.getPermissions(userId)) { - if (p.id === id) return p; - } - throw new Error("Permission not found"); - } -} diff --git a/packages/api/cms-api/src/user-permissions/user-permissions.constants.ts b/packages/api/cms-api/src/user-permissions/user-permissions.constants.ts deleted file mode 100644 index 144389ed03..0000000000 --- a/packages/api/cms-api/src/user-permissions/user-permissions.constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const USER_PERMISSIONS_OPTIONS = "user-permissions-options"; diff --git a/packages/api/cms-api/src/user-permissions/user-permissions.module.ts b/packages/api/cms-api/src/user-permissions/user-permissions.module.ts deleted file mode 100644 index 456e45f19b..0000000000 --- a/packages/api/cms-api/src/user-permissions/user-permissions.module.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { MikroOrmModule } from "@mikro-orm/nestjs"; -import { DynamicModule, Global, Module, Provider } from "@nestjs/common"; - -import { UserContentScopes } from "./entities/user-content-scopes.entity"; -import { UserPermission } from "./entities/user-permission.entity"; -import { UserResolver } from "./user.resolver"; -import { UserContentScopesResolver } from "./user-content-scopes.resolver"; -import { UserPermissionResolver } from "./user-permission.resolver"; -import { USER_PERMISSIONS_OPTIONS } from "./user-permissions.constants"; -import { UserPermissionsService } from "./user-permissions.service"; -import { UserPermissionsAsyncOptions, UserPermissionsOptions, UserPermissionsOptionsFactory } from "./user-permissions.types"; - -@Global() -@Module({ - imports: [MikroOrmModule.forFeature([UserPermission, UserContentScopes])], - providers: [UserPermissionsService, UserResolver, UserPermissionResolver, UserContentScopesResolver, UserPermissionsService], - exports: [UserPermissionsService], -}) -export class UserPermissionsModule { - static forRoot(options: UserPermissionsOptions): DynamicModule { - return { - module: UserPermissionsModule, - providers: [ - { - provide: USER_PERMISSIONS_OPTIONS, - useValue: options, - }, - ], - }; - } - - static forRootAsync(asyncOptions: UserPermissionsAsyncOptions): DynamicModule { - return { - module: UserPermissionsModule, - imports: asyncOptions.imports, - providers: [this.createProvider(asyncOptions)], - }; - } - - private static createProvider(options: UserPermissionsAsyncOptions): Provider { - if (options.useFactory) { - return { - provide: USER_PERMISSIONS_OPTIONS, - useFactory: options.useFactory, - inject: options.inject || [], - }; - } - - // For useClass and useExisting... - return { - provide: USER_PERMISSIONS_OPTIONS, - useFactory: async (optionsFactory: UserPermissionsOptionsFactory) => optionsFactory.createUserPermissionsOptions(), - inject: options.useExisting ? [options.useExisting] : options.useClass ? [options.useClass] : [], - }; - } -} diff --git a/packages/api/cms-api/src/user-permissions/user-permissions.service.ts b/packages/api/cms-api/src/user-permissions/user-permissions.service.ts deleted file mode 100644 index e156090433..0000000000 --- a/packages/api/cms-api/src/user-permissions/user-permissions.service.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { EntityRepository } from "@mikro-orm/core"; -import { InjectRepository } from "@mikro-orm/nestjs"; -import { Inject, Injectable } from "@nestjs/common"; -import { differenceInDays } from "date-fns"; -import isEqual from "lodash.isequal"; -import getUuid from "uuid-by-string"; - -import { CurrentUser } from "./dto/current-user"; -import { FindUsersArgs } from "./dto/paginated-user-list"; -import { User } from "./dto/user"; -import { UserContentScopes } from "./entities/user-content-scopes.entity"; -import { UserPermission, UserPermissionSource } from "./entities/user-permission.entity"; -import { ContentScope } from "./interfaces/content-scope.interface"; -import { Permission } from "./interfaces/user-permission.interface"; -import { USER_PERMISSIONS_OPTIONS } from "./user-permissions.constants"; -import { UserPermissions, UserPermissionsOptions } from "./user-permissions.types"; - -@Injectable() -export class UserPermissionsService { - constructor( - @Inject(USER_PERMISSIONS_OPTIONS) private readonly options: UserPermissionsOptions, - @InjectRepository(UserPermission) private readonly permissionRepository: EntityRepository, - @InjectRepository(UserContentScopes) private readonly contentScopeRepository: EntityRepository, - ) {} - - async getAvailableContentScopes(): Promise { - return this.options.availableContentScopes ?? []; - } - - async getAvailablePermissions(): Promise<(keyof Permission)[]> { - return [...new Set(["dam", "pageTree", "userPermissions", "system", ...(this.options.availablePermissions ?? [])])]; - } - - async getUser(id: string): Promise { - return this.options.userService.getUser(id); - } - - async findUsers(args: FindUsersArgs): Promise<[User[], number]> { - return this.options.userService.findUsers(args); - } - - async checkContentScopes(contentScopes: ContentScope[]): Promise { - const availableContentScopes = await this.getAvailableContentScopes(); - contentScopes.forEach((scope) => { - if (!availableContentScopes.some((cs) => isEqual(cs, scope))) { - throw new Error(`ContentScope does not exist: ${JSON.stringify(scope)}.`); - } - }); - } - - async getPermissions(userId: string): Promise { - const availablePermissions = await this.getAvailablePermissions(); - const permissions = ( - await this.permissionRepository.find({ - $and: [{ userId }, { permission: { $in: availablePermissions } }], - }) - ).map((p) => { - p.source = UserPermissionSource.MANUAL; - return p; - }); - if (this.options.userService.getPermissionsForUser) { - const user = await this.getUser(userId); - if (user) { - let permissionsByRule = await this.options.userService.getPermissionsForUser(user); - if (permissionsByRule === UserPermissions.allPermissions) { - permissionsByRule = availablePermissions.map((permission) => ({ permission })); - } - for (const p of permissionsByRule) { - const permission = new UserPermission(); - permission.id = getUuid(JSON.stringify(p)); - permission.source = UserPermissionSource.BY_RULE; - permission.userId = userId; - permission.assign({ - permission: p.permission, - validFrom: p.validFrom, - validTo: p.validTo, - reason: p.reason, - requestedBy: p.requestedBy, - approvedBy: p.approvedBy, - }); - permissions.push(permission); - } - } - } - - return permissions.sort( - (a, b) => availablePermissions.indexOf(a.permission as keyof Permission) - availablePermissions.indexOf(b.permission as keyof Permission), - ); - } - - async getContentScopes(userId: string, skipManual = false): Promise { - const availableContentScopes = await this.getAvailableContentScopes(); - const contentScopes: ContentScope[] = []; - if (this.options.userService.getContentScopesForUser) { - const user = await this.getUser(userId); - if (user) { - const userContentScopes = await this.options.userService.getContentScopesForUser(user); - if (userContentScopes === UserPermissions.allContentScopes) { - contentScopes.push(...availableContentScopes); - } else { - contentScopes.push(...userContentScopes); - } - } - } - if (!skipManual) { - const entity = await this.contentScopeRepository.findOne({ userId }); - if (entity) { - contentScopes.push(...entity.contentScopes); - } - } - return [...new Set(contentScopes)] // Make values unique - .filter((value) => availableContentScopes.some((cs) => isEqual(cs, value))) // Allow only values that are defined in availableContentScopes - .sort((a, b) => availableContentScopes.indexOf(a) - availableContentScopes.indexOf(b)); // Order by availableContentScopes - } - - async createCurrentUser(user: User): Promise { - const currentUser = new CurrentUser(); - Object.assign(currentUser, { - id: user.id, - name: user.name, - email: user.email ?? "", - language: user.language, - contentScopes: await this.getContentScopes(user.id), - permissions: (await this.getPermissions(user.id)) - .filter( - (p) => - (!p.validFrom || differenceInDays(new Date(), p.validFrom) >= 0) && - (!p.validTo || differenceInDays(p.validTo, new Date()) >= 0), - ) - .map((p) => ({ - permission: p.permission, - })), - }); - return currentUser; - } -} diff --git a/packages/api/cms-api/src/user-permissions/user-permissions.types.ts b/packages/api/cms-api/src/user-permissions/user-permissions.types.ts deleted file mode 100644 index b76d48a914..0000000000 --- a/packages/api/cms-api/src/user-permissions/user-permissions.types.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { ModuleMetadata, Type } from "@nestjs/common"; - -import { FindUsersArgs } from "./dto/paginated-user-list"; -import { User } from "./dto/user"; -import { UserPermission } from "./entities/user-permission.entity"; -import { ContentScope } from "./interfaces/content-scope.interface"; -import { Permission } from "./interfaces/user-permission.interface"; - -export enum UserPermissions { - allContentScopes = "all-content-scopes", - allPermissions = "all-permissions", -} - -export type Users = [User[], number]; - -export type PermissionsForUser = - | Pick[] - | UserPermissions.allPermissions; - -export type ContentScopesForUser = ContentScope[] | UserPermissions.allContentScopes; - -export interface UserPermissionsOptions { - availablePermissions?: (keyof Permission)[]; - availableContentScopes?: ContentScope[]; - userService: UserPermissionsUserService; -} - -export interface UserPermissionsUserService { - getUser: (id: string) => Promise | User; - findUsers: (args: FindUsersArgs) => Promise | Users; - getPermissionsForUser?: (user: User) => Promise | PermissionsForUser; - getContentScopesForUser?: (user: User) => Promise | ContentScopesForUser; -} - -export interface UserPermissionsOptionsFactory { - createUserPermissionsOptions(): Promise | UserPermissionsOptions; -} - -export interface UserPermissionsAsyncOptions extends Pick { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - inject?: any[]; - useExisting?: Type; - useClass?: Type; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - useFactory?: (...args: any[]) => Promise | UserPermissionsOptions; -} diff --git a/packages/api/cms-api/src/user-permissions/user.resolver.ts b/packages/api/cms-api/src/user-permissions/user.resolver.ts deleted file mode 100644 index 2773bc0e9a..0000000000 --- a/packages/api/cms-api/src/user-permissions/user.resolver.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Args, ObjectType, Query, Resolver } from "@nestjs/graphql"; - -import { PaginatedResponseFactory } from "../common/pagination/paginated-response.factory"; -import { FindUsersArgs } from "./dto/paginated-user-list"; -import { User } from "./dto/user"; -import { UserPermissionsService } from "./user-permissions.service"; - -@ObjectType() -class PaginatedUserList extends PaginatedResponseFactory.create(User) {} - -@Resolver(() => User) -export class UserResolver { - constructor(private readonly userService: UserPermissionsService) {} - - @Query(() => User) - async userPermissionsUserById(@Args("id", { type: () => String }) id: string): Promise { - return this.userService.getUser(id); - } - - @Query(() => PaginatedUserList) - async userPermissionsUsers(@Args() args: FindUsersArgs): Promise { - const [users, totalCount] = await this.userService.findUsers(args); - return new PaginatedUserList(users, totalCount, args); - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3aa69379f..f70da55deb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2497,9 +2497,6 @@ importers: uuid: specifier: ^9.0.0 version: 9.0.0 - uuid-by-string: - specifier: ^4.0.0 - version: 4.0.0 devDependencies: '@aws-sdk/client-s3': specifier: ^3.47.0 @@ -2626,7 +2623,7 @@ importers: version: 7.8.0 ts-jest: specifier: ^29.0.5 - version: 29.0.5(@babel/core@7.20.12)(jest@29.5.0)(typescript@4.9.4) + version: 29.0.5(jest@29.5.0)(typescript@4.9.4) ts-node: specifier: ^10.0.0 version: 10.9.1(@types/node@18.15.3)(typescript@4.9.4) @@ -4316,7 +4313,7 @@ packages: '@babel/traverse': 7.22.11 '@babel/types': 7.22.11 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -6744,6 +6741,24 @@ packages: '@babel/parser': 7.22.14 '@babel/types': 7.22.11 + /@babel/traverse@7.20.13: + resolution: {integrity: sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.7 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.20.13 + '@babel/types': 7.20.7 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/traverse@7.20.13(supports-color@5.5.0): resolution: {integrity: sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==} engines: {node: '>=6.9.0'} @@ -6773,7 +6788,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.22.14 '@babel/types': 7.22.11 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -8128,7 +8143,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 espree: 9.5.2 globals: 13.19.0 ignore: 5.2.4 @@ -9486,7 +9501,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -18347,6 +18362,17 @@ packages: ms: 2.1.3 dev: false + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + /debug@4.3.4(supports-color@5.5.0): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -19665,7 +19691,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 @@ -21297,7 +21323,7 @@ packages: peerDependencies: graphql: '>=0.9 <0.14 || ^14.0.2 || ^15.4.0 || ^16.3.0' dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 graphql: 15.8.0 tslib: 2.4.1 transitivePeerDependencies: @@ -22806,7 +22832,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: @@ -23252,7 +23278,7 @@ packages: '@babel/generator': 7.20.7 '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.22.11) '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.11) - '@babel/traverse': 7.20.13(supports-color@5.5.0) + '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 '@jest/expect-utils': 29.5.0 '@jest/transform': 29.5.0 @@ -23400,17 +23426,9 @@ packages: engines: {node: '>=0.10.0'} dev: true - /js-md5@0.7.3: - resolution: {integrity: sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==} - dev: false - /js-sdsl@4.3.0: resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} - /js-sha1@0.6.0: - resolution: {integrity: sha512-01gwBFreYydzmU9BmZxpVk6svJJHrVxEN3IOiGl6VO93bVKYETJ0sIth6DASI6mIFdt7NmfX9UiByRzsYHGU9w==} - dev: false - /js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} engines: {node: '>= 0.8'} @@ -23694,7 +23712,7 @@ packages: dependencies: '@types/express': 4.17.16 '@types/jsonwebtoken': 9.0.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 jose: 4.11.2 limiter: 1.1.5 lru-memoizer: 2.1.4 @@ -23785,7 +23803,7 @@ packages: dependencies: colorette: 2.0.19 commander: 9.5.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 escalade: 3.1.1 esm: 3.2.25 get-package-type: 0.1.0 @@ -30180,6 +30198,39 @@ packages: yargs-parser: 21.1.1 dev: true + /ts-jest@29.0.5(jest@29.5.0)(typescript@4.9.4): + resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.5.0(@types/node@18.15.3)(ts-node@10.9.1) + jest-util: 29.5.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.8 + typescript: 4.9.4 + yargs-parser: 21.1.1 + dev: true + /ts-loader@6.2.2(typescript@4.9.4): resolution: {integrity: sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ==} engines: {node: '>=8.6'} @@ -30944,13 +30995,6 @@ packages: resolution: {integrity: sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg==} dev: false - /uuid-by-string@4.0.0: - resolution: {integrity: sha512-88ZSfcSkN04juiLqSsuyteqlSrXNFdsEPzSv3urnElDXNsZUXQN0smeTnh99x2DE15SCUQNgqKBfro54CuzHNQ==} - dependencies: - js-md5: 0.7.3 - js-sha1: 0.6.0 - dev: false - /uuid@3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.