diff --git a/web/lib/permissions/graphql/server/get-app-read-permissions.generated.ts b/web/lib/permissions/graphql/server/get-app-read-permissions.generated.ts new file mode 100644 index 000000000..80c66092d --- /dev/null +++ b/web/lib/permissions/graphql/server/get-app-read-permissions.generated.ts @@ -0,0 +1,77 @@ +/* eslint-disable */ +import * as Types from "@/graphql/graphql"; + +import { GraphQLClient, RequestOptions } from "graphql-request"; +import gql from "graphql-tag"; +type GraphQLClientRequestHeaders = RequestOptions["requestHeaders"]; +export type GetIsUserPermittedToReadAppQueryVariables = Types.Exact<{ + appId: Types.Scalars["String"]["input"]; + userId: Types.Scalars["String"]["input"]; +}>; + +export type GetIsUserPermittedToReadAppQuery = { + __typename?: "query_root"; + app_by_pk?: { + __typename?: "app"; + team: { + __typename?: "team"; + memberships: Array<{ + __typename?: "membership"; + user_id: string; + role: Types.Role_Enum; + }>; + }; + } | null; +}; + +export const GetIsUserPermittedToReadAppDocument = gql` + query GetIsUserPermittedToReadApp($appId: String!, $userId: String!) { + app_by_pk(id: $appId) { + team { + memberships(where: { user_id: { _eq: $userId } }) { + user_id + role + } + } + } + } +`; + +export type SdkFunctionWrapper = ( + action: (requestHeaders?: Record) => Promise, + operationName: string, + operationType?: string, + variables?: any, +) => Promise; + +const defaultWrapper: SdkFunctionWrapper = ( + action, + _operationName, + _operationType, + _variables, +) => action(); + +export function getSdk( + client: GraphQLClient, + withWrapper: SdkFunctionWrapper = defaultWrapper, +) { + return { + GetIsUserPermittedToReadApp( + variables: GetIsUserPermittedToReadAppQueryVariables, + requestHeaders?: GraphQLClientRequestHeaders, + ): Promise { + return withWrapper( + (wrappedRequestHeaders) => + client.request( + GetIsUserPermittedToReadAppDocument, + variables, + { ...requestHeaders, ...wrappedRequestHeaders }, + ), + "GetIsUserPermittedToReadApp", + "query", + variables, + ); + }, + }; +} +export type Sdk = ReturnType; diff --git a/web/lib/permissions/graphql/server/get-app-read-permissions.graphql b/web/lib/permissions/graphql/server/get-app-read-permissions.graphql new file mode 100644 index 000000000..9df06006d --- /dev/null +++ b/web/lib/permissions/graphql/server/get-app-read-permissions.graphql @@ -0,0 +1,10 @@ +query GetIsUserPermittedToReadApp($appId: String!, $userId: String!) { + app_by_pk(id: $appId) { + team { + memberships(where: { user_id: { _eq: $userId } }) { + user_id + role + } + } + } +} diff --git a/web/lib/permissions/index.ts b/web/lib/permissions/index.ts index 570581c47..220f26d81 100644 --- a/web/lib/permissions/index.ts +++ b/web/lib/permissions/index.ts @@ -6,6 +6,7 @@ import { entityIdSchema } from "../schema"; import { getSdk as getAppDeletePermissionsSdk } from "./graphql/server/get-app-delete-permissions.generated"; import { getSdk as getAppInsertPermissionsSdk } from "./graphql/server/get-app-insert-permissions.generated"; import { getSdk as getAppMetadataPermissionsSdk } from "./graphql/server/get-app-metadata-update-permissions.generated"; +import { getSdk as getAppReadPermissionsSdk } from "./graphql/server/get-app-read-permissions.generated"; import { getSdk as getAppUpdatePermissionsSdk } from "./graphql/server/get-app-update-permissions.generated"; import { getSdk as getLocalisationsDeletePermissionsSdk } from "./graphql/server/get-localisations-delete-permissions.generated"; import { getSdk as getLocalisationsInsertPermissionsSdk } from "./graphql/server/get-localisations-insert-permissions.generated"; @@ -40,6 +41,27 @@ export const getIsUserAllowedToInsertApp = async (teamId: string) => { return false; }; +export const getIsUserAllowedToReadApp = async (appId: string) => { + if (!getIsIdValid(appId)) { + return false; + } + + const session = await getSession(); + if (!session) { + return false; + } + + const userId = session.user.hasura.id; + const response = await getAppReadPermissionsSdk( + await getAPIServiceGraphqlClient(), + ).GetIsUserPermittedToReadApp({ appId, userId }); + + if (response.app_by_pk?.team.memberships.length) { + return true; + } + return false; +}; + export const getIsUserAllowedToUpdateApp = async (appId: string) => { if (!getIsIdValid(appId)) { return false; diff --git a/web/scenes/Portal/Teams/TeamId/Apps/AppId/MiniApp/Transactions/page/server/getAccumulativeTransactionData.ts b/web/scenes/Portal/Teams/TeamId/Apps/AppId/MiniApp/Transactions/page/server/getAccumulativeTransactionData.ts index 3a9ca3853..0f5248f70 100644 --- a/web/scenes/Portal/Teams/TeamId/Apps/AppId/MiniApp/Transactions/page/server/getAccumulativeTransactionData.ts +++ b/web/scenes/Portal/Teams/TeamId/Apps/AppId/MiniApp/Transactions/page/server/getAccumulativeTransactionData.ts @@ -1,6 +1,7 @@ "use server"; import { errorFormAction } from "@/api/helpers/errors"; +import { getIsUserAllowedToReadApp } from "@/lib/permissions"; import { extractIdsFromPath, getPathFromHeaders } from "@/lib/server-utils"; import { FormActionResult, @@ -85,6 +86,17 @@ export const getAccumulativePaymentsData = async ( const path = getPathFromHeaders() || ""; const { Teams: teamId } = extractIdsFromPath(path, ["Teams"]); + const isAllowed = await getIsUserAllowedToReadApp(appId); + + if (!isAllowed) { + return errorFormAction({ + message: "User is not authorized to view this app's data", + app_id: appId, + team_id: teamId, + logLevel: "error", + }); + } + try { if (!process.env.NEXT_SERVER_INTERNAL_PAYMENTS_ENDPOINT) { return errorFormAction({ @@ -197,6 +209,17 @@ export const getAccumulativeTransactionsData = async ( const path = getPathFromHeaders() || ""; const { Teams: teamId } = extractIdsFromPath(path, ["Teams"]); + const isAllowed = await getIsUserAllowedToReadApp(appId); + + if (!isAllowed) { + return errorFormAction({ + message: "User is not authorized to view this app's data", + app_id: appId, + team_id: teamId, + logLevel: "error", + }); + } + try { if (!process.env.NEXT_SERVER_INTERNAL_PAYMENTS_ENDPOINT) { return errorFormAction({