From 44d85a2b12315bfada1e3c396753e1934be952fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Thu, 15 Jan 2026 16:16:06 +0400 Subject: [PATCH] Trust center access management - get rid of access token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- .../src/components/OrganizationSidebar.tsx | 4 +- ...estAccessDialogDocumentMutation.graphql.ts | 105 ---- .../RequestAccessDialogMutation.graphql.ts | 87 --- ...questAccessDialogReportMutation.graphql.ts | 105 ---- ...ssDialogTrustCenterFileMutation.graphql.ts | 105 ---- apps/trust/src/layouts/MainLayout.tsx | 2 +- apps/trust/src/pages/OverviewPage.tsx | 6 +- .../src/providers/TrustCenterProvider.tsx | 6 +- apps/trust/src/queries/TrustGraph.ts | 81 +-- .../TrustGraphCurrentQuery.graphql.ts | 12 +- .../TrustGraphDocumentsQuery.graphql.ts | 339 ---------- .../__generated__/TrustGraphQuery.graphql.ts | 581 ------------------ .../TrustGraphVendorsQuery.graphql.ts | 244 -------- packages/emails/src/DocumentSigning.tsx | 2 +- packages/emails/src/Invitation.tsx | 2 +- packages/emails/src/TrustCenterAccess.tsx | 4 - .../emails/templates/trust-center-access.txt | 2 - pkg/coredata/custom_domain.go | 4 + pkg/coredata/trust_center.go | 12 + pkg/coredata/trust_center_file.go | 5 +- pkg/coredata/trust_center_file_filter.go | 6 +- pkg/iam/errors.go | 2 +- pkg/probo/trust_center_access_service.go | 28 +- pkg/probo/trust_center_file_service.go | 3 +- pkg/server/api/compliancepage/context.go | 8 +- .../api/compliancepage/id_middleware.go | 46 +- .../compliancepage/membership_middleware.go | 74 +++ .../api/compliancepage/sni_middleware.go | 31 +- pkg/server/api/console/v1/v1_resolver.go | 2 +- pkg/server/api/trust/v1/graphql_handler.go | 63 ++ pkg/server/api/trust/v1/resolver.go | 19 +- pkg/server/api/trust/v1/schema.graphql | 25 +- pkg/server/api/trust/v1/schema/schema.go | 232 ++++++- pkg/server/api/trust/v1/types/types.go | 2 +- pkg/server/api/trust/v1/v1_resolver.go | 25 +- pkg/trust/errors.go | 23 + pkg/trust/service.go | 53 ++ pkg/trust/trust_center_access_service.go | 29 +- pkg/trust/trust_center_file_service.go | 3 +- 39 files changed, 582 insertions(+), 1800 deletions(-) delete mode 100644 apps/trust/src/components/__generated__/RequestAccessDialogDocumentMutation.graphql.ts delete mode 100644 apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts delete mode 100644 apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts delete mode 100644 apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts delete mode 100644 apps/trust/src/queries/__generated__/TrustGraphDocumentsQuery.graphql.ts delete mode 100644 apps/trust/src/queries/__generated__/TrustGraphQuery.graphql.ts delete mode 100644 apps/trust/src/queries/__generated__/TrustGraphVendorsQuery.graphql.ts create mode 100644 pkg/server/api/compliancepage/membership_middleware.go create mode 100644 pkg/server/api/trust/v1/graphql_handler.go create mode 100644 pkg/trust/errors.go diff --git a/apps/trust/src/components/OrganizationSidebar.tsx b/apps/trust/src/components/OrganizationSidebar.tsx index d663de99b..b62259f6d 100644 --- a/apps/trust/src/components/OrganizationSidebar.tsx +++ b/apps/trust/src/components/OrganizationSidebar.tsx @@ -7,7 +7,6 @@ import { IconMedal, useToast, } from "@probo/ui"; -import type { TrustGraphQuery$data } from "/queries/__generated__/TrustGraphQuery.graphql"; import { use, type PropsWithChildren } from "react"; import { domain, formatError } from "@probo/helpers"; import { AuditRowAvatar } from "./AuditRow"; @@ -16,6 +15,7 @@ import { MagicLinkDialog } from "./MagicLinkDialog"; import { graphql } from "relay-runtime"; import { useMutation } from "react-relay"; import type { OrganizationSidebar_requestAllAccessesMutation } from "./__generated__/OrganizationSidebar_requestAllAccessesMutation.graphql"; +import type { TrustGraphCurrentQuery$data } from "/queries/__generated__/TrustGraphCurrentQuery.graphql"; const requestAllAccessesMutation = graphql` mutation OrganizationSidebar_requestAllAccessesMutation { @@ -30,7 +30,7 @@ const requestAllAccessesMutation = graphql` export function OrganizationSidebar({ trustCenter, }: { - trustCenter: TrustGraphQuery$data["currentTrustCenter"]; + trustCenter: TrustGraphCurrentQuery$data["currentTrustCenter"]; }) { const { __ } = useTranslate(); const isAuthenticated = !!use(Viewer); diff --git a/apps/trust/src/components/__generated__/RequestAccessDialogDocumentMutation.graphql.ts b/apps/trust/src/components/__generated__/RequestAccessDialogDocumentMutation.graphql.ts deleted file mode 100644 index b1aef2ba9..000000000 --- a/apps/trust/src/components/__generated__/RequestAccessDialogDocumentMutation.graphql.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @generated SignedSource<<2ea33498f7101d861d940b9c52ed3283>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest } from 'relay-runtime'; -export type RequestDocumentAccessInput = { - documentId: string; -}; -export type RequestAccessDialogDocumentMutation$variables = { - input: RequestDocumentAccessInput; -}; -export type RequestAccessDialogDocumentMutation$data = { - readonly requestDocumentAccess: { - readonly trustCenterAccess: { - readonly id: string; - }; - }; -}; -export type RequestAccessDialogDocumentMutation = { - response: RequestAccessDialogDocumentMutation$data; - variables: RequestAccessDialogDocumentMutation$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = [ - { - "defaultValue": null, - "kind": "LocalArgument", - "name": "input" - } -], -v1 = [ - { - "alias": null, - "args": [ - { - "kind": "Variable", - "name": "input", - "variableName": "input" - } - ], - "concreteType": "RequestAccessesPayload", - "kind": "LinkedField", - "name": "requestDocumentAccess", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterAccess", - "kind": "LinkedField", - "name": "trustCenterAccess", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } -]; -return { - "fragment": { - "argumentDefinitions": (v0/*: any*/), - "kind": "Fragment", - "metadata": null, - "name": "RequestAccessDialogDocumentMutation", - "selections": (v1/*: any*/), - "type": "Mutation", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": (v0/*: any*/), - "kind": "Operation", - "name": "RequestAccessDialogDocumentMutation", - "selections": (v1/*: any*/) - }, - "params": { - "cacheID": "2ed1f0d19711a9f22acdc6251dbfca8f", - "id": null, - "metadata": {}, - "name": "RequestAccessDialogDocumentMutation", - "operationKind": "mutation", - "text": "mutation RequestAccessDialogDocumentMutation(\n $input: RequestDocumentAccessInput!\n) {\n requestDocumentAccess(input: $input) {\n trustCenterAccess {\n id\n }\n }\n}\n" - } -}; -})(); - -(node as any).hash = "c3ebabe2f3fac32e5035cc7acfd54799"; - -export default node; diff --git a/apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts b/apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts deleted file mode 100644 index 61cd80bd5..000000000 --- a/apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @generated SignedSource<> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest } from 'relay-runtime'; -export type RequestAccessDialogMutation$variables = Record; -export type RequestAccessDialogMutation$data = { - readonly requestAllAccesses: { - readonly trustCenterAccess: { - readonly id: string; - }; - }; -}; -export type RequestAccessDialogMutation = { - response: RequestAccessDialogMutation$data; - variables: RequestAccessDialogMutation$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = [ - { - "alias": null, - "args": null, - "concreteType": "RequestAccessesPayload", - "kind": "LinkedField", - "name": "requestAllAccesses", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterAccess", - "kind": "LinkedField", - "name": "trustCenterAccess", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } -]; -return { - "fragment": { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "RequestAccessDialogMutation", - "selections": (v0/*: any*/), - "type": "Mutation", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": [], - "kind": "Operation", - "name": "RequestAccessDialogMutation", - "selections": (v0/*: any*/) - }, - "params": { - "cacheID": "b48847e88c57289efc120745c743c8f6", - "id": null, - "metadata": {}, - "name": "RequestAccessDialogMutation", - "operationKind": "mutation", - "text": "mutation RequestAccessDialogMutation {\n requestAllAccesses {\n trustCenterAccess {\n id\n }\n }\n}\n" - } -}; -})(); - -(node as any).hash = "44c3538831968b7146fbf2c9e027a778"; - -export default node; diff --git a/apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts b/apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts deleted file mode 100644 index 59fb271e9..000000000 --- a/apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @generated SignedSource<<3e5433cc33d54bff9511d01eb4c65b00>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest } from 'relay-runtime'; -export type RequestReportAccessInput = { - reportId: string; -}; -export type RequestAccessDialogReportMutation$variables = { - input: RequestReportAccessInput; -}; -export type RequestAccessDialogReportMutation$data = { - readonly requestReportAccess: { - readonly trustCenterAccess: { - readonly id: string; - }; - }; -}; -export type RequestAccessDialogReportMutation = { - response: RequestAccessDialogReportMutation$data; - variables: RequestAccessDialogReportMutation$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = [ - { - "defaultValue": null, - "kind": "LocalArgument", - "name": "input" - } -], -v1 = [ - { - "alias": null, - "args": [ - { - "kind": "Variable", - "name": "input", - "variableName": "input" - } - ], - "concreteType": "RequestAccessesPayload", - "kind": "LinkedField", - "name": "requestReportAccess", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterAccess", - "kind": "LinkedField", - "name": "trustCenterAccess", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } -]; -return { - "fragment": { - "argumentDefinitions": (v0/*: any*/), - "kind": "Fragment", - "metadata": null, - "name": "RequestAccessDialogReportMutation", - "selections": (v1/*: any*/), - "type": "Mutation", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": (v0/*: any*/), - "kind": "Operation", - "name": "RequestAccessDialogReportMutation", - "selections": (v1/*: any*/) - }, - "params": { - "cacheID": "da69c3e370244e4a9a7793b06b2abdd5", - "id": null, - "metadata": {}, - "name": "RequestAccessDialogReportMutation", - "operationKind": "mutation", - "text": "mutation RequestAccessDialogReportMutation(\n $input: RequestReportAccessInput!\n) {\n requestReportAccess(input: $input) {\n trustCenterAccess {\n id\n }\n }\n}\n" - } -}; -})(); - -(node as any).hash = "3ef2b6424ba751f15f91ad7f4bd28fc5"; - -export default node; diff --git a/apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts b/apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts deleted file mode 100644 index 3770d5701..000000000 --- a/apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @generated SignedSource<<447bd664ebefbd6a672ccde86c63c028>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest } from 'relay-runtime'; -export type RequestTrustCenterFileAccessInput = { - trustCenterFileId: string; -}; -export type RequestAccessDialogTrustCenterFileMutation$variables = { - input: RequestTrustCenterFileAccessInput; -}; -export type RequestAccessDialogTrustCenterFileMutation$data = { - readonly requestTrustCenterFileAccess: { - readonly trustCenterAccess: { - readonly id: string; - }; - }; -}; -export type RequestAccessDialogTrustCenterFileMutation = { - response: RequestAccessDialogTrustCenterFileMutation$data; - variables: RequestAccessDialogTrustCenterFileMutation$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = [ - { - "defaultValue": null, - "kind": "LocalArgument", - "name": "input" - } -], -v1 = [ - { - "alias": null, - "args": [ - { - "kind": "Variable", - "name": "input", - "variableName": "input" - } - ], - "concreteType": "RequestAccessesPayload", - "kind": "LinkedField", - "name": "requestTrustCenterFileAccess", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterAccess", - "kind": "LinkedField", - "name": "trustCenterAccess", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } -]; -return { - "fragment": { - "argumentDefinitions": (v0/*: any*/), - "kind": "Fragment", - "metadata": null, - "name": "RequestAccessDialogTrustCenterFileMutation", - "selections": (v1/*: any*/), - "type": "Mutation", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": (v0/*: any*/), - "kind": "Operation", - "name": "RequestAccessDialogTrustCenterFileMutation", - "selections": (v1/*: any*/) - }, - "params": { - "cacheID": "73b3007ef1a45f6dc6d31be0c627e482", - "id": null, - "metadata": {}, - "name": "RequestAccessDialogTrustCenterFileMutation", - "operationKind": "mutation", - "text": "mutation RequestAccessDialogTrustCenterFileMutation(\n $input: RequestTrustCenterFileAccessInput!\n) {\n requestTrustCenterFileAccess(input: $input) {\n trustCenterAccess {\n id\n }\n }\n}\n" - } -}; -})(); - -(node as any).hash = "a97eb6c4ec94c79a293d1cc51bf18e66"; - -export default node; diff --git a/apps/trust/src/layouts/MainLayout.tsx b/apps/trust/src/layouts/MainLayout.tsx index e4003cf58..0e68cf5f8 100644 --- a/apps/trust/src/layouts/MainLayout.tsx +++ b/apps/trust/src/layouts/MainLayout.tsx @@ -22,7 +22,7 @@ export function MainLayout(props: Props) { return null; } const showNDADialog = - trustCenter.isUserAuthenticated && + trustCenter.isViewerMember && !trustCenter.hasAcceptedNonDisclosureAgreement && trustCenter.ndaFileUrl; return ( diff --git a/apps/trust/src/pages/OverviewPage.tsx b/apps/trust/src/pages/OverviewPage.tsx index c8c484679..2c1095e79 100644 --- a/apps/trust/src/pages/OverviewPage.tsx +++ b/apps/trust/src/pages/OverviewPage.tsx @@ -11,7 +11,6 @@ import { sprintf, getTrustCenterUrl, } from "@probo/helpers"; -import type { TrustGraphQuery$data } from "/queries/__generated__/TrustGraphQuery.graphql"; import { useTranslate } from "@probo/i18n"; import { Card, IconChevronRight } from "@probo/ui"; import { AuditRow } from "/components/AuditRow"; @@ -22,6 +21,7 @@ import { TrustCenterFileRow } from "/components/TrustCenterFileRow"; import { VendorRow } from "/components/VendorRow"; import { RowHeader } from "/components/RowHeader.tsx"; import { Rows } from "/components/Rows.tsx"; +import type { TrustGraphCurrentQuery$data } from "/queries/__generated__/TrustGraphCurrentQuery.graphql"; const overviewFragment = graphql` fragment OverviewPageFragment on TrustCenter { @@ -68,7 +68,7 @@ const overviewFragment = graphql` export function OverviewPage() { const { trustCenter } = useOutletContext<{ trustCenter: OverviewPageFragment$key & - TrustGraphQuery$data["currentTrustCenter"]; + TrustGraphCurrentQuery$data["currentTrustCenter"]; }>(); const fragment = useFragment(overviewFragment, trustCenter); return ( @@ -100,7 +100,7 @@ function Documents({ documents: OverviewPageFragment$data["documents"]["edges"]; files: OverviewPageFragment$data["trustCenterFiles"]["edges"]; audits: NonNullable< - TrustGraphQuery$data["currentTrustCenter"] + TrustGraphCurrentQuery$data["currentTrustCenter"] >["audits"]["edges"]; url: string; }) { diff --git a/apps/trust/src/providers/TrustCenterProvider.tsx b/apps/trust/src/providers/TrustCenterProvider.tsx index 57d44cc09..469eba072 100644 --- a/apps/trust/src/providers/TrustCenterProvider.tsx +++ b/apps/trust/src/providers/TrustCenterProvider.tsx @@ -1,8 +1,8 @@ import { createContext, type ReactNode } from "react"; -import type { TrustGraphQuery$data } from "/queries/__generated__/TrustGraphQuery.graphql"; +import type { TrustGraphCurrentQuery$data } from "/queries/__generated__/TrustGraphCurrentQuery.graphql"; export const TrustCenterContext = createContext< - TrustGraphQuery$data["currentTrustCenter"] | null + TrustGraphCurrentQuery$data["currentTrustCenter"] | null >(null); export const TrustCenterProvider = ({ @@ -10,7 +10,7 @@ export const TrustCenterProvider = ({ trustCenter, }: { children: ReactNode; - trustCenter: TrustGraphQuery$data["currentTrustCenter"]; + trustCenter: TrustGraphCurrentQuery$data["currentTrustCenter"]; }) => { return ( diff --git a/apps/trust/src/queries/TrustGraph.ts b/apps/trust/src/queries/TrustGraph.ts index f178e4b48..db9eded1d 100644 --- a/apps/trust/src/queries/TrustGraph.ts +++ b/apps/trust/src/queries/TrustGraph.ts @@ -1,84 +1,5 @@ import { graphql } from "relay-runtime"; -export const trustGraphQuery = graphql` - query TrustGraphQuery { - currentTrustCenter { - id - slug - isUserAuthenticated - hasAcceptedNonDisclosureAgreement - ndaFileName - ndaFileUrl - organization { - name - description - websiteUrl - logoUrl - email - headquarterAddress - } - ...OverviewPageFragment - audits(first: 50) { - edges { - node { - id - ...AuditRowFragment - } - } - } - } - } -`; - -export const trustDocumentsQuery = graphql` - query TrustGraphDocumentsQuery { - currentTrustCenter { - id - organization { - name - } - documents(first: 50) { - edges { - node { - id - documentType - ...DocumentRowFragment - } - } - } - trustCenterFiles(first: 50) { - edges { - node { - id - category - ...TrustCenterFileRowFragment - } - } - } - } - } -`; - -export const trustVendorsQuery = graphql` - query TrustGraphVendorsQuery { - currentTrustCenter { - id - organization { - name - } - vendors(first: 50) { - edges { - node { - id - countries - ...VendorRowFragment - } - } - } - } - } -`; - // Queries for custom domain (subdomain) approach export const currentTrustGraphQuery = graphql` query TrustGraphCurrentQuery { @@ -89,7 +10,7 @@ export const currentTrustGraphQuery = graphql` currentTrustCenter { id slug - isUserAuthenticated + isViewerMember hasAcceptedNonDisclosureAgreement ndaFileName ndaFileUrl diff --git a/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts index 9b3aa7239..42d17d8ed 100644 --- a/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts +++ b/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<1f5a4da3a77a893daebeceb249612a72>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -23,7 +23,7 @@ export type TrustGraphCurrentQuery$data = { }; readonly hasAcceptedNonDisclosureAgreement: boolean; readonly id: string; - readonly isUserAuthenticated: boolean; + readonly isViewerMember: boolean; readonly ndaFileName: string | null | undefined; readonly ndaFileUrl: string | null | undefined; readonly organization: { @@ -80,7 +80,7 @@ v4 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "isUserAuthenticated", + "name": "isViewerMember", "storageKey": null }, v5 = { @@ -604,16 +604,16 @@ return { ] }, "params": { - "cacheID": "9d22288e0159409cf43bece191e3dc47", + "cacheID": "c76b7662b00ed20e82ed9333792e396b", "id": null, "metadata": {}, "name": "TrustGraphCurrentQuery", "operationKind": "query", - "text": "query TrustGraphCurrentQuery {\n viewer {\n email\n fullName\n id\n }\n currentTrustCenter {\n id\n slug\n isUserAuthenticated\n hasAcceptedNonDisclosureAgreement\n ndaFileName\n ndaFileUrl\n organization {\n name\n description\n websiteUrl\n logoUrl\n email\n headquarterAddress\n id\n }\n ...OverviewPageFragment\n audits(first: 50) {\n edges {\n node {\n id\n ...AuditRowFragment\n }\n }\n }\n }\n}\n\nfragment AuditRowFragment on Audit {\n report {\n id\n filename\n isUserAuthorized\n hasUserRequestedAccess\n }\n framework {\n id\n name\n lightLogoURL\n darkLogoURL\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n isUserAuthorized\n hasUserRequestedAccess\n}\n\nfragment OverviewPageFragment on TrustCenter {\n references(first: 14) {\n edges {\n node {\n id\n name\n logoUrl\n websiteUrl\n }\n }\n }\n vendors(first: 3) {\n edges {\n node {\n id\n countries\n ...VendorRowFragment\n }\n }\n }\n documents(first: 5) {\n edges {\n node {\n id\n ...DocumentRowFragment\n documentType\n }\n }\n }\n trustCenterFiles(first: 5) {\n edges {\n node {\n id\n category\n ...TrustCenterFileRowFragment\n }\n }\n }\n}\n\nfragment TrustCenterFileRowFragment on TrustCenterFile {\n id\n name\n isUserAuthorized\n hasUserRequestedAccess\n}\n\nfragment VendorRowFragment on Vendor {\n id\n name\n category\n websiteUrl\n privacyPolicyUrl\n countries\n}\n" + "text": "query TrustGraphCurrentQuery {\n viewer {\n email\n fullName\n id\n }\n currentTrustCenter {\n id\n slug\n isViewerMember\n hasAcceptedNonDisclosureAgreement\n ndaFileName\n ndaFileUrl\n organization {\n name\n description\n websiteUrl\n logoUrl\n email\n headquarterAddress\n id\n }\n ...OverviewPageFragment\n audits(first: 50) {\n edges {\n node {\n id\n ...AuditRowFragment\n }\n }\n }\n }\n}\n\nfragment AuditRowFragment on Audit {\n report {\n id\n filename\n isUserAuthorized\n hasUserRequestedAccess\n }\n framework {\n id\n name\n lightLogoURL\n darkLogoURL\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n isUserAuthorized\n hasUserRequestedAccess\n}\n\nfragment OverviewPageFragment on TrustCenter {\n references(first: 14) {\n edges {\n node {\n id\n name\n logoUrl\n websiteUrl\n }\n }\n }\n vendors(first: 3) {\n edges {\n node {\n id\n countries\n ...VendorRowFragment\n }\n }\n }\n documents(first: 5) {\n edges {\n node {\n id\n ...DocumentRowFragment\n documentType\n }\n }\n }\n trustCenterFiles(first: 5) {\n edges {\n node {\n id\n category\n ...TrustCenterFileRowFragment\n }\n }\n }\n}\n\nfragment TrustCenterFileRowFragment on TrustCenterFile {\n id\n name\n isUserAuthorized\n hasUserRequestedAccess\n}\n\nfragment VendorRowFragment on Vendor {\n id\n name\n category\n websiteUrl\n privacyPolicyUrl\n countries\n}\n" } }; })(); -(node as any).hash = "dd2040667d5cc7b9af9659e993ba6a5b"; +(node as any).hash = "e8ad7c480c3fac7346ea7d2be6ea5c9f"; export default node; diff --git a/apps/trust/src/queries/__generated__/TrustGraphDocumentsQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphDocumentsQuery.graphql.ts deleted file mode 100644 index e29caf0bf..000000000 --- a/apps/trust/src/queries/__generated__/TrustGraphDocumentsQuery.graphql.ts +++ /dev/null @@ -1,339 +0,0 @@ -/** - * @generated SignedSource<<7b9033da420b208f17d993bee5023358>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest } from 'relay-runtime'; -import { FragmentRefs } from "relay-runtime"; -export type DocumentType = "ISMS" | "OTHER" | "POLICY" | "PROCEDURE"; -export type TrustGraphDocumentsQuery$variables = Record; -export type TrustGraphDocumentsQuery$data = { - readonly currentTrustCenter: { - readonly documents: { - readonly edges: ReadonlyArray<{ - readonly node: { - readonly documentType: DocumentType; - readonly id: string; - readonly " $fragmentSpreads": FragmentRefs<"DocumentRowFragment">; - }; - }>; - }; - readonly id: string; - readonly organization: { - readonly name: string; - }; - readonly trustCenterFiles: { - readonly edges: ReadonlyArray<{ - readonly node: { - readonly category: string; - readonly id: string; - readonly " $fragmentSpreads": FragmentRefs<"TrustCenterFileRowFragment">; - }; - }>; - }; - } | null | undefined; -}; -export type TrustGraphDocumentsQuery = { - response: TrustGraphDocumentsQuery$data; - variables: TrustGraphDocumentsQuery$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null -}, -v1 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "name", - "storageKey": null -}, -v2 = [ - { - "kind": "Literal", - "name": "first", - "value": 50 - } -], -v3 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "documentType", - "storageKey": null -}, -v4 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "category", - "storageKey": null -}, -v5 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "isUserAuthorized", - "storageKey": null -}, -v6 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasUserRequestedAccess", - "storageKey": null -}; -return { - "fragment": { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "TrustGraphDocumentsQuery", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "currentTrustCenter", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "Organization", - "kind": "LinkedField", - "name": "organization", - "plural": false, - "selections": [ - (v1/*: any*/) - ], - "storageKey": null - }, - { - "alias": null, - "args": (v2/*: any*/), - "concreteType": "DocumentConnection", - "kind": "LinkedField", - "name": "documents", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "DocumentEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Document", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v3/*: any*/), - { - "args": null, - "kind": "FragmentSpread", - "name": "DocumentRowFragment" - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "documents(first:50)" - }, - { - "alias": null, - "args": (v2/*: any*/), - "concreteType": "TrustCenterFileConnection", - "kind": "LinkedField", - "name": "trustCenterFiles", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterFileEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterFile", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v4/*: any*/), - { - "args": null, - "kind": "FragmentSpread", - "name": "TrustCenterFileRowFragment" - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "trustCenterFiles(first:50)" - } - ], - "storageKey": null - } - ], - "type": "Query", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": [], - "kind": "Operation", - "name": "TrustGraphDocumentsQuery", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "currentTrustCenter", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "Organization", - "kind": "LinkedField", - "name": "organization", - "plural": false, - "selections": [ - (v1/*: any*/), - (v0/*: any*/) - ], - "storageKey": null - }, - { - "alias": null, - "args": (v2/*: any*/), - "concreteType": "DocumentConnection", - "kind": "LinkedField", - "name": "documents", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "DocumentEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Document", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v3/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "title", - "storageKey": null - }, - (v5/*: any*/), - (v6/*: any*/) - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "documents(first:50)" - }, - { - "alias": null, - "args": (v2/*: any*/), - "concreteType": "TrustCenterFileConnection", - "kind": "LinkedField", - "name": "trustCenterFiles", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterFileEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterFile", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v4/*: any*/), - (v1/*: any*/), - (v5/*: any*/), - (v6/*: any*/) - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "trustCenterFiles(first:50)" - } - ], - "storageKey": null - } - ] - }, - "params": { - "cacheID": "cf917e2efd7c2f644d222385443fd923", - "id": null, - "metadata": {}, - "name": "TrustGraphDocumentsQuery", - "operationKind": "query", - "text": "query TrustGraphDocumentsQuery {\n currentTrustCenter {\n id\n organization {\n name\n id\n }\n documents(first: 50) {\n edges {\n node {\n id\n documentType\n ...DocumentRowFragment\n }\n }\n }\n trustCenterFiles(first: 50) {\n edges {\n node {\n id\n category\n ...TrustCenterFileRowFragment\n }\n }\n }\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n isUserAuthorized\n hasUserRequestedAccess\n}\n\nfragment TrustCenterFileRowFragment on TrustCenterFile {\n id\n name\n isUserAuthorized\n hasUserRequestedAccess\n}\n" - } -}; -})(); - -(node as any).hash = "32ee5adbf1a5cf44f836f45990a95732"; - -export default node; diff --git a/apps/trust/src/queries/__generated__/TrustGraphQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphQuery.graphql.ts deleted file mode 100644 index b16c8bbfc..000000000 --- a/apps/trust/src/queries/__generated__/TrustGraphQuery.graphql.ts +++ /dev/null @@ -1,581 +0,0 @@ -/** - * @generated SignedSource<<1a198d80eadc34edf67fd0f757c6f120>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest } from 'relay-runtime'; -import { FragmentRefs } from "relay-runtime"; -export type TrustGraphQuery$variables = Record; -export type TrustGraphQuery$data = { - readonly currentTrustCenter: { - readonly audits: { - readonly edges: ReadonlyArray<{ - readonly node: { - readonly id: string; - readonly " $fragmentSpreads": FragmentRefs<"AuditRowFragment">; - }; - }>; - }; - readonly hasAcceptedNonDisclosureAgreement: boolean; - readonly id: string; - readonly isUserAuthenticated: boolean; - readonly ndaFileName: string | null | undefined; - readonly ndaFileUrl: string | null | undefined; - readonly organization: { - readonly description: string | null | undefined; - readonly email: string | null | undefined; - readonly headquarterAddress: string | null | undefined; - readonly logoUrl: string | null | undefined; - readonly name: string; - readonly websiteUrl: string | null | undefined; - }; - readonly slug: string; - readonly " $fragmentSpreads": FragmentRefs<"OverviewPageFragment">; - } | null | undefined; -}; -export type TrustGraphQuery = { - response: TrustGraphQuery$data; - variables: TrustGraphQuery$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null -}, -v1 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "slug", - "storageKey": null -}, -v2 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "isUserAuthenticated", - "storageKey": null -}, -v3 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasAcceptedNonDisclosureAgreement", - "storageKey": null -}, -v4 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "ndaFileName", - "storageKey": null -}, -v5 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "ndaFileUrl", - "storageKey": null -}, -v6 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "name", - "storageKey": null -}, -v7 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "description", - "storageKey": null -}, -v8 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "websiteUrl", - "storageKey": null -}, -v9 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "logoUrl", - "storageKey": null -}, -v10 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "email", - "storageKey": null -}, -v11 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "headquarterAddress", - "storageKey": null -}, -v12 = [ - { - "kind": "Literal", - "name": "first", - "value": 50 - } -], -v13 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "category", - "storageKey": null -}, -v14 = [ - { - "kind": "Literal", - "name": "first", - "value": 5 - } -], -v15 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "isUserAuthorized", - "storageKey": null -}, -v16 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasUserRequestedAccess", - "storageKey": null -}; -return { - "fragment": { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "TrustGraphQuery", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "currentTrustCenter", - "plural": false, - "selections": [ - (v0/*: any*/), - (v1/*: any*/), - (v2/*: any*/), - (v3/*: any*/), - (v4/*: any*/), - (v5/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "Organization", - "kind": "LinkedField", - "name": "organization", - "plural": false, - "selections": [ - (v6/*: any*/), - (v7/*: any*/), - (v8/*: any*/), - (v9/*: any*/), - (v10/*: any*/), - (v11/*: any*/) - ], - "storageKey": null - }, - { - "args": null, - "kind": "FragmentSpread", - "name": "OverviewPageFragment" - }, - { - "alias": null, - "args": (v12/*: any*/), - "concreteType": "AuditConnection", - "kind": "LinkedField", - "name": "audits", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "AuditEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Audit", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "args": null, - "kind": "FragmentSpread", - "name": "AuditRowFragment" - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "audits(first:50)" - } - ], - "storageKey": null - } - ], - "type": "Query", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": [], - "kind": "Operation", - "name": "TrustGraphQuery", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "currentTrustCenter", - "plural": false, - "selections": [ - (v0/*: any*/), - (v1/*: any*/), - (v2/*: any*/), - (v3/*: any*/), - (v4/*: any*/), - (v5/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "Organization", - "kind": "LinkedField", - "name": "organization", - "plural": false, - "selections": [ - (v6/*: any*/), - (v7/*: any*/), - (v8/*: any*/), - (v9/*: any*/), - (v10/*: any*/), - (v11/*: any*/), - (v0/*: any*/) - ], - "storageKey": null - }, - { - "alias": null, - "args": [ - { - "kind": "Literal", - "name": "first", - "value": 14 - } - ], - "concreteType": "TrustCenterReferenceConnection", - "kind": "LinkedField", - "name": "references", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterReferenceEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterReference", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v6/*: any*/), - (v9/*: any*/), - (v8/*: any*/) - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "references(first:14)" - }, - { - "alias": null, - "args": [ - { - "kind": "Literal", - "name": "first", - "value": 3 - } - ], - "concreteType": "VendorConnection", - "kind": "LinkedField", - "name": "vendors", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "VendorEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Vendor", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "countries", - "storageKey": null - }, - (v6/*: any*/), - (v13/*: any*/), - (v8/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "privacyPolicyUrl", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "vendors(first:3)" - }, - { - "alias": null, - "args": (v14/*: any*/), - "concreteType": "DocumentConnection", - "kind": "LinkedField", - "name": "documents", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "DocumentEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Document", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "title", - "storageKey": null - }, - (v15/*: any*/), - (v16/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "documentType", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "documents(first:5)" - }, - { - "alias": null, - "args": (v14/*: any*/), - "concreteType": "TrustCenterFileConnection", - "kind": "LinkedField", - "name": "trustCenterFiles", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterFileEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenterFile", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v13/*: any*/), - (v6/*: any*/), - (v15/*: any*/), - (v16/*: any*/) - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "trustCenterFiles(first:5)" - }, - { - "alias": null, - "args": (v12/*: any*/), - "concreteType": "AuditConnection", - "kind": "LinkedField", - "name": "audits", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "AuditEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Audit", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "Report", - "kind": "LinkedField", - "name": "report", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "filename", - "storageKey": null - }, - (v15/*: any*/), - (v16/*: any*/) - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "Framework", - "kind": "LinkedField", - "name": "framework", - "plural": false, - "selections": [ - (v0/*: any*/), - (v6/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "lightLogoURL", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "darkLogoURL", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "audits(first:50)" - } - ], - "storageKey": null - } - ] - }, - "params": { - "cacheID": "86e2d66de7f28486d0a0b649661c2d43", - "id": null, - "metadata": {}, - "name": "TrustGraphQuery", - "operationKind": "query", - "text": "query TrustGraphQuery {\n currentTrustCenter {\n id\n slug\n isUserAuthenticated\n hasAcceptedNonDisclosureAgreement\n ndaFileName\n ndaFileUrl\n organization {\n name\n description\n websiteUrl\n logoUrl\n email\n headquarterAddress\n id\n }\n ...OverviewPageFragment\n audits(first: 50) {\n edges {\n node {\n id\n ...AuditRowFragment\n }\n }\n }\n }\n}\n\nfragment AuditRowFragment on Audit {\n report {\n id\n filename\n isUserAuthorized\n hasUserRequestedAccess\n }\n framework {\n id\n name\n lightLogoURL\n darkLogoURL\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n isUserAuthorized\n hasUserRequestedAccess\n}\n\nfragment OverviewPageFragment on TrustCenter {\n references(first: 14) {\n edges {\n node {\n id\n name\n logoUrl\n websiteUrl\n }\n }\n }\n vendors(first: 3) {\n edges {\n node {\n id\n countries\n ...VendorRowFragment\n }\n }\n }\n documents(first: 5) {\n edges {\n node {\n id\n ...DocumentRowFragment\n documentType\n }\n }\n }\n trustCenterFiles(first: 5) {\n edges {\n node {\n id\n category\n ...TrustCenterFileRowFragment\n }\n }\n }\n}\n\nfragment TrustCenterFileRowFragment on TrustCenterFile {\n id\n name\n isUserAuthorized\n hasUserRequestedAccess\n}\n\nfragment VendorRowFragment on Vendor {\n id\n name\n category\n websiteUrl\n privacyPolicyUrl\n countries\n}\n" - } -}; -})(); - -(node as any).hash = "03abd3f1a77fe3d8799cdf69d0b30fad"; - -export default node; diff --git a/apps/trust/src/queries/__generated__/TrustGraphVendorsQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphVendorsQuery.graphql.ts deleted file mode 100644 index 7a860eddd..000000000 --- a/apps/trust/src/queries/__generated__/TrustGraphVendorsQuery.graphql.ts +++ /dev/null @@ -1,244 +0,0 @@ -/** - * @generated SignedSource<<72173b053a11fc1da7712cc7167c8d31>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest } from 'relay-runtime'; -import { FragmentRefs } from "relay-runtime"; -export type CountryCode = "AD" | "AE" | "AF" | "AG" | "AI" | "AL" | "AM" | "AO" | "AQ" | "AR" | "AS" | "AT" | "AU" | "AW" | "AX" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BI" | "BJ" | "BL" | "BM" | "BN" | "BO" | "BQ" | "BR" | "BS" | "BT" | "BV" | "BW" | "BY" | "BZ" | "CA" | "CC" | "CD" | "CF" | "CG" | "CH" | "CI" | "CK" | "CL" | "CM" | "CN" | "CO" | "CR" | "CU" | "CV" | "CW" | "CX" | "CY" | "CZ" | "DE" | "DJ" | "DK" | "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "EH" | "ER" | "ES" | "ET" | "EU" | "FI" | "FJ" | "FK" | "FM" | "FO" | "FR" | "GA" | "GB" | "GD" | "GE" | "GF" | "GG" | "GH" | "GI" | "GL" | "GM" | "GN" | "GP" | "GQ" | "GR" | "GT" | "GU" | "GW" | "GY" | "HK" | "HM" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IM" | "IN" | "IO" | "IQ" | "IR" | "IS" | "IT" | "JE" | "JM" | "JO" | "JP" | "KE" | "KG" | "KH" | "KI" | "KM" | "KN" | "KP" | "KR" | "KW" | "KY" | "KZ" | "LA" | "LB" | "LC" | "LI" | "LK" | "LR" | "LS" | "LT" | "LU" | "LV" | "LY" | "MA" | "MC" | "MD" | "ME" | "MF" | "MG" | "MH" | "MK" | "ML" | "MM" | "MN" | "MO" | "MP" | "MQ" | "MR" | "MS" | "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "MZ" | "NA" | "NC" | "NE" | "NF" | "NG" | "NI" | "NL" | "NO" | "NP" | "NR" | "NU" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM" | "PN" | "PR" | "PS" | "PT" | "PW" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA" | "SB" | "SC" | "SD" | "SE" | "SG" | "SH" | "SI" | "SJ" | "SK" | "SL" | "SM" | "SN" | "SO" | "SR" | "SS" | "ST" | "SV" | "SX" | "SY" | "SZ" | "TC" | "TD" | "TF" | "TG" | "TH" | "TJ" | "TK" | "TL" | "TM" | "TN" | "TO" | "TR" | "TT" | "TV" | "TW" | "TZ" | "UA" | "UG" | "UM" | "US" | "UY" | "UZ" | "VA" | "VC" | "VE" | "VG" | "VI" | "VN" | "VU" | "WF" | "WS" | "YE" | "YT" | "ZA" | "ZM" | "ZW"; -export type TrustGraphVendorsQuery$variables = Record; -export type TrustGraphVendorsQuery$data = { - readonly currentTrustCenter: { - readonly id: string; - readonly organization: { - readonly name: string; - }; - readonly vendors: { - readonly edges: ReadonlyArray<{ - readonly node: { - readonly countries: ReadonlyArray; - readonly id: string; - readonly " $fragmentSpreads": FragmentRefs<"VendorRowFragment">; - }; - }>; - }; - } | null | undefined; -}; -export type TrustGraphVendorsQuery = { - response: TrustGraphVendorsQuery$data; - variables: TrustGraphVendorsQuery$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null -}, -v1 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "name", - "storageKey": null -}, -v2 = [ - { - "kind": "Literal", - "name": "first", - "value": 50 - } -], -v3 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "countries", - "storageKey": null -}; -return { - "fragment": { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "TrustGraphVendorsQuery", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "currentTrustCenter", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "Organization", - "kind": "LinkedField", - "name": "organization", - "plural": false, - "selections": [ - (v1/*: any*/) - ], - "storageKey": null - }, - { - "alias": null, - "args": (v2/*: any*/), - "concreteType": "VendorConnection", - "kind": "LinkedField", - "name": "vendors", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "VendorEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Vendor", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v3/*: any*/), - { - "args": null, - "kind": "FragmentSpread", - "name": "VendorRowFragment" - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "vendors(first:50)" - } - ], - "storageKey": null - } - ], - "type": "Query", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": [], - "kind": "Operation", - "name": "TrustGraphVendorsQuery", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "currentTrustCenter", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "Organization", - "kind": "LinkedField", - "name": "organization", - "plural": false, - "selections": [ - (v1/*: any*/), - (v0/*: any*/) - ], - "storageKey": null - }, - { - "alias": null, - "args": (v2/*: any*/), - "concreteType": "VendorConnection", - "kind": "LinkedField", - "name": "vendors", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "VendorEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Vendor", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - (v3/*: any*/), - (v1/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "category", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "websiteUrl", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "privacyPolicyUrl", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "vendors(first:50)" - } - ], - "storageKey": null - } - ] - }, - "params": { - "cacheID": "ab7e606f23e33445efbccc09a91e4c95", - "id": null, - "metadata": {}, - "name": "TrustGraphVendorsQuery", - "operationKind": "query", - "text": "query TrustGraphVendorsQuery {\n currentTrustCenter {\n id\n organization {\n name\n id\n }\n vendors(first: 50) {\n edges {\n node {\n id\n countries\n ...VendorRowFragment\n }\n }\n }\n }\n}\n\nfragment VendorRowFragment on Vendor {\n id\n name\n category\n websiteUrl\n privacyPolicyUrl\n countries\n}\n" - } -}; -})(); - -(node as any).hash = "c38f80f339187ccce1c0dec76e9a9bd2"; - -export default node; diff --git a/packages/emails/src/DocumentSigning.tsx b/packages/emails/src/DocumentSigning.tsx index d3d82afa4..3e2debdcd 100644 --- a/packages/emails/src/DocumentSigning.tsx +++ b/packages/emails/src/DocumentSigning.tsx @@ -4,7 +4,7 @@ import EmailLayout, { bodyText, button, buttonContainer, footerText } from './co export const DocumentSigning = () => { return ( - + You're receiving this message because your company, {'{{.OrganizationName}}'}, has shared a new compliance document that requires your review and signature. diff --git a/packages/emails/src/Invitation.tsx b/packages/emails/src/Invitation.tsx index f62737683..b10fef7b2 100644 --- a/packages/emails/src/Invitation.tsx +++ b/packages/emails/src/Invitation.tsx @@ -4,7 +4,7 @@ import EmailLayout, { bodyText, button, buttonContainer, footerText } from './co export const Invitation = () => { return ( - + You have been invited to join organization {'{{.OrganizationName}}'} on Probo. Click the button below to accept the invitation: diff --git a/packages/emails/src/TrustCenterAccess.tsx b/packages/emails/src/TrustCenterAccess.tsx index 1a1f9ec77..a60c4a01b 100644 --- a/packages/emails/src/TrustCenterAccess.tsx +++ b/packages/emails/src/TrustCenterAccess.tsx @@ -23,10 +23,6 @@ export const TrustCenterAccess = () => { Access Trust Center - - - This link will expire in {"{{.DurationInDays}}"} days. - ); }; diff --git a/packages/emails/templates/trust-center-access.txt b/packages/emails/templates/trust-center-access.txt index dfb3c1974..71ebab3d1 100644 --- a/packages/emails/templates/trust-center-access.txt +++ b/packages/emails/templates/trust-center-access.txt @@ -6,6 +6,4 @@ You have been granted access to {{.OrganizationName}}'s Trust Center! Click the {{.AccessUrl}} -This link will expire in {{.DurationInDays}} days. - Probo Inc, 490 Post St, STE 640, San Francisco, CA, 94102, US diff --git a/pkg/coredata/custom_domain.go b/pkg/coredata/custom_domain.go index 159aed5bd..735b57a26 100644 --- a/pkg/coredata/custom_domain.go +++ b/pkg/coredata/custom_domain.go @@ -308,6 +308,10 @@ LIMIT 1 customDomain, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CustomDomain]) if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return ErrResourceNotFound + } + return fmt.Errorf("cannot collect custom domain: %w", err) } diff --git a/pkg/coredata/trust_center.go b/pkg/coredata/trust_center.go index 810663a3c..e1745c543 100644 --- a/pkg/coredata/trust_center.go +++ b/pkg/coredata/trust_center.go @@ -102,6 +102,10 @@ LIMIT 1; trustCenter, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[TrustCenter]) if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return ErrResourceNotFound + } + return fmt.Errorf("cannot collect trust center: %w", err) } @@ -146,6 +150,10 @@ LIMIT 1; trustCenter, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[TrustCenter]) if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return ErrResourceNotFound + } + return fmt.Errorf("cannot collect trust center: %w", err) } @@ -186,6 +194,10 @@ LIMIT 1; trustCenter, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[TrustCenter]) if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return ErrResourceNotFound + } + return fmt.Errorf("cannot collect trust center: %w", err) } diff --git a/pkg/coredata/trust_center_file.go b/pkg/coredata/trust_center_file.go index 35b90bf43..7d669338d 100644 --- a/pkg/coredata/trust_center_file.go +++ b/pkg/coredata/trust_center_file.go @@ -290,6 +290,7 @@ func (t *TrustCenterFiles) LoadByOrganizationID( scope Scoper, organizationID gid.GID, cursor *page.Cursor[TrustCenterFileOrderField], + filter *TrustCenterFileFilter, ) error { q := ` SELECT @@ -307,12 +308,14 @@ WHERE %s AND organization_id = @organization_id AND %s + AND %s ` - q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment()) args := pgx.StrictNamedArgs{"organization_id": organizationID} maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) maps.Copy(args, cursor.SQLArguments()) rows, err := conn.Query(ctx, q, args) diff --git a/pkg/coredata/trust_center_file_filter.go b/pkg/coredata/trust_center_file_filter.go index d491072c0..de9b8066d 100644 --- a/pkg/coredata/trust_center_file_filter.go +++ b/pkg/coredata/trust_center_file_filter.go @@ -24,9 +24,9 @@ type ( } ) -type TrustCenterFileOption func(f *TrustCenterFileFilter) +type TrustCenterFileFilterOption func(f *TrustCenterFileFilter) -func NewTrustCenterFileFilter(opts ...TrustCenterFileOption) *TrustCenterFileFilter { +func NewTrustCenterFileFilter(opts ...TrustCenterFileFilterOption) *TrustCenterFileFilter { f := &TrustCenterFileFilter{} for _, opt := range opts { @@ -36,7 +36,7 @@ func NewTrustCenterFileFilter(opts ...TrustCenterFileOption) *TrustCenterFileFil return f } -func WithTrustCenterFileVisibilities(visibilities ...TrustCenterVisibility) TrustCenterFileOption { +func WithTrustCenterFileVisibilities(visibilities ...TrustCenterVisibility) TrustCenterFileFilterOption { return func(f *TrustCenterFileFilter) { f.trustCenterVisibilities = visibilities } diff --git a/pkg/iam/errors.go b/pkg/iam/errors.go index 3e553df47..06f7b1ba1 100644 --- a/pkg/iam/errors.go +++ b/pkg/iam/errors.go @@ -24,7 +24,7 @@ import ( type ErrInvalidToken struct{ message string } func NewInvalidTokenError() error { - return &ErrInvalidToken{"invalid invitation token"} + return &ErrInvalidToken{"invalid token"} } func (e ErrInvalidToken) Error() string { diff --git a/pkg/probo/trust_center_access_service.go b/pkg/probo/trust_center_access_service.go index 0a54e3476..1cb5fb527 100644 --- a/pkg/probo/trust_center_access_service.go +++ b/pkg/probo/trust_center_access_service.go @@ -28,7 +28,6 @@ import ( "go.probo.inc/probo/pkg/mail" "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/slack" - "go.probo.inc/probo/pkg/statelesstoken" "go.probo.inc/probo/pkg/validator" ) @@ -402,28 +401,14 @@ func (s TrustCenterAccessService) Delete( } func (s TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Conn, access *coredata.TrustCenterAccess) error { - accessToken, err := statelesstoken.NewToken( - s.svc.trustConfig.TokenSecret, - s.svc.trustConfig.TokenType, - s.svc.trustConfig.TokenDuration, - TrustCenterAccessData{ - TrustCenterID: access.TrustCenterID, - Email: access.Email, - }, - ) - if err != nil { - return fmt.Errorf("cannot generate access token: %w", err) - } - trustCenter := &coredata.TrustCenter{} - err = trustCenter.LoadByID(ctx, tx, s.svc.scope, access.TrustCenterID) - if err != nil { + + if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, access.TrustCenterID); err != nil { return fmt.Errorf("cannot load trust center: %w", err) } organization := &coredata.Organization{} - err = organization.LoadByID(ctx, tx, s.svc.scope, trustCenter.OrganizationID) - if err != nil { + if err := organization.LoadByID(ctx, tx, s.svc.scope, trustCenter.OrganizationID); err != nil { return fmt.Errorf("cannot load organization: %w", err) } @@ -434,7 +419,7 @@ func (s TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Con hostname := baseURLParsed.Host scheme := baseURLParsed.Scheme - path := "/trust/" + trustCenter.Slug + "/access" + path := "/trust/" + trustCenter.Slug if organization.CustomDomainID != nil { customDomain, err := s.svc.CustomDomains.GetOrganizationCustomDomain(ctx, organization.ID) @@ -448,16 +433,13 @@ func (s TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Con hostname = customDomain.Domain scheme = "https" - path = "/access" + path = "" } accessURL := url.URL{ Scheme: scheme, Host: hostname, Path: path, - RawQuery: url.Values{ - "token": []string{accessToken}, - }.Encode(), } now := time.Now() diff --git a/pkg/probo/trust_center_file_service.go b/pkg/probo/trust_center_file_service.go index b6e67f94b..586d09eb5 100644 --- a/pkg/probo/trust_center_file_service.go +++ b/pkg/probo/trust_center_file_service.go @@ -83,13 +83,14 @@ func (s TrustCenterFileService) ListForOrganizationID( ctx context.Context, organizationID gid.GID, cursor *page.Cursor[coredata.TrustCenterFileOrderField], + filter *coredata.TrustCenterFileFilter, ) (*page.Page[*coredata.TrustCenterFile, coredata.TrustCenterFileOrderField], error) { var files coredata.TrustCenterFiles err := s.svc.pg.WithConn( ctx, func(conn pg.Conn) error { - if err := files.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor); err != nil { + if err := files.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor, filter); err != nil { return fmt.Errorf("cannot load trust center files: %w", err) } diff --git a/pkg/server/api/compliancepage/context.go b/pkg/server/api/compliancepage/context.go index 746ebd155..6d06588dc 100644 --- a/pkg/server/api/compliancepage/context.go +++ b/pkg/server/api/compliancepage/context.go @@ -23,10 +23,16 @@ import ( type ctxKey struct{ name string } var ( - compliancePageKey = &ctxKey{name: "compliance_page"} + compliancePageKey = &ctxKey{name: "compliance_page"} + complianceMembershipKey = &ctxKey{name: "compliance_membership"} ) func CompliancePageFromContext(ctx context.Context) *coredata.TrustCenter { trustCenter, _ := ctx.Value(compliancePageKey).(*coredata.TrustCenter) return trustCenter } + +func ComplianceMembershipFromContext(ctx context.Context) *coredata.TrustCenterAccess { + membership, _ := ctx.Value(complianceMembershipKey).(*coredata.TrustCenterAccess) + return membership +} diff --git a/pkg/server/api/compliancepage/id_middleware.go b/pkg/server/api/compliancepage/id_middleware.go index d314c012a..68980cf02 100644 --- a/pkg/server/api/compliancepage/id_middleware.go +++ b/pkg/server/api/compliancepage/id_middleware.go @@ -16,10 +16,15 @@ package compliancepage import ( "context" + "errors" "net/http" + "github.com/99designs/gqlgen/graphql" "github.com/go-chi/chi/v5" + "github.com/vektah/gqlparser/v2/gqlerror" + "go.gearno.de/kit/httpserver" "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/server/gqlutils" "go.probo.inc/probo/pkg/trust" ) @@ -33,7 +38,25 @@ func NewIDMiddleware(trustSvc *trust.Service) func(next http.Handler) http.Handl if id, err := gid.ParseGID(value); err == nil { compliancePage, err := trustSvc.Get(ctx, id) - if err != nil || !compliancePage.Active { + if err != nil { + if errors.Is(err, trust.ErrPageNotFound) { + next.ServeHTTP(w, r) + return + } + + httpserver.RenderJSON( + w, + http.StatusInternalServerError, + &graphql.Response{ + Errors: gqlerror.List{ + gqlutils.Internal(ctx), + }, + }, + ) + return + } + + if !compliancePage.Active { next.ServeHTTP(w, r) return } @@ -43,7 +66,26 @@ func NewIDMiddleware(trustSvc *trust.Service) func(next http.Handler) http.Handl return } - if compliancePage, err := trustSvc.GetBySlug(ctx, value); err == nil && compliancePage.Active { + compliancePage, err := trustSvc.GetBySlug(ctx, value) + if err != nil { + if errors.Is(err, trust.ErrPageNotFound) { + next.ServeHTTP(w, r) + return + } + + httpserver.RenderJSON( + w, + http.StatusInternalServerError, + &graphql.Response{ + Errors: gqlerror.List{ + gqlutils.Internal(ctx), + }, + }, + ) + return + } + + if compliancePage.Active { ctx = context.WithValue(ctx, compliancePageKey, compliancePage) next.ServeHTTP(w, r.WithContext(ctx)) return diff --git a/pkg/server/api/compliancepage/membership_middleware.go b/pkg/server/api/compliancepage/membership_middleware.go new file mode 100644 index 000000000..b3632d804 --- /dev/null +++ b/pkg/server/api/compliancepage/membership_middleware.go @@ -0,0 +1,74 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package compliancepage + +import ( + "context" + "errors" + "net/http" + + "github.com/99designs/gqlgen/graphql" + "github.com/vektah/gqlparser/v2/gqlerror" + "go.gearno.de/kit/httpserver" + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/server/api/authn" + "go.probo.inc/probo/pkg/server/gqlutils" + "go.probo.inc/probo/pkg/trust" +) + +func NewMembershipMiddleware(trustSvc *trust.Service, logger *log.Logger) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + identity := authn.IdentityFromContext(r.Context()) + if identity == nil { + next.ServeHTTP(w, r) + return + } + compliancePage := CompliancePageFromContext(ctx) + + membership, err := trustSvc.GetMembershipByCompliancePageIDAndEmail(ctx, compliancePage.ID, identity.EmailAddress) + if err != nil { + if errors.Is(err, trust.ErrMembershipNotFound) { + next.ServeHTTP(w, r) + return + } + + logger.ErrorCtx(ctx, "cannot get membership by page id and email", log.Error(err)) + httpserver.RenderJSON( + w, + http.StatusInternalServerError, + &graphql.Response{ + Errors: gqlerror.List{ + gqlutils.Internal(ctx), + }, + }, + ) + return + } + + if membership.Active { + ctx = context.WithValue(ctx, complianceMembershipKey, membership) + next.ServeHTTP(w, r.WithContext(ctx)) + return + } + + next.ServeHTTP(w, r) + }, + ) + } +} diff --git a/pkg/server/api/compliancepage/sni_middleware.go b/pkg/server/api/compliancepage/sni_middleware.go index 666fc49ba..737264d73 100644 --- a/pkg/server/api/compliancepage/sni_middleware.go +++ b/pkg/server/api/compliancepage/sni_middleware.go @@ -16,8 +16,13 @@ package compliancepage import ( "context" + "errors" "net/http" + "github.com/99designs/gqlgen/graphql" + "github.com/vektah/gqlparser/v2/gqlerror" + "go.gearno.de/kit/httpserver" + "go.probo.inc/probo/pkg/server/gqlutils" "go.probo.inc/probo/pkg/trust" ) @@ -32,13 +37,31 @@ func NewSNIMiddleware(trustSvc *trust.Service) func(next http.Handler) http.Hand } compliancePage, err := trustSvc.GetByDomainName(ctx, r.TLS.ServerName) - if err != nil || !compliancePage.Active { - next.ServeHTTP(w, r) + if err != nil { + if errors.Is(err, trust.ErrPageNotFound) { + next.ServeHTTP(w, r) + return + } + + httpserver.RenderJSON( + w, + http.StatusInternalServerError, + &graphql.Response{ + Errors: gqlerror.List{ + gqlutils.Internal(ctx), + }, + }, + ) return } - ctx = context.WithValue(ctx, compliancePageKey, compliancePage) - next.ServeHTTP(w, r.WithContext(ctx)) + if compliancePage.Active { + ctx = context.WithValue(ctx, compliancePageKey, compliancePage) + next.ServeHTTP(w, r.WithContext(ctx)) + return + } + + next.ServeHTTP(w, r) }) } } diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 43514f358..61c04c751 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -6169,7 +6169,7 @@ func (r *organizationResolver) TrustCenterFiles(ctx context.Context, obj *types. cursor := types.NewCursor(first, after, last, before, pageOrderBy) - pageResult, err := prb.TrustCenterFiles.ListForOrganizationID(ctx, obj.ID, cursor) + pageResult, err := prb.TrustCenterFiles.ListForOrganizationID(ctx, obj.ID, cursor, &coredata.TrustCenterFileFilter{}) if err != nil { // TODO no panic use gqlutils.InternalError panic(fmt.Errorf("cannot list organization trust center files: %w", err)) diff --git a/pkg/server/api/trust/v1/graphql_handler.go b/pkg/server/api/trust/v1/graphql_handler.go new file mode 100644 index 000000000..40051245c --- /dev/null +++ b/pkg/server/api/trust/v1/graphql_handler.go @@ -0,0 +1,63 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package trust_v1 + +import ( + "context" + "net/http" + + "github.com/99designs/gqlgen/graphql" + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/iam" + "go.probo.inc/probo/pkg/securecookie" + "go.probo.inc/probo/pkg/server/api/authn" + "go.probo.inc/probo/pkg/server/api/compliancepage" + "go.probo.inc/probo/pkg/server/api/trust/v1/schema" + "go.probo.inc/probo/pkg/server/gqlutils" + "go.probo.inc/probo/pkg/server/gqlutils/directives/session" + "go.probo.inc/probo/pkg/trust" +) + +func MembersOnlyDirective(ctx context.Context, obj any, next graphql.Resolver) (any, error) { + membership := compliancepage.ComplianceMembershipFromContext(ctx) + + if membership == nil { + return nil, gqlutils.Forbiddenf(ctx, "insufficient permission to access this resource") + } + + return next(ctx) +} + +func NewGraphQLHandler(iamSvc *iam.Service, trustSvc *trust.Service, logger *log.Logger, baseURL *baseurl.BaseURL, cookieConfig securecookie.Config) http.Handler { + config := schema.Config{ + Resolvers: &Resolver{ + iam: iamSvc, + trust: trustSvc, + logger: logger, + baseURL: baseURL, + sessionCookie: authn.NewCookie(&cookieConfig), + }, + Directives: schema.DirectiveRoot{ + Session: session.Directive, + MembersOnly: MembersOnlyDirective, + }, + } + + es := schema.NewExecutableSchema(config) + gqlh := gqlutils.NewHandler(es, logger) + + return gqlh +} diff --git a/pkg/server/api/trust/v1/resolver.go b/pkg/server/api/trust/v1/resolver.go index 86f31d0db..f6214eec2 100644 --- a/pkg/server/api/trust/v1/resolver.go +++ b/pkg/server/api/trust/v1/resolver.go @@ -28,9 +28,6 @@ import ( "go.probo.inc/probo/pkg/securecookie" "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/compliancepage" - "go.probo.inc/probo/pkg/server/api/trust/v1/schema" - "go.probo.inc/probo/pkg/server/gqlutils" - "go.probo.inc/probo/pkg/server/gqlutils/directives/session" "go.probo.inc/probo/pkg/trust" ) @@ -85,21 +82,9 @@ func NewMux( r.Use(compliancepage.NewCompliancePagePresenceMiddleware()) r.Use(authn.NewSessionMiddleware(iamSvc, cookieConfig)) + r.Use(compliancepage.NewMembershipMiddleware(trustSvc, logger)) - config := schema.Config{ - Resolvers: &Resolver{ - iam: iamSvc, - trust: trustSvc, - logger: logger, - sessionCookie: authn.NewCookie(&cookieConfig), - baseURL: baseURL, - }, - Directives: schema.DirectiveRoot{ - Session: session.Directive, - }, - } - es := schema.NewExecutableSchema(config) - graphqlHandler := gqlutils.NewHandler(es, logger) + graphqlHandler := NewGraphQLHandler(iamSvc, trustSvc, logger, baseURL, cookieConfig) r.Handle("/graphql", graphqlHandler) diff --git a/pkg/server/api/trust/v1/schema.graphql b/pkg/server/api/trust/v1/schema.graphql index b664e2649..8680e8f32 100644 --- a/pkg/server/api/trust/v1/schema.graphql +++ b/pkg/server/api/trust/v1/schema.graphql @@ -12,6 +12,8 @@ directive @goModel( directive @goEnum(value: String) on ENUM_VALUE +directive @membersOnly on FIELD_DEFINITION + enum Role { NONE USER @@ -495,7 +497,7 @@ type TrustCenter implements Node { ndaFileName: String ndaFileUrl: String @goField(forceResolver: true) organization: Organization! @goField(forceResolver: true) - isUserAuthenticated: Boolean! @goField(forceResolver: true) + isViewerMember: Boolean! @goField(forceResolver: true) hasAcceptedNonDisclosureAgreement: Boolean! @goField(forceResolver: true) documents( @@ -610,30 +612,37 @@ type Query { type Mutation { sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload + @session(required: OPTIONAL) verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload + @session(required: OPTIONAL) - requestAllAccesses: RequestAccessesPayload! + requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) exportDocumentPDF(input: ExportDocumentPDFInput!): ExportDocumentPDFPayload! - @session(required: NONE) + @session(required: PRESENT) + @membersOnly exportReportPDF(input: ExportReportPDFInput!): ExportReportPDFPayload! - @session(required: NONE) + @session(required: PRESENT) + @membersOnly acceptNonDisclosureAgreement: AcceptNonDisclosureAgreementPayload! @session(required: PRESENT) + @membersOnly requestDocumentAccess( input: RequestDocumentAccessInput! - ): RequestAccessesPayload! + ): RequestAccessesPayload! @session(required: PRESENT) - requestReportAccess(input: RequestReportAccessInput!): RequestAccessesPayload! + requestReportAccess( + input: RequestReportAccessInput! + ): RequestAccessesPayload! @session(required: PRESENT) requestTrustCenterFileAccess( input: RequestTrustCenterFileAccessInput! - ): RequestAccessesPayload! + ): RequestAccessesPayload! @session(required: PRESENT) exportTrustCenterFile( input: ExportTrustCenterFileInput! - ): ExportTrustCenterFilePayload! + ): ExportTrustCenterFilePayload! @session(required: PRESENT) @membersOnly } diff --git a/pkg/server/api/trust/v1/schema/schema.go b/pkg/server/api/trust/v1/schema/schema.go index f1ae423a1..139c18036 100644 --- a/pkg/server/api/trust/v1/schema/schema.go +++ b/pkg/server/api/trust/v1/schema/schema.go @@ -60,7 +60,8 @@ type ResolverRoot interface { } type DirectiveRoot struct { - Session func(ctx context.Context, obj any, next graphql.Resolver, required session.SessionRequirement) (res any, err error) + MembersOnly func(ctx context.Context, obj any, next graphql.Resolver) (res any, err error) + Session func(ctx context.Context, obj any, next graphql.Resolver, required session.SessionRequirement) (res any, err error) } type ComplexityRoot struct { @@ -187,7 +188,7 @@ type ComplexityRoot struct { Documents func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int HasAcceptedNonDisclosureAgreement func(childComplexity int) int ID func(childComplexity int) int - IsUserAuthenticated func(childComplexity int) int + IsViewerMember func(childComplexity int) int NdaFileName func(childComplexity int) int NdaFileURL func(childComplexity int) int Organization func(childComplexity int) int @@ -304,7 +305,7 @@ type ReportResolver interface { type TrustCenterResolver interface { NdaFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) Organization(ctx context.Context, obj *types.TrustCenter) (*types.Organization, error) - IsUserAuthenticated(ctx context.Context, obj *types.TrustCenter) (bool, error) + IsViewerMember(ctx context.Context, obj *types.TrustCenter) (bool, error) HasAcceptedNonDisclosureAgreement(ctx context.Context, obj *types.TrustCenter) (bool, error) Documents(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.DocumentConnection, error) Audits(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.AuditConnection, error) @@ -803,12 +804,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.TrustCenter.ID(childComplexity), true - case "TrustCenter.isUserAuthenticated": - if e.complexity.TrustCenter.IsUserAuthenticated == nil { + case "TrustCenter.isViewerMember": + if e.complexity.TrustCenter.IsViewerMember == nil { break } - return e.complexity.TrustCenter.IsUserAuthenticated(childComplexity), true + return e.complexity.TrustCenter.IsViewerMember(childComplexity), true case "TrustCenter.ndaFileName": if e.complexity.TrustCenter.NdaFileName == nil { break @@ -1209,6 +1210,8 @@ directive @goModel( directive @goEnum(value: String) on ENUM_VALUE +directive @membersOnly on FIELD_DEFINITION + enum Role { NONE USER @@ -1692,7 +1695,7 @@ type TrustCenter implements Node { ndaFileName: String ndaFileUrl: String @goField(forceResolver: true) organization: Organization! @goField(forceResolver: true) - isUserAuthenticated: Boolean! @goField(forceResolver: true) + isViewerMember: Boolean! @goField(forceResolver: true) hasAcceptedNonDisclosureAgreement: Boolean! @goField(forceResolver: true) documents( @@ -1807,32 +1810,39 @@ type Query { type Mutation { sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload + @session(required: OPTIONAL) verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload + @session(required: OPTIONAL) - requestAllAccesses: RequestAccessesPayload! + requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) exportDocumentPDF(input: ExportDocumentPDFInput!): ExportDocumentPDFPayload! - @session(required: NONE) + @session(required: PRESENT) + @membersOnly exportReportPDF(input: ExportReportPDFInput!): ExportReportPDFPayload! - @session(required: NONE) + @session(required: PRESENT) + @membersOnly acceptNonDisclosureAgreement: AcceptNonDisclosureAgreementPayload! @session(required: PRESENT) + @membersOnly requestDocumentAccess( input: RequestDocumentAccessInput! - ): RequestAccessesPayload! + ): RequestAccessesPayload! @session(required: PRESENT) - requestReportAccess(input: RequestReportAccessInput!): RequestAccessesPayload! + requestReportAccess( + input: RequestReportAccessInput! + ): RequestAccessesPayload! @session(required: PRESENT) requestTrustCenterFileAccess( input: RequestTrustCenterFileAccessInput! - ): RequestAccessesPayload! + ): RequestAccessesPayload! @session(required: PRESENT) exportTrustCenterFile( input: ExportTrustCenterFileInput! - ): ExportTrustCenterFilePayload! + ): ExportTrustCenterFilePayload! @session(required: PRESENT) @membersOnly } `, BuiltIn: false}, {Name: "../../../../gqlutils/directives/session/schema.graphql", Input: `# Session directive for GraphQL APIs @@ -3136,7 +3146,25 @@ func (ec *executionContext) _Mutation_sendMagicLink(ctx context.Context, field g fc := graphql.GetFieldContext(ctx) return ec.resolvers.Mutation().SendMagicLink(ctx, fc.Args["input"].(types.SendMagicLinkInput)) }, - nil, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "OPTIONAL") + if err != nil { + var zeroVal *types.SendMagicLinkPayload + return zeroVal, err + } + if ec.directives.Session == nil { + var zeroVal *types.SendMagicLinkPayload + return zeroVal, errors.New("directive session is not implemented") + } + return ec.directives.Session(ctx, nil, directive0, required) + } + + next = directive1 + return next + }, ec.marshalOSendMagicLinkPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSendMagicLinkPayload, true, false, @@ -3181,7 +3209,25 @@ func (ec *executionContext) _Mutation_verifyMagicLink(ctx context.Context, field fc := graphql.GetFieldContext(ctx) return ec.resolvers.Mutation().VerifyMagicLink(ctx, fc.Args["input"].(types.VerifyMagicLinkInput)) }, - nil, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "OPTIONAL") + if err != nil { + var zeroVal *types.VerifyMagicLinkPayload + return zeroVal, err + } + if ec.directives.Session == nil { + var zeroVal *types.VerifyMagicLinkPayload + return zeroVal, errors.New("directive session is not implemented") + } + return ec.directives.Session(ctx, nil, directive0, required) + } + + next = directive1 + return next + }, ec.marshalOVerifyMagicLinkPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐVerifyMagicLinkPayload, true, false, @@ -3225,7 +3271,25 @@ func (ec *executionContext) _Mutation_requestAllAccesses(ctx context.Context, fi func(ctx context.Context) (any, error) { return ec.resolvers.Mutation().RequestAllAccesses(ctx) }, - nil, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT") + if err != nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, err + } + if ec.directives.Session == nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, errors.New("directive session is not implemented") + } + return ec.directives.Session(ctx, nil, directive0, required) + } + + next = directive1 + return next + }, ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload, true, true, @@ -3263,7 +3327,7 @@ func (ec *executionContext) _Mutation_exportDocumentPDF(ctx context.Context, fie directive0 := next directive1 := func(ctx context.Context) (any, error) { - required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE") + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT") if err != nil { var zeroVal *types.ExportDocumentPDFPayload return zeroVal, err @@ -3274,8 +3338,15 @@ func (ec *executionContext) _Mutation_exportDocumentPDF(ctx context.Context, fie } return ec.directives.Session(ctx, nil, directive0, required) } + directive2 := func(ctx context.Context) (any, error) { + if ec.directives.MembersOnly == nil { + var zeroVal *types.ExportDocumentPDFPayload + return zeroVal, errors.New("directive membersOnly is not implemented") + } + return ec.directives.MembersOnly(ctx, nil, directive1) + } - next = directive1 + next = directive2 return next }, ec.marshalNExportDocumentPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐExportDocumentPDFPayload, @@ -3326,7 +3397,7 @@ func (ec *executionContext) _Mutation_exportReportPDF(ctx context.Context, field directive0 := next directive1 := func(ctx context.Context) (any, error) { - required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE") + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT") if err != nil { var zeroVal *types.ExportReportPDFPayload return zeroVal, err @@ -3337,8 +3408,15 @@ func (ec *executionContext) _Mutation_exportReportPDF(ctx context.Context, field } return ec.directives.Session(ctx, nil, directive0, required) } + directive2 := func(ctx context.Context) (any, error) { + if ec.directives.MembersOnly == nil { + var zeroVal *types.ExportReportPDFPayload + return zeroVal, errors.New("directive membersOnly is not implemented") + } + return ec.directives.MembersOnly(ctx, nil, directive1) + } - next = directive1 + next = directive2 return next }, ec.marshalNExportReportPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐExportReportPDFPayload, @@ -3399,8 +3477,15 @@ func (ec *executionContext) _Mutation_acceptNonDisclosureAgreement(ctx context.C } return ec.directives.Session(ctx, nil, directive0, required) } + directive2 := func(ctx context.Context) (any, error) { + if ec.directives.MembersOnly == nil { + var zeroVal *types.AcceptNonDisclosureAgreementPayload + return zeroVal, errors.New("directive membersOnly is not implemented") + } + return ec.directives.MembersOnly(ctx, nil, directive1) + } - next = directive1 + next = directive2 return next }, ec.marshalNAcceptNonDisclosureAgreementPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐAcceptNonDisclosureAgreementPayload, @@ -3436,7 +3521,25 @@ func (ec *executionContext) _Mutation_requestDocumentAccess(ctx context.Context, fc := graphql.GetFieldContext(ctx) return ec.resolvers.Mutation().RequestDocumentAccess(ctx, fc.Args["input"].(types.RequestDocumentAccessInput)) }, - nil, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT") + if err != nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, err + } + if ec.directives.Session == nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, errors.New("directive session is not implemented") + } + return ec.directives.Session(ctx, nil, directive0, required) + } + + next = directive1 + return next + }, ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload, true, true, @@ -3481,7 +3584,25 @@ func (ec *executionContext) _Mutation_requestReportAccess(ctx context.Context, f fc := graphql.GetFieldContext(ctx) return ec.resolvers.Mutation().RequestReportAccess(ctx, fc.Args["input"].(types.RequestReportAccessInput)) }, - nil, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT") + if err != nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, err + } + if ec.directives.Session == nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, errors.New("directive session is not implemented") + } + return ec.directives.Session(ctx, nil, directive0, required) + } + + next = directive1 + return next + }, ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload, true, true, @@ -3526,7 +3647,25 @@ func (ec *executionContext) _Mutation_requestTrustCenterFileAccess(ctx context.C fc := graphql.GetFieldContext(ctx) return ec.resolvers.Mutation().RequestTrustCenterFileAccess(ctx, fc.Args["input"].(types.RequestTrustCenterFileAccessInput)) }, - nil, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT") + if err != nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, err + } + if ec.directives.Session == nil { + var zeroVal *types.RequestAccessesPayload + return zeroVal, errors.New("directive session is not implemented") + } + return ec.directives.Session(ctx, nil, directive0, required) + } + + next = directive1 + return next + }, ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload, true, true, @@ -3571,7 +3710,32 @@ func (ec *executionContext) _Mutation_exportTrustCenterFile(ctx context.Context, fc := graphql.GetFieldContext(ctx) return ec.resolvers.Mutation().ExportTrustCenterFile(ctx, fc.Args["input"].(types.ExportTrustCenterFileInput)) }, - nil, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT") + if err != nil { + var zeroVal *types.ExportTrustCenterFilePayload + return zeroVal, err + } + if ec.directives.Session == nil { + var zeroVal *types.ExportTrustCenterFilePayload + return zeroVal, errors.New("directive session is not implemented") + } + return ec.directives.Session(ctx, nil, directive0, required) + } + directive2 := func(ctx context.Context) (any, error) { + if ec.directives.MembersOnly == nil { + var zeroVal *types.ExportTrustCenterFilePayload + return zeroVal, errors.New("directive membersOnly is not implemented") + } + return ec.directives.MembersOnly(ctx, nil, directive1) + } + + next = directive2 + return next + }, ec.marshalNExportTrustCenterFilePayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐExportTrustCenterFilePayload, true, true, @@ -4045,8 +4209,8 @@ func (ec *executionContext) fieldContext_Query_currentTrustCenter(_ context.Cont return ec.fieldContext_TrustCenter_ndaFileUrl(ctx, field) case "organization": return ec.fieldContext_TrustCenter_organization(ctx, field) - case "isUserAuthenticated": - return ec.fieldContext_TrustCenter_isUserAuthenticated(ctx, field) + case "isViewerMember": + return ec.fieldContext_TrustCenter_isViewerMember(ctx, field) case "hasAcceptedNonDisclosureAgreement": return ec.fieldContext_TrustCenter_hasAcceptedNonDisclosureAgreement(ctx, field) case "documents": @@ -4550,14 +4714,14 @@ func (ec *executionContext) fieldContext_TrustCenter_organization(_ context.Cont return fc, nil } -func (ec *executionContext) _TrustCenter_isUserAuthenticated(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) { +func (ec *executionContext) _TrustCenter_isViewerMember(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_TrustCenter_isUserAuthenticated, + ec.fieldContext_TrustCenter_isViewerMember, func(ctx context.Context) (any, error) { - return ec.resolvers.TrustCenter().IsUserAuthenticated(ctx, obj) + return ec.resolvers.TrustCenter().IsViewerMember(ctx, obj) }, nil, ec.marshalNBoolean2bool, @@ -4566,7 +4730,7 @@ func (ec *executionContext) _TrustCenter_isUserAuthenticated(ctx context.Context ) } -func (ec *executionContext) fieldContext_TrustCenter_isUserAuthenticated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrustCenter_isViewerMember(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "TrustCenter", Field: field, @@ -9046,7 +9210,7 @@ func (ec *executionContext) _TrustCenter(ctx context.Context, sel ast.SelectionS } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "isUserAuthenticated": + case "isViewerMember": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -9055,7 +9219,7 @@ func (ec *executionContext) _TrustCenter(ctx context.Context, sel ast.SelectionS ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._TrustCenter_isUserAuthenticated(ctx, field, obj) + res = ec._TrustCenter_isViewerMember(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } diff --git a/pkg/server/api/trust/v1/types/types.go b/pkg/server/api/trust/v1/types/types.go index 47932391c..2d1de4ec0 100644 --- a/pkg/server/api/trust/v1/types/types.go +++ b/pkg/server/api/trust/v1/types/types.go @@ -177,7 +177,7 @@ type TrustCenter struct { NdaFileName *string `json:"ndaFileName,omitempty"` NdaFileURL *string `json:"ndaFileUrl,omitempty"` Organization *Organization `json:"organization"` - IsUserAuthenticated bool `json:"isUserAuthenticated"` + IsViewerMember bool `json:"isViewerMember"` HasAcceptedNonDisclosureAgreement bool `json:"hasAcceptedNonDisclosureAgreement"` Documents *DocumentConnection `json:"documents"` Audits *AuditConnection `json:"audits"` diff --git a/pkg/server/api/trust/v1/v1_resolver.go b/pkg/server/api/trust/v1/v1_resolver.go index 7cdf2db37..85cd98028 100644 --- a/pkg/server/api/trust/v1/v1_resolver.go +++ b/pkg/server/api/trust/v1/v1_resolver.go @@ -166,11 +166,12 @@ func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMa customDomain, err := r.trust.GetCustomDomainByOrganizationID(ctx, organization.ID) if err != nil { - var errNotFound *iam.ErrOrganizationNotFound - if !errors.As(err, &errNotFound) { - r.logger.ErrorCtx(ctx, "cannot get custom domain", log.Error(err)) - return nil, gqlutils.Internal(ctx) + if errors.Is(err, trust.ErrCustomDomainNotFound) { + return nil, gqlutils.NotFoundf(ctx, "custom domain not found") } + + r.logger.ErrorCtx(ctx, "cannot get custom domain", log.Error(err)) + return nil, gqlutils.Internal(ctx) } req := &iam.SendMagicLinkRequest{ @@ -874,11 +875,11 @@ func (r *trustCenterResolver) Organization(ctx context.Context, obj *types.Trust return obj.Organization, nil } -// IsUserAuthenticated is the resolver for the isUserAuthenticated field. -func (r *trustCenterResolver) IsUserAuthenticated(ctx context.Context, obj *types.TrustCenter) (bool, error) { - identity := authn.IdentityFromContext(ctx) +// IsViewerMember is the resolver for the isViewerMember field. +func (r *trustCenterResolver) IsViewerMember(ctx context.Context, obj *types.TrustCenter) (bool, error) { + membership := compliancepage.ComplianceMembershipFromContext(ctx) - return identity != nil, nil + return membership != nil, nil } // HasAcceptedNonDisclosureAgreement is the resolver for the hasAcceptedNonDisclosureAgreement field. @@ -985,7 +986,13 @@ func (r *trustCenterResolver) TrustCenterFiles(ctx context.Context, obj *types.T } cursor := types.NewCursor(first, after, last, before, pageOrderBy) - trustCenterFilePage, err := trustService.TrustCenterFiles.ListForOrganizationId(ctx, obj.Organization.ID, cursor) + filter := coredata.NewTrustCenterFileFilter( + coredata.WithTrustCenterFileVisibilities( + coredata.TrustCenterVisibilityPublic, + coredata.TrustCenterVisibilityPrivate, + ), + ) + trustCenterFilePage, err := trustService.TrustCenterFiles.ListForOrganizationId(ctx, obj.Organization.ID, cursor, filter) if err != nil { r.logger.ErrorCtx(ctx, "cannot list public trust center files", log.Error(err)) return nil, gqlutils.Internal(ctx) diff --git a/pkg/trust/errors.go b/pkg/trust/errors.go new file mode 100644 index 000000000..b88a0b135 --- /dev/null +++ b/pkg/trust/errors.go @@ -0,0 +1,23 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package trust + +import "errors" + +var ( + ErrCustomDomainNotFound = errors.New("custom domain not found") + ErrPageNotFound = errors.New("page not found") + ErrMembershipNotFound = errors.New("membership not found") +) diff --git a/pkg/trust/service.go b/pkg/trust/service.go index 9f5ba52b5..e7679ec99 100644 --- a/pkg/trust/service.go +++ b/pkg/trust/service.go @@ -16,6 +16,7 @@ package trust import ( "context" + "errors" "fmt" "time" @@ -28,6 +29,7 @@ import ( "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/iam" + "go.probo.inc/probo/pkg/mail" "go.probo.inc/probo/pkg/probo" "go.probo.inc/probo/pkg/slack" ) @@ -159,6 +161,9 @@ func (s *Service) Get( func(conn pg.Conn) error { err := trustCenter.LoadByID(ctx, conn, coredata.NewNoScope(), id) if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrPageNotFound + } return fmt.Errorf("cannot load trust center: %w", err) } @@ -184,6 +189,9 @@ func (s *Service) GetBySlug( func(conn pg.Conn) error { err := trustCenter.LoadBySlug(ctx, conn, slug) if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrPageNotFound + } return fmt.Errorf("cannot load trust center: %w", err) } @@ -206,16 +214,28 @@ func (s *Service) GetByDomainName(ctx context.Context, domain string) (*coredata func(conn pg.Conn) error { var customDomain coredata.CustomDomain if err := customDomain.LoadByDomain(ctx, conn, coredata.NewNoScope(), s.encryptionKey, domain); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrPageNotFound + } + return fmt.Errorf("cannot load custom domain: %w", err) } var org coredata.Organization if err := org.LoadByCustomDomainID(ctx, conn, coredata.NewNoScope(), customDomain.ID); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrPageNotFound + } + return fmt.Errorf("cannot load organization: %w", err) } trustCenter = &coredata.TrustCenter{} if err := trustCenter.LoadByOrganizationID(ctx, conn, coredata.NewNoScope(), org.ID); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrPageNotFound + } + return fmt.Errorf("cannot load trust center: %w", err) } @@ -239,6 +259,39 @@ func (s *Service) GetCustomDomainByOrganizationID(ctx context.Context, organizat return customDomain.LoadByOrganizationID(ctx, conn, coredata.NewNoScope(), s.encryptionKey, organizationID) }, ) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, ErrCustomDomainNotFound + } + + return nil, err + } return customDomain, err } + +func (s *Service) GetMembershipByCompliancePageIDAndEmail(ctx context.Context, compliancePageID gid.GID, email mail.Addr) (*coredata.TrustCenterAccess, error) { + membership := &coredata.TrustCenterAccess{} + + err := s.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return membership.LoadByTrustCenterIDAndEmail( + ctx, + conn, + coredata.NewScopeFromObjectID(compliancePageID), + compliancePageID, + email, + ) + }, + ) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, ErrMembershipNotFound + } + + return nil, err + } + + return membership, nil +} diff --git a/pkg/trust/trust_center_access_service.go b/pkg/trust/trust_center_access_service.go index 096af00bc..59419a98e 100644 --- a/pkg/trust/trust_center_access_service.go +++ b/pkg/trust/trust_center_access_service.go @@ -29,8 +29,6 @@ import ( "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/mail" - "go.probo.inc/probo/pkg/probo" - "go.probo.inc/probo/pkg/statelesstoken" "go.probo.inc/probo/pkg/validator" ) @@ -445,28 +443,14 @@ func (s *TrustCenterAccessService) GrantByIDs( } func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Conn, access *coredata.TrustCenterAccess) error { - accessToken, err := statelesstoken.NewToken( - s.svc.trustConfig.TokenSecret, - s.svc.trustConfig.TokenType, - s.svc.trustConfig.TokenDuration, - probo.TrustCenterAccessData{ - TrustCenterID: access.TrustCenterID, - Email: access.Email, - }, - ) - if err != nil { - return fmt.Errorf("cannot generate access token: %w", err) - } - trustCenter := &coredata.TrustCenter{} - err = trustCenter.LoadByID(ctx, tx, s.svc.scope, access.TrustCenterID) - if err != nil { + + if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, access.TrustCenterID); err != nil { return fmt.Errorf("cannot load trust center: %w", err) } organization := &coredata.Organization{} - err = organization.LoadByID(ctx, tx, s.svc.scope, trustCenter.OrganizationID) - if err != nil { + if err := organization.LoadByID(ctx, tx, s.svc.scope, trustCenter.OrganizationID); err != nil { return fmt.Errorf("cannot load organization: %w", err) } @@ -477,7 +461,7 @@ func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Co hostname := baseURLParsed.Host scheme := baseURLParsed.Scheme - path := "/trust/" + trustCenter.Slug + "/access" + path := "/trust/" + trustCenter.Slug if organization.CustomDomainID != nil { customDomain, err := s.svc.Organizations.GetOrganizationCustomDomain(ctx, organization.ID) @@ -491,16 +475,13 @@ func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Co hostname = customDomain.Domain scheme = "https" - path = "/access" + path = "" } accessURL := url.URL{ Scheme: scheme, Host: hostname, Path: path, - RawQuery: url.Values{ - "token": []string{accessToken}, - }.Encode(), } now := time.Now() diff --git a/pkg/trust/trust_center_file_service.go b/pkg/trust/trust_center_file_service.go index f6d47c940..7c62414c1 100644 --- a/pkg/trust/trust_center_file_service.go +++ b/pkg/trust/trust_center_file_service.go @@ -62,13 +62,14 @@ func (s *TrustCenterFileService) ListForOrganizationId( ctx context.Context, organizationID gid.GID, cursor *page.Cursor[coredata.TrustCenterFileOrderField], + filter *coredata.TrustCenterFileFilter, ) (*page.Page[*coredata.TrustCenterFile, coredata.TrustCenterFileOrderField], error) { var trustCenterFiles coredata.TrustCenterFiles err := s.svc.pg.WithConn( ctx, func(conn pg.Conn) error { - err := trustCenterFiles.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor) + err := trustCenterFiles.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor, filter) if err != nil { return fmt.Errorf("cannot load trust center files: %w", err) }