From 7322201dab85bd15452e1eabb17e20d58938a7a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89mile=20R=C3=A9?=
Date: Fri, 9 Jan 2026 19:14:17 +0100
Subject: [PATCH] Plug trust center part 1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Émile Ré
---
.../src/components/OrganizationSidebar.tsx | 6 +-
.../src/components/RequestAccessDialog.tsx | 46 +-
...estAccessDialogDocumentMutation.graphql.ts | 6 +-
.../RequestAccessDialogMutation.graphql.ts | 6 +-
...questAccessDialogReportMutation.graphql.ts | 6 +-
...ssDialogTrustCenterFileMutation.graphql.ts | 6 +-
apps/trust/src/hooks/useIsAuthenticated.ts | 6 -
apps/trust/src/layouts/MainLayout.tsx | 14 +-
apps/trust/src/providers/Viewer.tsx | 11 +
apps/trust/src/queries/TrustGraph.ts | 4 +
.../TrustGraphCurrentQuery.graphql.ts | 148 +-
apps/trust/src/routes.tsx | 10 +-
apps/trust/vite.config.ts | 6 +-
packages/emails/emails.go | 17 +
packages/emails/scripts/build.ts | 68 +-
packages/emails/src/MagicLink.tsx | 28 +
packages/emails/src/TrustCenterAccess.tsx | 23 +-
packages/emails/templates/magic-link.txt | 11 +
packages/prettier/prettier.config.js | 2 +-
pkg/iam/auth_service.go | 164 ++
pkg/iam/service.go | 3 +
pkg/mail/addr.go | 13 +
pkg/probo/service.go | 22 +
pkg/probo/trust_center_access_service.go | 6 +-
pkg/probod/auth_config.go | 1 +
pkg/probod/probod.go | 2 +
pkg/server/api/authn/context.go | 1 +
pkg/server/api/connect/v1/v1_resolver.go | 6 +-
pkg/server/api/console/v1/graphql_handler.go | 1 +
pkg/server/api/console/v1/resolver.go | 8 +-
pkg/server/api/console/v1/types/audit.go | 10 +-
pkg/server/api/console/v1/v1_resolver.go | 25 +-
pkg/server/api/trust/v1/resolver.go | 53 +-
pkg/server/api/trust/v1/schema.graphql | 36 +-
pkg/server/api/trust/v1/schema/schema.go | 662 ++++++++-
.../trust/v1/trust_center_access_handler.go | 139 --
pkg/server/api/trust/v1/types/types.go | 50 +-
pkg/server/api/trust/v1/v1_resolver.go | 1315 +++++++++--------
pkg/server/gqlutils/errors.go | 8 +
pkg/server/server.go | 19 +-
pkg/trust/trust_center_access_service.go | 24 +-
41 files changed, 1959 insertions(+), 1033 deletions(-)
delete mode 100644 apps/trust/src/hooks/useIsAuthenticated.ts
create mode 100644 apps/trust/src/providers/Viewer.tsx
create mode 100644 packages/emails/src/MagicLink.tsx
create mode 100644 packages/emails/templates/magic-link.txt
delete mode 100644 pkg/server/api/trust/v1/trust_center_access_handler.go
diff --git a/apps/trust/src/components/OrganizationSidebar.tsx b/apps/trust/src/components/OrganizationSidebar.tsx
index ca8bfa377..8060db6c3 100644
--- a/apps/trust/src/components/OrganizationSidebar.tsx
+++ b/apps/trust/src/components/OrganizationSidebar.tsx
@@ -1,11 +1,11 @@
import { useTranslate } from "@probo/i18n";
import { Button, Card, IconBlock, IconLock, IconMedal } from "@probo/ui";
import type { TrustGraphQuery$data } from "/queries/__generated__/TrustGraphQuery.graphql";
-import type { PropsWithChildren } from "react";
+import { use, type PropsWithChildren } from "react";
import { domain } from "@probo/helpers";
import { AuditRowAvatar } from "./AuditRow";
import { RequestAccessDialog } from "./RequestAccessDialog";
-import { useIsAuthenticated } from "/hooks/useIsAuthenticated";
+import { Viewer } from "/providers/Viewer";
export function OrganizationSidebar({
trustCenter,
@@ -13,7 +13,7 @@ export function OrganizationSidebar({
trustCenter: TrustGraphQuery$data["trustCenterBySlug"];
}) {
const { __ } = useTranslate();
- const isAuthenticated = useIsAuthenticated();
+ const isAuthenticated = !!use(Viewer);
if (!trustCenter) {
return null;
diff --git a/apps/trust/src/components/RequestAccessDialog.tsx b/apps/trust/src/components/RequestAccessDialog.tsx
index 6597c9cad..84eb1360f 100644
--- a/apps/trust/src/components/RequestAccessDialog.tsx
+++ b/apps/trust/src/components/RequestAccessDialog.tsx
@@ -15,9 +15,9 @@ import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { graphql } from "relay-runtime";
import { useMutationWithToasts } from "/hooks/useMutationWithToast";
import { useTrustCenter } from "/hooks/useTrustCenter";
-import { type FormEventHandler, type PropsWithChildren } from "react";
-import { useIsAuthenticated } from "/hooks/useIsAuthenticated.ts";
+import { use, type FormEventHandler, type PropsWithChildren } from "react";
import { InvalidError } from "/providers/RelayProviders";
+import { Viewer } from "/providers/Viewer";
type Props = PropsWithChildren<{
documentId?: string;
@@ -27,7 +27,7 @@ type Props = PropsWithChildren<{
}>;
const schema = z.object({
- name: z.string(),
+ fullName: z.string(),
email: z.string().email(),
});
@@ -41,18 +41,28 @@ export function RequestAccessDialog({
const trustCenter = useTrustCenter();
const { toast } = useToast();
const { __ } = useTranslate();
- const { handleSubmit, register, setError, formState } = useFormWithSchema(schema, {
- defaultValues: {
- name: "",
- email: "",
+ const viewer = use(Viewer);
+ const { handleSubmit, register, setError, formState } = useFormWithSchema(
+ schema,
+ {
+ defaultValues: {
+ fullName: "",
+ email: "",
+ },
},
- });
- const isAuthenticated = useIsAuthenticated();
+ );
const dialogRef = useDialogRef();
- const [commitMutation, isMutating] = useMutation({ documentId, reportId, trustCenterFileId });
+ const [commitMutation, isMutating] = useMutation({
+ documentId,
+ reportId,
+ trustCenterFileId,
+ });
const submitCallback = (data: z.infer | null) => {
- commitMutation(data)
+ commitMutation({
+ email: data?.email ?? viewer?.email ?? "",
+ fullName: data?.fullName ?? viewer?.fullName ?? "",
+ })
.then(() => {
onSuccess?.();
toast({
@@ -65,7 +75,7 @@ export function RequestAccessDialog({
.catch((error) => {
if (error instanceof InvalidError) {
if (error.field === "email") {
- setError(error.field, {message: error.message})
+ setError(error.field, { message: error.message });
}
return;
}
@@ -77,7 +87,7 @@ export function RequestAccessDialog({
});
};
- const onSubmit: FormEventHandler = isAuthenticated
+ const onSubmit: FormEventHandler = viewer
? (e) => {
e.preventDefault();
submitCallback(null);
@@ -102,12 +112,12 @@ export function RequestAccessDialog({
trustCenter.organization.name,
)}
- {!isAuthenticated && (
+ {!viewer && (
>
+ * @generated SignedSource<<5113feef2c7f7b13a2d6348202086916>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -11,8 +11,8 @@
import { ConcreteRequest } from 'relay-runtime';
export type RequestDocumentAccessInput = {
documentId: string;
- email?: any | null | undefined;
- name?: string | null | undefined;
+ email: any;
+ fullName: string;
trustCenterId: string;
};
export type RequestAccessDialogDocumentMutation$variables = {
diff --git a/apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts b/apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts
index 3df881031..7e568a46f 100644
--- a/apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts
+++ b/apps/trust/src/components/__generated__/RequestAccessDialogMutation.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<19ada29ea9b8f557b4fcc39f57bc3aec>>
+ * @generated SignedSource<<294b6a25a178cf3bd9269591ef61381f>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,8 +10,8 @@
import { ConcreteRequest } from 'relay-runtime';
export type RequestAllAccessesInput = {
- email?: any | null | undefined;
- name?: string | null | undefined;
+ email: any;
+ fullName: string;
trustCenterId: string;
};
export type RequestAccessDialogMutation$variables = {
diff --git a/apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts b/apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts
index 05c0f1997..8759c2321 100644
--- a/apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts
+++ b/apps/trust/src/components/__generated__/RequestAccessDialogReportMutation.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<858effd78b88a123430edcf6fcb100ed>>
+ * @generated SignedSource<>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,8 +10,8 @@
import { ConcreteRequest } from 'relay-runtime';
export type RequestReportAccessInput = {
- email?: any | null | undefined;
- name?: string | null | undefined;
+ email: any;
+ fullName: string;
reportId: string;
trustCenterId: string;
};
diff --git a/apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts b/apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts
index f4c55b39f..e57bdc053 100644
--- a/apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts
+++ b/apps/trust/src/components/__generated__/RequestAccessDialogTrustCenterFileMutation.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<32242c5e0b4dc9c9368c0e2f4034d95e>>
+ * @generated SignedSource<<1e744bd11b2d5dd4d982bf1ce8b2a721>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,8 +10,8 @@
import { ConcreteRequest } from 'relay-runtime';
export type RequestTrustCenterFileAccessInput = {
- email?: any | null | undefined;
- name?: string | null | undefined;
+ email: any;
+ fullName: string;
trustCenterFileId: string;
trustCenterId: string;
};
diff --git a/apps/trust/src/hooks/useIsAuthenticated.ts b/apps/trust/src/hooks/useIsAuthenticated.ts
deleted file mode 100644
index 64537af87..000000000
--- a/apps/trust/src/hooks/useIsAuthenticated.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { useContext } from "react";
-import { AuthContext } from "/providers/AuthProvider";
-
-export function useIsAuthenticated(): boolean {
- return useContext(AuthContext).isAuthenticated;
-}
diff --git a/apps/trust/src/layouts/MainLayout.tsx b/apps/trust/src/layouts/MainLayout.tsx
index ae7c96ad3..425c1ab6e 100644
--- a/apps/trust/src/layouts/MainLayout.tsx
+++ b/apps/trust/src/layouts/MainLayout.tsx
@@ -6,8 +6,8 @@ import { useTranslate } from "@probo/i18n";
import { OrganizationSidebar } from "/components/OrganizationSidebar";
import { Outlet } from "react-router";
import { NDADialog } from "/components/NDADialog";
-import { AuthProvider } from "/providers/AuthProvider";
import { TrustCenterProvider } from "/providers/TrustCenterProvider";
+import { Viewer } from "/providers/Viewer";
type Props = {
queryRef: PreloadedQuery;
@@ -26,7 +26,7 @@ export function MainLayout(props: Props) {
!trustCenter.hasAcceptedNonDisclosureAgreement &&
trustCenter.ndaFileUrl;
return (
-
+
{showNDADialog && (
{__("Overview")}
-
- {__("Documents")}
-
-
- {__("Subprocessors")}
-
+ {__("Documents")}
+ {__("Subprocessors")}
@@ -59,6 +55,6 @@ export function MainLayout(props: Props) {
{__("Powered by")}
-
+
);
}
diff --git a/apps/trust/src/providers/Viewer.tsx b/apps/trust/src/providers/Viewer.tsx
new file mode 100644
index 000000000..96f9a27dc
--- /dev/null
+++ b/apps/trust/src/providers/Viewer.tsx
@@ -0,0 +1,11 @@
+import { createContext } from "react";
+
+interface ViewerContextValue {
+ fullName: string;
+ email: string;
+}
+
+export const Viewer = createContext({
+ email: "",
+ fullName: "",
+});
diff --git a/apps/trust/src/queries/TrustGraph.ts b/apps/trust/src/queries/TrustGraph.ts
index 95ddd9d89..9a1240c35 100644
--- a/apps/trust/src/queries/TrustGraph.ts
+++ b/apps/trust/src/queries/TrustGraph.ts
@@ -82,6 +82,10 @@ export const trustVendorsQuery = graphql`
// Queries for custom domain (subdomain) approach
export const currentTrustGraphQuery = graphql`
query TrustGraphCurrentQuery {
+ viewer {
+ email
+ fullName
+ }
currentTrustCenter {
id
slug
diff --git a/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts
index 2e82a16b4..9b3aa7239 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<>
+ * @generated SignedSource<<1f5a4da3a77a893daebeceb249612a72>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -37,6 +37,10 @@ export type TrustGraphCurrentQuery$data = {
readonly slug: string;
readonly " $fragmentSpreads": FragmentRefs<"OverviewPageFragment">;
} | null | undefined;
+ readonly viewer: {
+ readonly email: any;
+ readonly fullName: string;
+ } | null | undefined;
};
export type TrustGraphCurrentQuery = {
response: TrustGraphCurrentQuery$data;
@@ -48,115 +52,122 @@ var v0 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "id",
+ "name": "email",
"storageKey": null
},
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "slug",
+ "name": "fullName",
"storageKey": null
},
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "isUserAuthenticated",
+ "name": "id",
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "hasAcceptedNonDisclosureAgreement",
+ "name": "slug",
"storageKey": null
},
v4 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "ndaFileName",
+ "name": "isUserAuthenticated",
"storageKey": null
},
v5 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "ndaFileUrl",
+ "name": "hasAcceptedNonDisclosureAgreement",
"storageKey": null
},
v6 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "name",
+ "name": "ndaFileName",
"storageKey": null
},
v7 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "description",
+ "name": "ndaFileUrl",
"storageKey": null
},
v8 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "websiteUrl",
+ "name": "name",
"storageKey": null
},
v9 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "logoUrl",
+ "name": "description",
"storageKey": null
},
v10 = {
"alias": null,
"args": null,
"kind": "ScalarField",
- "name": "email",
+ "name": "websiteUrl",
"storageKey": null
},
v11 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "logoUrl",
+ "storageKey": null
+},
+v12 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "headquarterAddress",
"storageKey": null
},
-v12 = [
+v13 = [
{
"kind": "Literal",
"name": "first",
"value": 50
}
],
-v13 = {
+v14 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "category",
"storageKey": null
},
-v14 = [
+v15 = [
{
"kind": "Literal",
"name": "first",
"value": 5
}
],
-v15 = {
+v16 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "isUserAuthorized",
"storageKey": null
},
-v16 = {
+v17 = {
"alias": null,
"args": null,
"kind": "ScalarField",
@@ -170,6 +181,19 @@ return {
"metadata": null,
"name": "TrustGraphCurrentQuery",
"selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "Identity",
+ "kind": "LinkedField",
+ "name": "viewer",
+ "plural": false,
+ "selections": [
+ (v0/*: any*/),
+ (v1/*: any*/)
+ ],
+ "storageKey": null
+ },
{
"alias": null,
"args": null,
@@ -178,12 +202,12 @@ return {
"name": "currentTrustCenter",
"plural": false,
"selections": [
- (v0/*: any*/),
- (v1/*: any*/),
(v2/*: any*/),
(v3/*: any*/),
(v4/*: any*/),
(v5/*: any*/),
+ (v6/*: any*/),
+ (v7/*: any*/),
{
"alias": null,
"args": null,
@@ -192,12 +216,12 @@ return {
"name": "organization",
"plural": false,
"selections": [
- (v6/*: any*/),
- (v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
- (v11/*: any*/)
+ (v11/*: any*/),
+ (v0/*: any*/),
+ (v12/*: any*/)
],
"storageKey": null
},
@@ -208,7 +232,7 @@ return {
},
{
"alias": null,
- "args": (v12/*: any*/),
+ "args": (v13/*: any*/),
"concreteType": "AuditConnection",
"kind": "LinkedField",
"name": "audits",
@@ -230,7 +254,7 @@ return {
"name": "node",
"plural": false,
"selections": [
- (v0/*: any*/),
+ (v2/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
@@ -258,6 +282,20 @@ return {
"kind": "Operation",
"name": "TrustGraphCurrentQuery",
"selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "Identity",
+ "kind": "LinkedField",
+ "name": "viewer",
+ "plural": false,
+ "selections": [
+ (v0/*: any*/),
+ (v1/*: any*/),
+ (v2/*: any*/)
+ ],
+ "storageKey": null
+ },
{
"alias": null,
"args": null,
@@ -266,12 +304,12 @@ return {
"name": "currentTrustCenter",
"plural": false,
"selections": [
- (v0/*: any*/),
- (v1/*: any*/),
(v2/*: any*/),
(v3/*: any*/),
(v4/*: any*/),
(v5/*: any*/),
+ (v6/*: any*/),
+ (v7/*: any*/),
{
"alias": null,
"args": null,
@@ -280,13 +318,13 @@ return {
"name": "organization",
"plural": false,
"selections": [
- (v6/*: any*/),
- (v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
(v11/*: any*/),
- (v0/*: any*/)
+ (v0/*: any*/),
+ (v12/*: any*/),
+ (v2/*: any*/)
],
"storageKey": null
},
@@ -320,10 +358,10 @@ return {
"name": "node",
"plural": false,
"selections": [
- (v0/*: any*/),
- (v6/*: any*/),
- (v9/*: any*/),
- (v8/*: any*/)
+ (v2/*: any*/),
+ (v8/*: any*/),
+ (v11/*: any*/),
+ (v10/*: any*/)
],
"storageKey": null
}
@@ -363,7 +401,7 @@ return {
"name": "node",
"plural": false,
"selections": [
- (v0/*: any*/),
+ (v2/*: any*/),
{
"alias": null,
"args": null,
@@ -371,9 +409,9 @@ return {
"name": "countries",
"storageKey": null
},
- (v6/*: any*/),
- (v13/*: any*/),
(v8/*: any*/),
+ (v14/*: any*/),
+ (v10/*: any*/),
{
"alias": null,
"args": null,
@@ -392,7 +430,7 @@ return {
},
{
"alias": null,
- "args": (v14/*: any*/),
+ "args": (v15/*: any*/),
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "documents",
@@ -414,7 +452,7 @@ return {
"name": "node",
"plural": false,
"selections": [
- (v0/*: any*/),
+ (v2/*: any*/),
{
"alias": null,
"args": null,
@@ -422,8 +460,8 @@ return {
"name": "title",
"storageKey": null
},
- (v15/*: any*/),
(v16/*: any*/),
+ (v17/*: any*/),
{
"alias": null,
"args": null,
@@ -442,7 +480,7 @@ return {
},
{
"alias": null,
- "args": (v14/*: any*/),
+ "args": (v15/*: any*/),
"concreteType": "TrustCenterFileConnection",
"kind": "LinkedField",
"name": "trustCenterFiles",
@@ -464,11 +502,11 @@ return {
"name": "node",
"plural": false,
"selections": [
- (v0/*: any*/),
- (v13/*: any*/),
- (v6/*: any*/),
- (v15/*: any*/),
- (v16/*: any*/)
+ (v2/*: any*/),
+ (v14/*: any*/),
+ (v8/*: any*/),
+ (v16/*: any*/),
+ (v17/*: any*/)
],
"storageKey": null
}
@@ -480,7 +518,7 @@ return {
},
{
"alias": null,
- "args": (v12/*: any*/),
+ "args": (v13/*: any*/),
"concreteType": "AuditConnection",
"kind": "LinkedField",
"name": "audits",
@@ -502,7 +540,7 @@ return {
"name": "node",
"plural": false,
"selections": [
- (v0/*: any*/),
+ (v2/*: any*/),
{
"alias": null,
"args": null,
@@ -511,7 +549,7 @@ return {
"name": "report",
"plural": false,
"selections": [
- (v0/*: any*/),
+ (v2/*: any*/),
{
"alias": null,
"args": null,
@@ -519,8 +557,8 @@ return {
"name": "filename",
"storageKey": null
},
- (v15/*: any*/),
- (v16/*: any*/)
+ (v16/*: any*/),
+ (v17/*: any*/)
],
"storageKey": null
},
@@ -532,8 +570,8 @@ return {
"name": "framework",
"plural": false,
"selections": [
- (v0/*: any*/),
- (v6/*: any*/),
+ (v2/*: any*/),
+ (v8/*: any*/),
{
"alias": null,
"args": null,
@@ -566,16 +604,16 @@ return {
]
},
"params": {
- "cacheID": "f098177c07810cbe7609821d63c22b0b",
+ "cacheID": "9d22288e0159409cf43bece191e3dc47",
"id": null,
"metadata": {},
"name": "TrustGraphCurrentQuery",
"operationKind": "query",
- "text": "query TrustGraphCurrentQuery {\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 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 = "2fc7c45c2636a551c13d806f89e53c4a";
+(node as any).hash = "dd2040667d5cc7b9af9659e993ba6a5b";
export default node;
diff --git a/apps/trust/src/routes.tsx b/apps/trust/src/routes.tsx
index 5dfc6841b..b63841c19 100644
--- a/apps/trust/src/routes.tsx
+++ b/apps/trust/src/routes.tsx
@@ -56,7 +56,7 @@ const routes = [
{
path: "/overview",
loader: loaderFromQueryLoader(() =>
- loadQuery(consoleEnvironment, currentTrustGraphQuery, {})
+ loadQuery(consoleEnvironment, currentTrustGraphQuery, {}),
),
Component: withQueryRef(MainLayout),
Fallback: MainSkeleton,
@@ -72,7 +72,7 @@ const routes = [
{
path: "/documents",
loader: loaderFromQueryLoader(() =>
- loadQuery(consoleEnvironment, currentTrustGraphQuery, {})
+ loadQuery(consoleEnvironment, currentTrustGraphQuery, {}),
),
Component: withQueryRef(MainLayout),
Fallback: MainSkeleton,
@@ -81,7 +81,7 @@ const routes = [
{
path: "",
loader: loaderFromQueryLoader(() =>
- loadQuery(consoleEnvironment, currentTrustDocumentsQuery, {})
+ loadQuery(consoleEnvironment, currentTrustDocumentsQuery, {}),
),
Fallback: TabSkeleton,
Component: withQueryRef(DocumentsPage),
@@ -91,7 +91,7 @@ const routes = [
{
path: "/subprocessors",
loader: loaderFromQueryLoader(() =>
- loadQuery(consoleEnvironment, currentTrustGraphQuery, {})
+ loadQuery(consoleEnvironment, currentTrustGraphQuery, {}),
),
Component: withQueryRef(MainLayout),
Fallback: MainSkeleton,
@@ -100,7 +100,7 @@ const routes = [
{
path: "",
loader: loaderFromQueryLoader(() =>
- loadQuery(consoleEnvironment, currentTrustVendorsQuery, {})
+ loadQuery(consoleEnvironment, currentTrustVendorsQuery, {}),
),
Fallback: TabSkeleton,
Component: withQueryRef(SubprocessorsPage),
diff --git a/apps/trust/vite.config.ts b/apps/trust/vite.config.ts
index 3b281c25e..9787ddda2 100644
--- a/apps/trust/vite.config.ts
+++ b/apps/trust/vite.config.ts
@@ -17,13 +17,17 @@ export default defineConfig({
target: "http://localhost:8080",
changeOrigin: true,
},
+ "/trust/YJwjPEJCAAEAFgAAAZsTYtQt-FLmpawO/api": {
+ target: "http://localhost:8080",
+ changeOrigin: true,
+ },
},
},
resolve: {
alias: {
"/type": fileURLToPath(new URL("./src/type.ts", import.meta.url)),
"/components": fileURLToPath(
- new URL("./src/components", import.meta.url)
+ new URL("./src/components", import.meta.url),
),
"/queries": fileURLToPath(new URL("./src/queries", import.meta.url)),
"/helpers": fileURLToPath(new URL("./src/helpers", import.meta.url)),
diff --git a/packages/emails/emails.go b/packages/emails/emails.go
index 39024d830..1d84f769b 100644
--- a/packages/emails/emails.go
+++ b/packages/emails/emails.go
@@ -196,6 +196,23 @@ func RenderTrustCenterDocumentAccessRejected(
return fmt.Sprintf(subjectTrustCenterDocumentAccessRejected, organizationName), textBody, htmlBody, err
}
+func RenderMagicLink(baseURL, fullName, magicLinkUrl string, tokenDuration time.Duration) (subject string, textBody string, htmlBody *string, err error) {
+ data := struct {
+ FullName string
+ MagicLinkUrl string
+ LogoURL string
+ DurationInMinutes int
+ }{
+ FullName: fullName,
+ MagicLinkUrl: magicLinkUrl,
+ LogoURL: baseURL + logoURLPath,
+ DurationInMinutes: int(tokenDuration.Minutes()),
+ }
+
+ textBody, htmlBody, err = renderEmail(trustCenterAccessTextTemplate, trustCenterAccessHTMLTemplate, data)
+ return subjectTrustCenterAccess, textBody, htmlBody, err
+}
+
func renderEmail(textTemplate *texttemplate.Template, htmlTemplate *htmltemplate.Template, data any) (textBody string, htmlBody *string, err error) {
var textBuf bytes.Buffer
if err := textTemplate.Execute(&textBuf, data); err != nil {
diff --git a/packages/emails/scripts/build.ts b/packages/emails/scripts/build.ts
index 347f34ed5..7c26b5827 100644
--- a/packages/emails/scripts/build.ts
+++ b/packages/emails/scripts/build.ts
@@ -1,17 +1,17 @@
-import { render } from '@react-email/components';
-import { copyFile, mkdir, writeFile } from 'node:fs/promises';
-import { dirname, join } from 'node:path';
-import { fileURLToPath } from 'node:url';
-import * as React from 'react';
+import { render } from "@react-email/components";
+import { copyFile, mkdir, writeFile } from "node:fs/promises";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+import * as React from "react";
-import ConfirmEmail from '../src/ConfirmEmail';
-import DocumentExport from '../src/DocumentExport';
-import DocumentSigning from '../src/DocumentSigning';
-import FrameworkExport from '../src/FrameworkExport';
-import Invitation from '../src/Invitation';
-import PasswordReset from '../src/PasswordReset';
-import TrustCenterAccess from '../src/TrustCenterAccess';
-import TrustCenterDocumentAccessRejected from '../src/TrustCenterDocumentAccessRejected';
+import ConfirmEmail from "../src/ConfirmEmail";
+import DocumentExport from "../src/DocumentExport";
+import DocumentSigning from "../src/DocumentSigning";
+import FrameworkExport from "../src/FrameworkExport";
+import Invitation from "../src/Invitation";
+import PasswordReset from "../src/PasswordReset";
+import TrustCenterAccess from "../src/TrustCenterAccess";
+import TrustCenterDocumentAccessRejected from "../src/TrustCenterDocumentAccessRejected";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -23,42 +23,46 @@ type TemplateConfig = {
const templates: TemplateConfig[] = [
{
- name: 'confirm-email',
- render: () => ConfirmEmail()
+ name: "confirm-email",
+ render: () => ConfirmEmail(),
},
{
- name: 'password-reset',
- render: () => PasswordReset()
+ name: "password-reset",
+ render: () => PasswordReset(),
},
{
- name: 'invitation',
- render: () => Invitation()
+ name: "invitation",
+ render: () => Invitation(),
},
{
- name: 'document-signing',
- render: () => DocumentSigning()
+ name: "document-signing",
+ render: () => DocumentSigning(),
},
{
- name: 'document-export',
- render: () => DocumentExport()
+ name: "document-export",
+ render: () => DocumentExport(),
},
{
- name: 'framework-export',
- render: () => FrameworkExport()
+ name: "framework-export",
+ render: () => FrameworkExport(),
},
{
- name: 'trust-center-access',
- render: () => TrustCenterAccess()
+ name: "trust-center-access",
+ render: () => TrustCenterAccess(),
},
{
- name: 'trust-center-document-access-rejected',
- render: () => TrustCenterDocumentAccessRejected()
+ name: "trust-center-document-access-rejected",
+ render: () => TrustCenterDocumentAccessRejected(),
+ },
+ {
+ name: "magic-link",
+ render: () => TrustCenterAccess(),
},
];
async function build() {
- const outputDir = join(__dirname, '..', 'dist');
- const templatesDir = join(__dirname, '..', 'templates');
+ const outputDir = join(__dirname, "..", "dist");
+ const templatesDir = join(__dirname, "..", "templates");
await mkdir(outputDir, { recursive: true });
for (const template of templates) {
@@ -74,6 +78,6 @@ async function build() {
}
build().catch((err) => {
- console.error('Failed to build email templates:', err);
+ console.error("Failed to build email templates:", err);
process.exit(1);
});
diff --git a/packages/emails/src/MagicLink.tsx b/packages/emails/src/MagicLink.tsx
new file mode 100644
index 000000000..6e45e70d1
--- /dev/null
+++ b/packages/emails/src/MagicLink.tsx
@@ -0,0 +1,28 @@
+import { Button, Section, Text } from "@react-email/components";
+import * as React from "react";
+import EmailLayout, {
+ bodyText,
+ button,
+ buttonContainer,
+ footerText,
+} from "./components/EmailLayout";
+
+export const MagicLink = () => {
+ return (
+
+ Please use this link to connect to Probo:
+
+
+
+
+ This link will expire in {"{{.DurationInMinutes}}"} minutes.
+
+
+ );
+};
+
+export default MagicLink;
diff --git a/packages/emails/src/TrustCenterAccess.tsx b/packages/emails/src/TrustCenterAccess.tsx
index 02c392a0d..1a1f9ec77 100644
--- a/packages/emails/src/TrustCenterAccess.tsx
+++ b/packages/emails/src/TrustCenterAccess.tsx
@@ -1,22 +1,31 @@
-import { Button, Section, Text } from '@react-email/components';
-import * as React from 'react';
-import EmailLayout, { bodyText, button, buttonContainer, footerText } from './components/EmailLayout';
+import { Button, Section, Text } from "@react-email/components";
+import * as React from "react";
+import EmailLayout, {
+ bodyText,
+ button,
+ buttonContainer,
+ footerText,
+} from "./components/EmailLayout";
export const TrustCenterAccess = () => {
return (
-
+
- You have been granted access to {'{{.OrganizationName}}'}'s Trust Center! Click the button below to access it:
+ You have been granted access to{" "}
+ {"{{.OrganizationName}}"}'s Trust Center! Click the
+ button below to access it:
- This link will expire in {'{{.DurationInDays}}'} days.
+ This link will expire in {"{{.DurationInDays}}"} days.
);
diff --git a/packages/emails/templates/magic-link.txt b/packages/emails/templates/magic-link.txt
new file mode 100644
index 000000000..f55d29df1
--- /dev/null
+++ b/packages/emails/templates/magic-link.txt
@@ -0,0 +1,11 @@
+Probo
+
+Hi {{.FullName}},
+
+Please use this link to connect to Probo:
+
+{{.MagicLinkURL}}
+
+This link will expire in {{.DurationInMinutes}} minutes.
+
+Probo Inc, 490 Post St, STE 640, San Francisco, CA, 94102, US
diff --git a/packages/prettier/prettier.config.js b/packages/prettier/prettier.config.js
index 3e650e73c..43caab550 100644
--- a/packages/prettier/prettier.config.js
+++ b/packages/prettier/prettier.config.js
@@ -1,3 +1,3 @@
module.exports = {
- tabWidth: 2,
+ tabWidth: 2,
};
diff --git a/pkg/iam/auth_service.go b/pkg/iam/auth_service.go
index 7cdbe5d40..d6c08607a 100644
--- a/pkg/iam/auth_service.go
+++ b/pkg/iam/auth_service.go
@@ -16,6 +16,7 @@ package iam
import (
"context"
+ "errors"
"fmt"
"time"
@@ -50,6 +51,11 @@ type (
FullName string
}
+ LoadOrCreateIdentityRequest struct {
+ Email mail.Addr
+ FullName string
+ }
+
CreateIdentityWithPasswordRequest struct {
Email mail.Addr
Password string
@@ -59,11 +65,16 @@ type (
PasswordResetData struct {
Email mail.Addr `json:"email"`
}
+
+ MagicLinkData struct {
+ Email mail.Addr `json:"email"`
+ }
)
const (
TokenTypeOrganizationInvitation = "organization_invitation"
TokenTypePasswordReset = "password_reset"
+ TokenTypeMagicLink = "magic_link"
)
func NewAuthService(svc *Service) *AuthService {
@@ -98,6 +109,14 @@ func (req ChangePasswordRequest) Validate() error {
return v.Error()
}
+func (req LoadOrCreateIdentityRequest) Validate() error {
+ v := validator.New()
+
+ v.Check(req.FullName, "fullName", validator.NotEmpty(), validator.MinLen(1), validator.MaxLen(255))
+
+ return v.Error()
+}
+
func (req CreateIdentityWithPasswordRequest) Validate() error {
v := validator.New()
@@ -300,6 +319,51 @@ func (s AuthService) SendPasswordResetInstructionByEmail(
)
}
+func (s AuthService) LoadOrCreateIdentity(
+ ctx context.Context,
+ req *LoadOrCreateIdentityRequest,
+) (*coredata.Identity, error) {
+ if err := req.Validate(); err != nil {
+ return nil, fmt.Errorf("invalid request: %w", err)
+ }
+
+ var (
+ identity *coredata.Identity
+ now = time.Now()
+ )
+
+ if err := s.pg.WithTx(ctx, func(tx pg.Conn) error {
+ identity = &coredata.Identity{}
+
+ if err := identity.LoadByEmail(ctx, tx, req.Email); err != nil {
+ if !errors.Is(err, coredata.ErrResourceNotFound) {
+ return fmt.Errorf("cannot load identity: %w", err)
+ }
+
+ identity = &coredata.Identity{
+ ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
+ EmailAddress: req.Email,
+ FullName: req.FullName,
+ HashedPassword: nil,
+ EmailAddressVerified: false,
+ CreatedAt: now,
+ UpdatedAt: now,
+ }
+
+ err = identity.Insert(ctx, tx)
+ if err != nil {
+ return fmt.Errorf("cannot insert identity: %w", err)
+ }
+ }
+
+ return nil
+ }); err != nil {
+ return nil, err
+ }
+
+ return identity, nil
+}
+
func (s AuthService) CreateIdentityWithPassword(
ctx context.Context,
req *CreateIdentityWithPasswordRequest,
@@ -476,3 +540,103 @@ func (s AuthService) OpenSessionWithPassword(ctx context.Context, email mail.Add
return identity, session, err
}
+
+func (s AuthService) SendMagicLink(ctx context.Context, email mail.Addr) error {
+ token, err := statelesstoken.NewToken(
+ s.tokenSecret,
+ TokenTypeMagicLink,
+ s.magicLinkTokenValidity,
+ MagicLinkData{
+ Email: email,
+ },
+ )
+ if err != nil {
+ return fmt.Errorf("cannot generate magic link token: %w", err)
+ }
+
+ base, err := baseurl.Parse(s.baseURL)
+ if err != nil {
+ return fmt.Errorf("cannot parse base URL: %w", err)
+ }
+
+ magicLinkURL := base.
+ WithPath("/auth/magic-link").
+ WithQuery("token", token).
+ MustString()
+
+ return s.pg.WithTx(
+ ctx,
+ func(tx pg.Conn) error {
+ fullName := email.Username()
+ identity := &coredata.Identity{}
+
+ err := identity.LoadByEmail(ctx, tx, email)
+ if err == nil {
+ fullName = identity.FullName
+ } else {
+ if !errors.Is(err, coredata.ErrResourceNotFound) {
+ return fmt.Errorf("cannot load identity: %w", err)
+ }
+ }
+
+ subject, textBody, htmlBody, err := emails.RenderMagicLink(
+ s.baseURL,
+ fullName,
+ magicLinkURL,
+ s.invitationTokenValidity,
+ )
+ if err != nil {
+ return fmt.Errorf("cannot render magic link email: %w", err)
+ }
+
+ magicLinkEmail := coredata.NewEmail(
+ fullName,
+ email,
+ subject,
+ textBody,
+ htmlBody,
+ )
+
+ err = magicLinkEmail.Insert(ctx, tx)
+ if err != nil {
+ return fmt.Errorf("cannot insert email: %w", err)
+ }
+
+ return nil
+ },
+ )
+}
+
+func (s AuthService) OpenSessionWithMagicLink(ctx context.Context, token string) (*coredata.Identity, *coredata.Session, error) {
+ var (
+ identity = &coredata.Identity{}
+ session = &coredata.Session{}
+ )
+
+ payload, err := statelesstoken.ValidateToken[MagicLinkData](s.tokenSecret, TokenTypeMagicLink, token)
+ if err != nil {
+ return nil, nil, NewInvalidTokenError()
+ }
+
+ if err := s.pg.WithTx(
+ ctx,
+ func(conn pg.Conn) error {
+ err := identity.LoadByEmail(ctx, conn, payload.Data.Email)
+ if err != nil {
+ return fmt.Errorf("cannot load identity by email: %w", err)
+ }
+
+ session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, s.sessionDuration)
+ err = session.Insert(ctx, conn)
+ if err != nil {
+ return fmt.Errorf("cannot insert session: %w", err)
+ }
+
+ return nil
+ },
+ ); err != nil {
+ return nil, nil, err
+ }
+
+ return identity, session, err
+}
diff --git a/pkg/iam/service.go b/pkg/iam/service.go
index 8ae4e389d..ba4b9b3be 100644
--- a/pkg/iam/service.go
+++ b/pkg/iam/service.go
@@ -31,6 +31,7 @@ type (
disableSignup bool
invitationTokenValidity time.Duration
passwordResetTokenValidity time.Duration
+ magicLinkTokenValidity time.Duration
sessionDuration time.Duration
bucket string
certificate *x509.Certificate
@@ -53,6 +54,7 @@ type (
DisableSignup bool
InvitationTokenValidity time.Duration
PasswordResetTokenValidity time.Duration
+ MagicLinkTokenValidity time.Duration
SessionDuration time.Duration
Bucket string
TokenSecret string
@@ -99,6 +101,7 @@ func NewService(
disableSignup: cfg.DisableSignup,
invitationTokenValidity: cfg.InvitationTokenValidity,
passwordResetTokenValidity: cfg.PasswordResetTokenValidity,
+ magicLinkTokenValidity: cfg.MagicLinkTokenValidity,
sessionDuration: cfg.SessionDuration,
bucket: cfg.Bucket,
certificate: cfg.Certificate,
diff --git a/pkg/mail/addr.go b/pkg/mail/addr.go
index 06e6b3766..f6e051e4b 100644
--- a/pkg/mail/addr.go
+++ b/pkg/mail/addr.go
@@ -16,6 +16,19 @@ func (a Addr) String() string {
return string(a)
}
+func (a *Addr) Username() string {
+ if a == nil || *a == Nil {
+ return ""
+ }
+
+ parts := strings.Split(a.String(), "@")
+ if len(parts) != 2 {
+ return ""
+ }
+
+ return parts[0]
+}
+
func (a *Addr) Domain() string {
if a == nil || *a == Nil {
return ""
diff --git a/pkg/probo/service.go b/pkg/probo/service.go
index f46f34b42..f01463e81 100644
--- a/pkg/probo/service.go
+++ b/pkg/probo/service.go
@@ -468,3 +468,25 @@ func (s *Service) LoadTrustCenterByID(ctx context.Context, id gid.GID) (*TrustCe
return &info, err
}
+
+func (s *Service) LoadTrustCenterByOrganizationID(ctx context.Context, organizationID gid.GID) (*TrustCenterInfo, error) {
+ var info TrustCenterInfo
+
+ err := s.pg.WithConn(
+ ctx,
+ func(conn pg.Conn) error {
+ scope := coredata.NewScope(organizationID.TenantID())
+ var trustCenter coredata.TrustCenter
+ if err := trustCenter.LoadByOrganizationID(ctx, conn, scope, organizationID); err != nil {
+ return fmt.Errorf("cannot load trust center: %w", err)
+ }
+
+ info.ID = trustCenter.ID
+ info.OrganizationID = trustCenter.OrganizationID
+
+ return nil
+ },
+ )
+
+ return &info, err
+}
diff --git a/pkg/probo/trust_center_access_service.go b/pkg/probo/trust_center_access_service.go
index d78f4cd0b..0a54e3476 100644
--- a/pkg/probo/trust_center_access_service.go
+++ b/pkg/probo/trust_center_access_service.go
@@ -40,7 +40,7 @@ type (
CreateTrustCenterAccessRequest struct {
TrustCenterID gid.GID
Email mail.Addr
- Name string
+ FullName string
}
UpdateTrustCenterDocumentAccessRequest struct {
@@ -69,7 +69,7 @@ func (ctcar *CreateTrustCenterAccessRequest) Validate() error {
v.Check(ctcar.TrustCenterID, "trust_center_id", validator.Required(), validator.GID(coredata.TrustCenterEntityType))
v.Check(ctcar.Email, "email", validator.Required(), validator.NotEmpty())
v.Check(ctcar.Email.Domain(), "email", validator.NotBlacklisted())
- v.Check(ctcar.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength))
+ v.Check(ctcar.FullName, "name", validator.SafeTextNoNewLine(TitleMaxLength))
return v.Error()
}
@@ -244,7 +244,7 @@ func (s TrustCenterAccessService) Create(
TenantID: s.svc.scope.GetTenantID(),
TrustCenterID: req.TrustCenterID,
Email: req.Email,
- Name: req.Name,
+ Name: req.FullName,
Active: false,
HasAcceptedNonDisclosureAgreement: false,
CreatedAt: now,
diff --git a/pkg/probod/auth_config.go b/pkg/probod/auth_config.go
index c60ff9c59..8ef68cadb 100644
--- a/pkg/probod/auth_config.go
+++ b/pkg/probod/auth_config.go
@@ -26,6 +26,7 @@ type (
DisableSignup bool `json:"disable-signup"`
InvitationConfirmationTokenValidity int `json:"invitation-confirmation-token-validity"`
PasswordResetTokenValidity int `json:"password-reset-token-validity"`
+ MagicLinkTokenValidity int `json:"magic-link-token-validity"`
SAML samlConfig `json:"saml"`
}
diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go
index 3267c4517..224aa82fb 100644
--- a/pkg/probod/probod.go
+++ b/pkg/probod/probod.go
@@ -125,6 +125,7 @@ func New() *Implm {
DisableSignup: false,
InvitationConfirmationTokenValidity: 3600,
PasswordResetTokenValidity: 3600,
+ MagicLinkTokenValidity: 3600,
SAML: samlConfig{
SessionDuration: 604800,
CleanupIntervalSeconds: 86400,
@@ -318,6 +319,7 @@ func (impl *Implm) Run(
DisableSignup: impl.cfg.Auth.DisableSignup,
InvitationTokenValidity: time.Duration(impl.cfg.Auth.InvitationConfirmationTokenValidity) * time.Second,
PasswordResetTokenValidity: time.Duration(impl.cfg.Auth.PasswordResetTokenValidity) * time.Second,
+ MagicLinkTokenValidity: time.Duration(impl.cfg.Auth.MagicLinkTokenValidity) * time.Second,
SessionDuration: time.Duration(impl.cfg.Auth.Cookie.Duration) * time.Hour,
Bucket: impl.cfg.AWS.Bucket,
TokenSecret: impl.cfg.Auth.Cookie.Secret,
diff --git a/pkg/server/api/authn/context.go b/pkg/server/api/authn/context.go
index 54731fb92..0ea9596e5 100644
--- a/pkg/server/api/authn/context.go
+++ b/pkg/server/api/authn/context.go
@@ -28,6 +28,7 @@ var (
identityContextKey = &ctxKey{name: "identity"}
sessionContextKey = &ctxKey{name: "session"}
apiKeyContextKey = &ctxKey{name: "api_key"}
+ TrustCenterKey = &ctxKey{name: "trust_center"}
)
func SessionFromContext(ctx context.Context) *coredata.Session {
diff --git a/pkg/server/api/connect/v1/v1_resolver.go b/pkg/server/api/connect/v1/v1_resolver.go
index b034a5a27..1ec34a145 100644
--- a/pkg/server/api/connect/v1/v1_resolver.go
+++ b/pkg/server/api/connect/v1/v1_resolver.go
@@ -359,11 +359,11 @@ func (r *membershipProfileResolver) Permission(ctx context.Context, obj *types.M
func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput) (*types.SignInPayload, error) {
// TODO: handle existing session to only open child session and chnage root session auth method to PASSWORD
- user, session, err := r.iam.AuthService.OpenSessionWithPassword(ctx, input.Email, input.Password)
+ identity, session, err := r.iam.AuthService.OpenSessionWithPassword(ctx, input.Email, input.Password)
if err != nil {
var errInvalidPassword *iam.ErrInvalidPassword
if errors.As(err, &errInvalidPassword) {
- return nil, graphql.ErrorOnPath(ctx, err)
+ return nil, gqlutils.Invalid(ctx, err)
}
var errInvalidCredentials *iam.ErrInvalidCredentials
@@ -384,7 +384,7 @@ func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput)
r.sessionCookie.Set(w, session)
return &types.SignInPayload{
- Identity: types.NewIdentity(user),
+ Identity: types.NewIdentity(identity),
Session: types.NewSession(session),
}, nil
}
diff --git a/pkg/server/api/console/v1/graphql_handler.go b/pkg/server/api/console/v1/graphql_handler.go
index 53242be45..f5d15c29e 100644
--- a/pkg/server/api/console/v1/graphql_handler.go
+++ b/pkg/server/api/console/v1/graphql_handler.go
@@ -32,6 +32,7 @@ func NewGraphQLHandler(iamSvc *iam.Service, proboSvc *probo.Service, customDomai
probo: proboSvc,
iam: iamSvc,
customDomainCname: customDomainCname,
+ logger: logger,
},
}
diff --git a/pkg/server/api/console/v1/resolver.go b/pkg/server/api/console/v1/resolver.go
index f1e9906ce..bfd0463e0 100644
--- a/pkg/server/api/console/v1/resolver.go
+++ b/pkg/server/api/console/v1/resolver.go
@@ -46,6 +46,7 @@ type (
authorize authz.AuthorizeFunc
probo *probo.Service
iam *iam.Service
+ logger *log.Logger
customDomainCname string
}
)
@@ -97,7 +98,10 @@ func NewMux(
}
w.WriteHeader(http.StatusOK)
- json.NewEncoder(w).Encode(requests)
+ if err := json.NewEncoder(w).Encode(requests); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
},
)
@@ -151,7 +155,7 @@ func NewMux(
w.Header().Set("Content-Type", "application/pdf")
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s.pdf\"", uuid.String()))
w.WriteHeader(http.StatusOK)
- w.Write(pdfData)
+ _, _ = w.Write(pdfData)
},
)
diff --git a/pkg/server/api/console/v1/types/audit.go b/pkg/server/api/console/v1/types/audit.go
index 7259ae75a..d89e7b9fe 100644
--- a/pkg/server/api/console/v1/types/audit.go
+++ b/pkg/server/api/console/v1/types/audit.go
@@ -60,7 +60,7 @@ func NewAuditEdge(a *coredata.Audit, orderField coredata.AuditOrderField) *Audit
}
func NewAudit(a *coredata.Audit) *Audit {
- return &Audit{
+ node := &Audit{
ID: a.ID,
Organization: &Organization{
ID: a.OrganizationID,
@@ -76,4 +76,12 @@ func NewAudit(a *coredata.Audit) *Audit {
CreatedAt: a.CreatedAt,
UpdatedAt: a.UpdatedAt,
}
+
+ if a.ReportID != nil {
+ node.Report = &Report{
+ ID: *a.ReportID,
+ }
+ }
+
+ return node
}
diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go
index 4452ca20d..a851f3e23 100644
--- a/pkg/server/api/console/v1/v1_resolver.go
+++ b/pkg/server/api/console/v1/v1_resolver.go
@@ -14,6 +14,7 @@ import (
"time"
pgx "github.com/jackc/pgx/v5"
+ "go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/iam"
@@ -1674,12 +1675,28 @@ func (r *mutationResolver) CreateTrustCenterAccess(ctx context.Context, input ty
prb := r.ProboService(ctx, input.TrustCenterID.TenantID())
+ // TODO: when admin/owner creates trust center access, we should have an invite for it instead of directly creating the identity
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ var err error
+ identity, err = r.iam.AuthService.LoadOrCreateIdentity(
+ ctx,
+ &iam.LoadOrCreateIdentityRequest{
+ Email: input.Email,
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load or create identity", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
+
access, err := prb.TrustCenterAccesses.Create(
ctx,
&probo.CreateTrustCenterAccessRequest{
TrustCenterID: input.TrustCenterID,
- Email: input.Email,
- Name: input.Name,
+ Email: identity.EmailAddress,
+ FullName: identity.FullName,
},
)
if err != nil {
@@ -1687,8 +1704,8 @@ func (r *mutationResolver) CreateTrustCenterAccess(ctx context.Context, input ty
return nil, gqlutils.Conflict(ctx, err)
}
- // TODO no panic use gqlutils.InternalError
- panic(fmt.Errorf("cannot create trust center access: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot create trust center access", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return &types.CreateTrustCenterAccessPayload{
diff --git a/pkg/server/api/trust/v1/resolver.go b/pkg/server/api/trust/v1/resolver.go
index 22f5c3be2..fd07a6b7b 100644
--- a/pkg/server/api/trust/v1/resolver.go
+++ b/pkg/server/api/trust/v1/resolver.go
@@ -20,13 +20,16 @@ import (
"context"
"time"
+ "github.com/99designs/gqlgen/graphql"
"github.com/go-chi/chi/v5"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/iam"
+ "go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/securecookie"
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/trust/v1/schema"
+ "go.probo.inc/probo/pkg/server/api/trust/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/trust"
)
@@ -45,10 +48,28 @@ type (
}
Resolver struct {
- trust *trust.Service
+ trust *trust.Service
+ logger *log.Logger
+ iam *iam.Service
+ sessionCookie *authn.Cookie
}
)
+type ctxKey struct{ name string }
+
+var (
+ TrustCenterKey = &ctxKey{name: "trust_center"}
+)
+
+func TrustCenterFromContext(ctx context.Context) probo.TrustCenterInfo {
+ trustCenter, _ := ctx.Value(TrustCenterKey).(probo.TrustCenterInfo)
+ return trustCenter
+}
+
+func ContextWithTrustCenter(ctx context.Context, trustCenter probo.TrustCenterInfo) context.Context {
+ return context.WithValue(ctx, TrustCenterKey, trustCenter)
+}
+
func NewMux(
logger *log.Logger,
iamSvc *iam.Service,
@@ -60,7 +81,25 @@ func NewMux(
sessionMiddleware := authn.NewSessionMiddleware(iamSvc, cookieConfig)
r.Use(sessionMiddleware)
- config := schema.Config{Resolvers: &Resolver{trust: trustSvc}}
+ config := schema.Config{
+ Resolvers: &Resolver{
+ iam: iamSvc,
+ trust: trustSvc,
+ logger: logger,
+ sessionCookie: authn.NewCookie(&cookieConfig),
+ },
+ Directives: schema.DirectiveRoot{
+ MustBeAuthenticated: func(ctx context.Context, obj any, next graphql.Resolver, role *types.Role) (any, error) {
+ identity := authn.IdentityFromContext(ctx)
+
+ if identity == nil {
+ return nil, gqlutils.Unauthenticatedf(ctx, "authentication required")
+ }
+
+ return next(ctx)
+ },
+ },
+ }
es := schema.NewExecutableSchema(config)
graphqlHandler := gqlutils.NewHandler(es, logger)
@@ -73,14 +112,6 @@ func (r *Resolver) RootTrustService(ctx context.Context) *trust.TenantService {
return r.trust.WithTenant(gid.NewTenantID())
}
-func (r *Resolver) PublicTrustService(ctx context.Context, tenantID gid.TenantID) *trust.TenantService {
+func (r *Resolver) TrustService(ctx context.Context, tenantID gid.TenantID) *trust.TenantService {
return r.trust.WithTenant(tenantID)
}
-
-func (r *Resolver) PrivateTrustService(ctx context.Context, tenantID gid.TenantID) (*trust.TenantService, error) {
- // if err := trustauth.ValidateTenantAccess(ctx, r, userTenantContextKey, tenantID); err != nil {
- // return nil, fmt.Errorf("cannot access trust center: %w", err)
- // }
-
- return r.trust.WithTenant(tenantID), nil
-}
diff --git a/pkg/server/api/trust/v1/schema.graphql b/pkg/server/api/trust/v1/schema.graphql
index 6cbe275b5..713ade4a5 100644
--- a/pkg/server/api/trust/v1/schema.graphql
+++ b/pkg/server/api/trust/v1/schema.graphql
@@ -34,6 +34,15 @@ type PageInfo {
endCursor: CursorKey
}
+type Identity implements Node {
+ id: ID!
+ email: EmailAddr!
+ fullName: String!
+ emailVerified: Boolean!
+ createdAt: Datetime!
+ updatedAt: Datetime!
+}
+
type Organization implements Node {
id: ID!
name: String!
@@ -535,10 +544,18 @@ type TrustCenterAccess implements Node {
updatedAt: Datetime!
}
+input SignInWithTokenInput {
+ token: String!
+}
+
+type SignInWithTokenPayload {
+ success: Boolean!
+}
+
input RequestAllAccessesInput {
trustCenterId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
type RequestAccessesPayload {
@@ -560,22 +577,22 @@ input AcceptNonDisclosureAgreementInput {
input RequestDocumentAccessInput {
trustCenterId: ID!
documentId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
input RequestReportAccessInput {
trustCenterId: ID!
reportId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
input RequestTrustCenterFileAccessInput {
trustCenterId: ID!
trustCenterFileId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
input ExportTrustCenterFileInput {
@@ -599,12 +616,15 @@ type AcceptNonDisclosureAgreementPayload {
}
type Query {
+ viewer: Identity
node(id: ID!): Node!
trustCenterBySlug(slug: String!): TrustCenter @mustBeAuthenticated(role: NONE)
currentTrustCenter: TrustCenter @mustBeAuthenticated(role: NONE)
}
type Mutation {
+ signInWithToken(input: SignInWithTokenInput!): SignInWithTokenPayload!
+
requestAllAccesses(input: RequestAllAccessesInput!): RequestAccessesPayload!
@mustBeAuthenticated(role: NONE)
diff --git a/pkg/server/api/trust/v1/schema/schema.go b/pkg/server/api/trust/v1/schema/schema.go
index f703d2831..630eb48b1 100644
--- a/pkg/server/api/trust/v1/schema/schema.go
+++ b/pkg/server/api/trust/v1/schema/schema.go
@@ -120,6 +120,15 @@ type ComplexityRoot struct {
Name func(childComplexity int) int
}
+ Identity struct {
+ CreatedAt func(childComplexity int) int
+ Email func(childComplexity int) int
+ EmailVerified func(childComplexity int) int
+ FullName func(childComplexity int) int
+ ID func(childComplexity int) int
+ UpdatedAt func(childComplexity int) int
+ }
+
Mutation struct {
AcceptNonDisclosureAgreement func(childComplexity int, input types.AcceptNonDisclosureAgreementInput) int
ExportDocumentPDF func(childComplexity int, input types.ExportDocumentPDFInput) int
@@ -129,6 +138,7 @@ type ComplexityRoot struct {
RequestDocumentAccess func(childComplexity int, input types.RequestDocumentAccessInput) int
RequestReportAccess func(childComplexity int, input types.RequestReportAccessInput) int
RequestTrustCenterFileAccess func(childComplexity int, input types.RequestTrustCenterFileAccessInput) int
+ SignInWithToken func(childComplexity int, input types.SignInWithTokenInput) int
}
Organization struct {
@@ -152,6 +162,7 @@ type ComplexityRoot struct {
CurrentTrustCenter func(childComplexity int) int
Node func(childComplexity int, id gid.GID) int
TrustCenterBySlug func(childComplexity int, slug string) int
+ Viewer func(childComplexity int) int
}
Report struct {
@@ -165,6 +176,10 @@ type ComplexityRoot struct {
TrustCenterAccess func(childComplexity int) int
}
+ SignInWithTokenPayload struct {
+ Success func(childComplexity int) int
+ }
+
TrustCenter struct {
Active func(childComplexity int) int
Audits func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
@@ -258,6 +273,7 @@ type FrameworkResolver interface {
DarkLogoURL(ctx context.Context, obj *types.Framework) (*string, error)
}
type MutationResolver interface {
+ SignInWithToken(ctx context.Context, input types.SignInWithTokenInput) (*types.SignInWithTokenPayload, error)
RequestAllAccesses(ctx context.Context, input types.RequestAllAccessesInput) (*types.RequestAccessesPayload, error)
ExportDocumentPDF(ctx context.Context, input types.ExportDocumentPDFInput) (*types.ExportDocumentPDFPayload, error)
ExportReportPDF(ctx context.Context, input types.ExportReportPDFInput) (*types.ExportReportPDFPayload, error)
@@ -271,6 +287,7 @@ type OrganizationResolver interface {
LogoURL(ctx context.Context, obj *types.Organization) (*string, error)
}
type QueryResolver interface {
+ Viewer(ctx context.Context) (*types.Identity, error)
Node(ctx context.Context, id gid.GID) (types.Node, error)
TrustCenterBySlug(ctx context.Context, slug string) (*types.TrustCenter, error)
CurrentTrustCenter(ctx context.Context) (*types.TrustCenter, error)
@@ -472,6 +489,43 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.Framework.Name(childComplexity), true
+ case "Identity.createdAt":
+ if e.complexity.Identity.CreatedAt == nil {
+ break
+ }
+
+ return e.complexity.Identity.CreatedAt(childComplexity), true
+ case "Identity.email":
+ if e.complexity.Identity.Email == nil {
+ break
+ }
+
+ return e.complexity.Identity.Email(childComplexity), true
+ case "Identity.emailVerified":
+ if e.complexity.Identity.EmailVerified == nil {
+ break
+ }
+
+ return e.complexity.Identity.EmailVerified(childComplexity), true
+ case "Identity.fullName":
+ if e.complexity.Identity.FullName == nil {
+ break
+ }
+
+ return e.complexity.Identity.FullName(childComplexity), true
+ case "Identity.id":
+ if e.complexity.Identity.ID == nil {
+ break
+ }
+
+ return e.complexity.Identity.ID(childComplexity), true
+ case "Identity.updatedAt":
+ if e.complexity.Identity.UpdatedAt == nil {
+ break
+ }
+
+ return e.complexity.Identity.UpdatedAt(childComplexity), true
+
case "Mutation.acceptNonDisclosureAgreement":
if e.complexity.Mutation.AcceptNonDisclosureAgreement == nil {
break
@@ -560,6 +614,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.complexity.Mutation.RequestTrustCenterFileAccess(childComplexity, args["input"].(types.RequestTrustCenterFileAccessInput)), true
+ case "Mutation.signInWithToken":
+ if e.complexity.Mutation.SignInWithToken == nil {
+ break
+ }
+
+ args, err := ec.field_Mutation_signInWithToken_args(ctx, rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Mutation.SignInWithToken(childComplexity, args["input"].(types.SignInWithTokenInput)), true
case "Organization.description":
if e.complexity.Organization.Description == nil {
@@ -657,6 +722,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.complexity.Query.TrustCenterBySlug(childComplexity, args["slug"].(string)), true
+ case "Query.viewer":
+ if e.complexity.Query.Viewer == nil {
+ break
+ }
+
+ return e.complexity.Query.Viewer(childComplexity), true
case "Report.filename":
if e.complexity.Report.Filename == nil {
@@ -690,6 +761,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.RequestAccessesPayload.TrustCenterAccess(childComplexity), true
+ case "SignInWithTokenPayload.success":
+ if e.complexity.SignInWithTokenPayload.Success == nil {
+ break
+ }
+
+ return e.complexity.SignInWithTokenPayload.Success(childComplexity), true
+
case "TrustCenter.active":
if e.complexity.TrustCenter.Active == nil {
break
@@ -1018,6 +1096,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputRequestDocumentAccessInput,
ec.unmarshalInputRequestReportAccessInput,
ec.unmarshalInputRequestTrustCenterFileAccessInput,
+ ec.unmarshalInputSignInWithTokenInput,
)
first := true
@@ -1151,6 +1230,15 @@ type PageInfo {
endCursor: CursorKey
}
+type Identity implements Node {
+ id: ID!
+ email: EmailAddr!
+ fullName: String!
+ emailVerified: Boolean!
+ createdAt: Datetime!
+ updatedAt: Datetime!
+}
+
type Organization implements Node {
id: ID!
name: String!
@@ -1652,10 +1740,18 @@ type TrustCenterAccess implements Node {
updatedAt: Datetime!
}
+input SignInWithTokenInput {
+ token: String!
+}
+
+type SignInWithTokenPayload {
+ success: Boolean!
+}
+
input RequestAllAccessesInput {
trustCenterId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
type RequestAccessesPayload {
@@ -1677,22 +1773,22 @@ input AcceptNonDisclosureAgreementInput {
input RequestDocumentAccessInput {
trustCenterId: ID!
documentId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
input RequestReportAccessInput {
trustCenterId: ID!
reportId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
input RequestTrustCenterFileAccessInput {
trustCenterId: ID!
trustCenterFileId: ID!
- email: EmailAddr
- name: String
+ email: EmailAddr!
+ fullName: String!
}
input ExportTrustCenterFileInput {
@@ -1716,12 +1812,15 @@ type AcceptNonDisclosureAgreementPayload {
}
type Query {
+ viewer: Identity
node(id: ID!): Node!
trustCenterBySlug(slug: String!): TrustCenter @mustBeAuthenticated(role: NONE)
currentTrustCenter: TrustCenter @mustBeAuthenticated(role: NONE)
}
type Mutation {
+ signInWithToken(input: SignInWithTokenInput!): SignInWithTokenPayload!
+
requestAllAccesses(input: RequestAllAccessesInput!): RequestAccessesPayload!
@mustBeAuthenticated(role: NONE)
@@ -1858,6 +1957,17 @@ func (ec *executionContext) field_Mutation_requestTrustCenterFileAccess_args(ctx
return args, nil
}
+func (ec *executionContext) field_Mutation_signInWithToken_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
+ var err error
+ args := map[string]any{}
+ arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNSignInWithTokenInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSignInWithTokenInput)
+ if err != nil {
+ return nil, err
+ }
+ args["input"] = arg0
+ return args, nil
+}
+
func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -2841,6 +2951,225 @@ func (ec *executionContext) fieldContext_Framework_darkLogoURL(_ context.Context
return fc, nil
}
+func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.CollectedField, obj *types.Identity) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Identity_id,
+ func(ctx context.Context) (any, error) {
+ return obj.ID, nil
+ },
+ nil,
+ ec.marshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_Identity_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Identity",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type ID does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.CollectedField, obj *types.Identity) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Identity_email,
+ func(ctx context.Context) (any, error) {
+ return obj.Email, nil
+ },
+ nil,
+ ec.marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_Identity_email(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Identity",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type EmailAddr does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Identity_fullName(ctx context.Context, field graphql.CollectedField, obj *types.Identity) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Identity_fullName,
+ func(ctx context.Context) (any, error) {
+ return obj.FullName, nil
+ },
+ nil,
+ ec.marshalNString2string,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_Identity_fullName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Identity",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Identity_emailVerified(ctx context.Context, field graphql.CollectedField, obj *types.Identity) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Identity_emailVerified,
+ func(ctx context.Context) (any, error) {
+ return obj.EmailVerified, nil
+ },
+ nil,
+ ec.marshalNBoolean2bool,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_Identity_emailVerified(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Identity",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type Boolean does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Identity_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Identity) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Identity_createdAt,
+ func(ctx context.Context) (any, error) {
+ return obj.CreatedAt, nil
+ },
+ nil,
+ ec.marshalNDatetime2timeᚐTime,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_Identity_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Identity",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type Datetime does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Identity_updatedAt(ctx context.Context, field graphql.CollectedField, obj *types.Identity) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Identity_updatedAt,
+ func(ctx context.Context) (any, error) {
+ return obj.UpdatedAt, nil
+ },
+ nil,
+ ec.marshalNDatetime2timeᚐTime,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_Identity_updatedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Identity",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type Datetime does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Mutation_signInWithToken(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Mutation_signInWithToken,
+ func(ctx context.Context) (any, error) {
+ fc := graphql.GetFieldContext(ctx)
+ return ec.resolvers.Mutation().SignInWithToken(ctx, fc.Args["input"].(types.SignInWithTokenInput))
+ },
+ nil,
+ ec.marshalNSignInWithTokenPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSignInWithTokenPayload,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_Mutation_signInWithToken(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Mutation",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "success":
+ return ec.fieldContext_SignInWithTokenPayload_success(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type SignInWithTokenPayload", field.Name)
+ },
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ err = ec.Recover(ctx, r)
+ ec.Error(ctx, err)
+ }
+ }()
+ ctx = graphql.WithFieldContext(ctx, fc)
+ if fc.Args, err = ec.field_Mutation_signInWithToken_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+ ec.Error(ctx, err)
+ return fc, err
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _Mutation_requestAllAccesses(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -3664,6 +3993,49 @@ func (ec *executionContext) fieldContext_PageInfo_endCursor(_ context.Context, f
return fc, nil
}
+func (ec *executionContext) _Query_viewer(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_Query_viewer,
+ func(ctx context.Context) (any, error) {
+ return ec.resolvers.Query().Viewer(ctx)
+ },
+ nil,
+ ec.marshalOIdentity2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐIdentity,
+ true,
+ false,
+ )
+}
+
+func (ec *executionContext) fieldContext_Query_viewer(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Query",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "id":
+ return ec.fieldContext_Identity_id(ctx, field)
+ case "email":
+ return ec.fieldContext_Identity_email(ctx, field)
+ case "fullName":
+ return ec.fieldContext_Identity_fullName(ctx, field)
+ case "emailVerified":
+ return ec.fieldContext_Identity_emailVerified(ctx, field)
+ case "createdAt":
+ return ec.fieldContext_Identity_createdAt(ctx, field)
+ case "updatedAt":
+ return ec.fieldContext_Identity_updatedAt(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type Identity", field.Name)
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _Query_node(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -4132,6 +4504,35 @@ func (ec *executionContext) fieldContext_RequestAccessesPayload_trustCenterAcces
return fc, nil
}
+func (ec *executionContext) _SignInWithTokenPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.SignInWithTokenPayload) (ret graphql.Marshaler) {
+ return graphql.ResolveField(
+ ctx,
+ ec.OperationContext,
+ field,
+ ec.fieldContext_SignInWithTokenPayload_success,
+ func(ctx context.Context) (any, error) {
+ return obj.Success, nil
+ },
+ nil,
+ ec.marshalNBoolean2bool,
+ true,
+ true,
+ )
+}
+
+func (ec *executionContext) fieldContext_SignInWithTokenPayload_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "SignInWithTokenPayload",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type Boolean does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _TrustCenter_id(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -7219,7 +7620,7 @@ func (ec *executionContext) unmarshalInputRequestAllAccessesInput(ctx context.Co
asMap[k] = v
}
- fieldsInOrder := [...]string{"trustCenterId", "email", "name"}
+ fieldsInOrder := [...]string{"trustCenterId", "email", "fullName"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -7235,18 +7636,18 @@ func (ec *executionContext) unmarshalInputRequestAllAccessesInput(ctx context.Co
it.TrustCenterID = data
case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
- data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
+ data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil {
return it, err
}
it.Email = data
- case "name":
- ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name"))
- data, err := ec.unmarshalOString2ᚖstring(ctx, v)
+ case "fullName":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
+ data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
- it.Name = data
+ it.FullName = data
}
}
@@ -7260,7 +7661,7 @@ func (ec *executionContext) unmarshalInputRequestDocumentAccessInput(ctx context
asMap[k] = v
}
- fieldsInOrder := [...]string{"trustCenterId", "documentId", "email", "name"}
+ fieldsInOrder := [...]string{"trustCenterId", "documentId", "email", "fullName"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -7283,18 +7684,18 @@ func (ec *executionContext) unmarshalInputRequestDocumentAccessInput(ctx context
it.DocumentID = data
case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
- data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
+ data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil {
return it, err
}
it.Email = data
- case "name":
- ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name"))
- data, err := ec.unmarshalOString2ᚖstring(ctx, v)
+ case "fullName":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
+ data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
- it.Name = data
+ it.FullName = data
}
}
@@ -7308,7 +7709,7 @@ func (ec *executionContext) unmarshalInputRequestReportAccessInput(ctx context.C
asMap[k] = v
}
- fieldsInOrder := [...]string{"trustCenterId", "reportId", "email", "name"}
+ fieldsInOrder := [...]string{"trustCenterId", "reportId", "email", "fullName"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -7331,18 +7732,18 @@ func (ec *executionContext) unmarshalInputRequestReportAccessInput(ctx context.C
it.ReportID = data
case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
- data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
+ data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil {
return it, err
}
it.Email = data
- case "name":
- ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name"))
- data, err := ec.unmarshalOString2ᚖstring(ctx, v)
+ case "fullName":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
+ data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
- it.Name = data
+ it.FullName = data
}
}
@@ -7356,7 +7757,7 @@ func (ec *executionContext) unmarshalInputRequestTrustCenterFileAccessInput(ctx
asMap[k] = v
}
- fieldsInOrder := [...]string{"trustCenterId", "trustCenterFileId", "email", "name"}
+ fieldsInOrder := [...]string{"trustCenterId", "trustCenterFileId", "email", "fullName"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -7379,18 +7780,45 @@ func (ec *executionContext) unmarshalInputRequestTrustCenterFileAccessInput(ctx
it.TrustCenterFileID = data
case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
- data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
+ data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil {
return it, err
}
it.Email = data
- case "name":
- ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name"))
- data, err := ec.unmarshalOString2ᚖstring(ctx, v)
+ case "fullName":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
+ data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
- it.Name = data
+ it.FullName = data
+ }
+ }
+
+ return it, nil
+}
+
+func (ec *executionContext) unmarshalInputSignInWithTokenInput(ctx context.Context, obj any) (types.SignInWithTokenInput, error) {
+ var it types.SignInWithTokenInput
+ asMap := map[string]any{}
+ for k, v := range obj.(map[string]any) {
+ asMap[k] = v
+ }
+
+ fieldsInOrder := [...]string{"token"}
+ for _, k := range fieldsInOrder {
+ v, ok := asMap[k]
+ if !ok {
+ continue
+ }
+ switch k {
+ case "token":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token"))
+ data, err := ec.unmarshalNString2string(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.Token = data
}
}
@@ -7454,6 +7882,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj
return graphql.Null
}
return ec._Organization(ctx, sel, obj)
+ case types.Identity:
+ return ec._Identity(ctx, sel, &obj)
+ case *types.Identity:
+ if obj == nil {
+ return graphql.Null
+ }
+ return ec._Identity(ctx, sel, obj)
case types.Framework:
return ec._Framework(ctx, sel, &obj)
case *types.Framework:
@@ -8155,6 +8590,70 @@ func (ec *executionContext) _Framework(ctx context.Context, sel ast.SelectionSet
return out
}
+var identityImplementors = []string{"Identity", "Node"}
+
+func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet, obj *types.Identity) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, identityImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ deferred := make(map[string]*graphql.FieldSet)
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("Identity")
+ case "id":
+ out.Values[i] = ec._Identity_id(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "email":
+ out.Values[i] = ec._Identity_email(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "fullName":
+ out.Values[i] = ec._Identity_fullName(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "emailVerified":
+ out.Values[i] = ec._Identity_emailVerified(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "createdAt":
+ out.Values[i] = ec._Identity_createdAt(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "updatedAt":
+ out.Values[i] = ec._Identity_updatedAt(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ default:
+ panic("unknown field " + strconv.Quote(field.Name))
+ }
+ }
+ out.Dispatch(ctx)
+ if out.Invalids > 0 {
+ return graphql.Null
+ }
+
+ atomic.AddInt32(&ec.deferred, int32(len(deferred)))
+
+ for label, dfs := range deferred {
+ ec.processDeferredGroup(graphql.DeferredGroup{
+ Label: label,
+ Path: graphql.GetPath(ctx),
+ FieldSet: dfs,
+ Context: ctx,
+ })
+ }
+
+ return out
+}
+
var mutationImplementors = []string{"Mutation"}
func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {
@@ -8174,6 +8673,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("Mutation")
+ case "signInWithToken":
+ out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
+ return ec._Mutation_signInWithToken(ctx, field)
+ })
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
case "requestAllAccesses":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
return ec._Mutation_requestAllAccesses(ctx, field)
@@ -8405,6 +8911,25 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("Query")
+ case "viewer":
+ field := field
+
+ innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Query_viewer(ctx, field)
+ return res
+ }
+
+ rrm := func(ctx context.Context) graphql.Marshaler {
+ return ec.OperationContext.RootResolverMiddleware(ctx,
+ func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
case "node":
field := field
@@ -8651,6 +9176,45 @@ func (ec *executionContext) _RequestAccessesPayload(ctx context.Context, sel ast
return out
}
+var signInWithTokenPayloadImplementors = []string{"SignInWithTokenPayload"}
+
+func (ec *executionContext) _SignInWithTokenPayload(ctx context.Context, sel ast.SelectionSet, obj *types.SignInWithTokenPayload) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, signInWithTokenPayloadImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ deferred := make(map[string]*graphql.FieldSet)
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("SignInWithTokenPayload")
+ case "success":
+ out.Values[i] = ec._SignInWithTokenPayload_success(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ default:
+ panic("unknown field " + strconv.Quote(field.Name))
+ }
+ }
+ out.Dispatch(ctx)
+ if out.Invalids > 0 {
+ return graphql.Null
+ }
+
+ atomic.AddInt32(&ec.deferred, int32(len(deferred)))
+
+ for label, dfs := range deferred {
+ ec.processDeferredGroup(graphql.DeferredGroup{
+ Label: label,
+ Path: graphql.GetPath(ctx),
+ FieldSet: dfs,
+ Context: ctx,
+ })
+ }
+
+ return out
+}
+
var trustCenterImplementors = []string{"TrustCenter", "Node"}
func (ec *executionContext) _TrustCenter(ctx context.Context, sel ast.SelectionSet, obj *types.TrustCenter) graphql.Marshaler {
@@ -10954,6 +11518,25 @@ func (ec *executionContext) unmarshalNRequestTrustCenterFileAccessInput2goᚗpro
return res, graphql.ErrorOnPath(ctx, err)
}
+func (ec *executionContext) unmarshalNSignInWithTokenInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSignInWithTokenInput(ctx context.Context, v any) (types.SignInWithTokenInput, error) {
+ res, err := ec.unmarshalInputSignInWithTokenInput(ctx, v)
+ return res, graphql.ErrorOnPath(ctx, err)
+}
+
+func (ec *executionContext) marshalNSignInWithTokenPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSignInWithTokenPayload(ctx context.Context, sel ast.SelectionSet, v types.SignInWithTokenPayload) graphql.Marshaler {
+ return ec._SignInWithTokenPayload(ctx, sel, &v)
+}
+
+func (ec *executionContext) marshalNSignInWithTokenPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSignInWithTokenPayload(ctx context.Context, sel ast.SelectionSet, v *types.SignInWithTokenPayload) graphql.Marshaler {
+ if v == nil {
+ if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+ graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
+ }
+ return graphql.Null
+ }
+ return ec._SignInWithTokenPayload(ctx, sel, v)
+}
+
func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) {
res, err := graphql.UnmarshalString(v)
return res, graphql.ErrorOnPath(ctx, err)
@@ -11583,22 +12166,11 @@ func (ec *executionContext) marshalOCursorKey2ᚖgoᚗproboᚗincᚋproboᚋpkg
return res
}
-func (ec *executionContext) unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, v any) (*mail.Addr, error) {
- if v == nil {
- return nil, nil
- }
- res, err := mail1.UnmarshalAddrScalar(v)
- return &res, graphql.ErrorOnPath(ctx, err)
-}
-
-func (ec *executionContext) marshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, sel ast.SelectionSet, v *mail.Addr) graphql.Marshaler {
+func (ec *executionContext) marshalOIdentity2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐIdentity(ctx context.Context, sel ast.SelectionSet, v *types.Identity) graphql.Marshaler {
if v == nil {
return graphql.Null
}
- _ = sel
- _ = ctx
- res := mail1.MarshalAddrScalar(*v)
- return res
+ return ec._Identity(ctx, sel, v)
}
func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v any) (*int, error) {
diff --git a/pkg/server/api/trust/v1/trust_center_access_handler.go b/pkg/server/api/trust/v1/trust_center_access_handler.go
deleted file mode 100644
index 5a23668dc..000000000
--- a/pkg/server/api/trust/v1/trust_center_access_handler.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// 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"
- "encoding/json"
- "fmt"
- "net/http"
- "time"
-
- "go.gearno.de/kit/httpserver"
- "go.probo.inc/probo/pkg/gid"
- "go.probo.inc/probo/pkg/probo"
- "go.probo.inc/probo/pkg/statelesstoken"
- "go.probo.inc/probo/pkg/trust"
-)
-
-type ctxKey struct {
- name string
-}
-
-var (
- CustomDomainOrganizationIDKey = &ctxKey{name: "custom_domain_organization_id"}
-)
-
-func GetCustomDomainOrganizationID(ctx context.Context) (gid.GID, bool) {
- organizationID, ok := ctx.Value(CustomDomainOrganizationIDKey).(gid.GID)
- return organizationID, ok
-}
-
-type (
- AuthTokenRequest struct {
- Token string `json:"token"`
- }
-
- AuthTokenResponse struct {
- Success bool `json:"success"`
- TrustCenterID string `json:"trust_center_id,omitempty"`
- Message string `json:"message,omitempty"`
- }
-)
-
-func authTokenHandler(trustSvc *trust.Service, trustAuthCfg TrustAuthConfig) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- var req AuthTokenRequest
- // Limit request body size to 1KB to prevent DoS attacks
- limitedReader := http.MaxBytesReader(w, r.Body, 1024)
- if err := json.NewDecoder(limitedReader).Decode(&req); err != nil {
- httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("cannot decode body: %w", err))
- return
- }
-
- if req.Token == "" {
- httpserver.RenderJSON(w, http.StatusBadRequest, AuthTokenResponse{
- Success: false,
- Message: "Token is required",
- })
- return
- }
-
- accessData, err := validateTrustCenterAccessToken(r.Context(), trustSvc, trustAuthCfg, req.Token)
- if err != nil {
- httpserver.RenderJSON(w, http.StatusUnauthorized, AuthTokenResponse{
- Success: false,
- Message: "Invalid or expired token",
- })
- return
- }
-
- tokenString, err := statelesstoken.NewToken(
- trustAuthCfg.TokenSecret,
- trustAuthCfg.TokenType,
- trustAuthCfg.TokenDuration,
- *accessData,
- )
- if err != nil {
- httpserver.RenderError(w, http.StatusInternalServerError, fmt.Errorf("cannot create token: %w", err))
- return
- }
-
- // Determine cookie domain: use custom domain if present, otherwise use configured domain
- cookieDomain := trustAuthCfg.CookieDomain
- if _, ok := GetCustomDomainOrganizationID(r.Context()); ok {
- // On custom domain, use the request host
- if r.TLS != nil && r.TLS.ServerName != "" {
- cookieDomain = r.TLS.ServerName
- }
- }
-
- cookie := &http.Cookie{
- Name: trustAuthCfg.CookieName,
- Value: tokenString,
- Domain: cookieDomain,
- Path: "/",
- MaxAge: int(trustAuthCfg.CookieDuration / time.Second),
- Secure: trustAuthCfg.CookieSecure,
- HttpOnly: true,
- SameSite: http.SameSiteStrictMode,
- }
- http.SetCookie(w, cookie)
-
- httpserver.RenderJSON(w, http.StatusOK, AuthTokenResponse{
- Success: true,
- TrustCenterID: accessData.TrustCenterID.String(),
- Message: "Authentication successful",
- })
- }
-}
-
-func validateTrustCenterAccessToken(ctx context.Context, trustSvc *trust.Service, trustAuthCfg TrustAuthConfig, tokenString string) (*probo.TrustCenterAccessData, error) {
- token, err := statelesstoken.ValidateToken[probo.TrustCenterAccessData](
- trustSvc.GetTokenSecret(),
- trustAuthCfg.TokenType,
- tokenString,
- )
- if err != nil {
- return nil, fmt.Errorf("cannot validate trust center access token: %w", err)
- }
-
- tenantSvc := trustSvc.WithTenant(token.Data.TrustCenterID.TenantID())
- if err := tenantSvc.TrustCenterAccesses.ValidateToken(ctx, token.Data.TrustCenterID, token.Data.Email); err != nil {
- return nil, fmt.Errorf("cannot validate trust center access token: %w", err)
- }
-
- return &token.Data, nil
-}
diff --git a/pkg/server/api/trust/v1/types/types.go b/pkg/server/api/trust/v1/types/types.go
index f63e07d83..543eeac3d 100644
--- a/pkg/server/api/trust/v1/types/types.go
+++ b/pkg/server/api/trust/v1/types/types.go
@@ -102,6 +102,18 @@ type Framework struct {
func (Framework) IsNode() {}
func (this Framework) GetID() gid.GID { return this.ID }
+type Identity struct {
+ ID gid.GID `json:"id"`
+ Email mail.Addr `json:"email"`
+ FullName string `json:"fullName"`
+ EmailVerified bool `json:"emailVerified"`
+ CreatedAt time.Time `json:"createdAt"`
+ UpdatedAt time.Time `json:"updatedAt"`
+}
+
+func (Identity) IsNode() {}
+func (this Identity) GetID() gid.GID { return this.ID }
+
type Mutation struct {
}
@@ -143,30 +155,38 @@ type RequestAccessesPayload struct {
}
type RequestAllAccessesInput struct {
- TrustCenterID gid.GID `json:"trustCenterId"`
- Email *mail.Addr `json:"email,omitempty"`
- Name *string `json:"name,omitempty"`
+ TrustCenterID gid.GID `json:"trustCenterId"`
+ Email mail.Addr `json:"email"`
+ FullName string `json:"fullName"`
}
type RequestDocumentAccessInput struct {
- TrustCenterID gid.GID `json:"trustCenterId"`
- DocumentID gid.GID `json:"documentId"`
- Email *mail.Addr `json:"email,omitempty"`
- Name *string `json:"name,omitempty"`
+ TrustCenterID gid.GID `json:"trustCenterId"`
+ DocumentID gid.GID `json:"documentId"`
+ Email mail.Addr `json:"email"`
+ FullName string `json:"fullName"`
}
type RequestReportAccessInput struct {
- TrustCenterID gid.GID `json:"trustCenterId"`
- ReportID gid.GID `json:"reportId"`
- Email *mail.Addr `json:"email,omitempty"`
- Name *string `json:"name,omitempty"`
+ TrustCenterID gid.GID `json:"trustCenterId"`
+ ReportID gid.GID `json:"reportId"`
+ Email mail.Addr `json:"email"`
+ FullName string `json:"fullName"`
}
type RequestTrustCenterFileAccessInput struct {
- TrustCenterID gid.GID `json:"trustCenterId"`
- TrustCenterFileID gid.GID `json:"trustCenterFileId"`
- Email *mail.Addr `json:"email,omitempty"`
- Name *string `json:"name,omitempty"`
+ TrustCenterID gid.GID `json:"trustCenterId"`
+ TrustCenterFileID gid.GID `json:"trustCenterFileId"`
+ Email mail.Addr `json:"email"`
+ FullName string `json:"fullName"`
+}
+
+type SignInWithTokenInput struct {
+ Token string `json:"token"`
+}
+
+type SignInWithTokenPayload struct {
+ Success bool `json:"success"`
}
type TrustCenter struct {
diff --git a/pkg/server/api/trust/v1/v1_resolver.go b/pkg/server/api/trust/v1/v1_resolver.go
index c3f7b721a..2e570ef5e 100644
--- a/pkg/server/api/trust/v1/v1_resolver.go
+++ b/pkg/server/api/trust/v1/v1_resolver.go
@@ -7,29 +7,37 @@ package trust_v1
import (
"context"
+ "encoding/base64"
+ "errors"
"fmt"
"time"
- "github.com/vektah/gqlparser/v2/gqlerror"
+ "go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
+ "go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/page"
+ "go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/trust/v1/schema"
"go.probo.inc/probo/pkg/server/api/trust/v1/types"
+ "go.probo.inc/probo/pkg/server/gqlutils"
+ "go.probo.inc/probo/pkg/trust"
)
// Framework is the resolver for the framework field.
func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types.Framework, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- audit, err := publicTrustService.Audits.Get(ctx, obj.ID)
+ audit, err := trustService.Audits.Get(ctx, obj.ID)
if err != nil {
- panic(fmt.Errorf("cannot load audit: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
- framework, err := publicTrustService.Frameworks.Get(ctx, audit.FrameworkID)
+ framework, err := trustService.Frameworks.Get(ctx, audit.FrameworkID)
if err != nil {
- panic(fmt.Errorf("cannot load framework: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
@@ -37,20 +45,22 @@ func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types
// Report is the resolver for the report field.
func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Report, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- audit, err := publicTrustService.Audits.Get(ctx, obj.ID)
+ audit, err := trustService.Audits.Get(ctx, obj.ID)
if err != nil {
- panic(fmt.Errorf("cannot load audit: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
if audit.ReportID == nil {
return nil, nil
}
- report, err := publicTrustService.Reports.Get(ctx, *audit.ReportID)
+ report, err := trustService.Reports.Get(ctx, *audit.ReportID)
if err != nil {
- panic(fmt.Errorf("cannot load report: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewReport(report), nil
@@ -58,610 +68,696 @@ func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Re
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Document) (bool, error) {
- // publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- // document, err := publicTrustService.Documents.Get(ctx, obj.ID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load document: %w", err))
- // }
+ document, err := trustService.Documents.Get(ctx, obj.ID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
+ return false, gqlutils.Internal(ctx)
+ }
- // if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // return true, nil
- // }
+ if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ return true, nil
+ }
- // privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- // if err != nil {
- // return false, nil
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return false, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
+ }
- // userData := connect_v1.IdentityFromContext(ctx)
- // if userData != nil {
- // return true, nil
- // }
+ trustCenter := TrustCenterFromContext(ctx)
+ documentAccess, err := trustService.TrustCenterAccesses.LoadDocumentAccess(
+ ctx,
+ trustCenter.ID,
+ identity.EmailAddress,
+ obj.ID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check document access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return false, nil
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // documentAccess, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
- // if err != nil {
- // return false, nil
- // }
-
- // return documentAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
- // }
-
- panic(fmt.Errorf("no user or token data found"))
+ return documentAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// HasUserRequestedAccess is the resolver for the hasUserRequestedAccess field.
func (r *documentResolver) HasUserRequestedAccess(ctx context.Context, obj *types.Document) (bool, error) {
- // privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- // if err != nil {
- // return false, nil
- // }
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return false, nil
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return false, nil // User is not authenticated, so no access requested
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // // Try to load document access - if it exists (regardless of active status), user has requested it
- // _, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
- // if err != nil {
- // return false, nil // No access requested or error
- // }
- // return true, nil // Access exists (requested)
- // }
-
- // return false, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ trustCenter := TrustCenterFromContext(ctx)
+ // Try to load document access - if it exists (regardless of active status), user has requested it
+ _, err := trustService.TrustCenterAccesses.LoadDocumentAccess(
+ ctx,
+ trustCenter.ID,
+ identity.EmailAddress,
+ obj.ID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check trust center file access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return false, nil
+ }
+ return true, nil // Access exists (requested)
}
// LightLogoURL is the resolver for the lightLogoURL field.
func (r *frameworkResolver) LightLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- return publicTrustService.Frameworks.GenerateLightLogoURL(ctx, obj.ID, 1*time.Hour)
+ return trustService.Frameworks.GenerateLightLogoURL(ctx, obj.ID, 1*time.Hour)
}
// DarkLogoURL is the resolver for the darkLogoURL field.
func (r *frameworkResolver) DarkLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
- privateTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- return privateTrustService.Frameworks.GenerateDarkLogoURL(ctx, obj.ID, 1*time.Hour)
+ return trustService.Frameworks.GenerateDarkLogoURL(ctx, obj.ID, 1*time.Hour)
+}
+
+// SignInWithToken is the resolver for the signInWithToken field.
+func (r *mutationResolver) SignInWithToken(ctx context.Context, input types.SignInWithTokenInput) (*types.SignInWithTokenPayload, error) {
+ _, session, err := r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
+ if err != nil {
+ var errInvalidToken *iam.ErrInvalidToken
+ if errors.As(err, &errInvalidToken) {
+ return nil, gqlutils.Invalid(ctx, err)
+ }
+
+ r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+
+ // TODO cookie domain
+ w := gqlutils.HTTPResponseWriterFromContext(ctx)
+ r.sessionCookie.Set(w, session)
+
+ return &types.SignInWithTokenPayload{
+ Success: true,
+ }, nil
}
// RequestAllAccesses is the resolver for the requestAllAccesses field.
func (r *mutationResolver) RequestAllAccesses(ctx context.Context, input types.RequestAllAccessesInput) (*types.RequestAccessesPayload, error) {
- // publicTrustService := r.PublicTrustService(ctx, input.TrustCenterID.TenantID())
+ trustService := r.TrustService(ctx, input.TrustCenterID.TenantID())
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return nil, fmt.Errorf("session users cannot request trust center access")
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ var err error
+ identity, err = r.iam.AuthService.LoadOrCreateIdentity(
+ ctx,
+ &iam.LoadOrCreateIdentityRequest{
+ Email: input.Email,
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load or create identity", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
- // email := input.Email
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // if email != nil || input.Name != nil {
- // return nil, fmt.Errorf("email and name are not allowed for authenticated users")
- // }
+ access, err := trustService.TrustCenterAccesses.Request(
+ ctx,
+ &trust.TrustCenterAccessRequest{
+ TrustCenterID: input.TrustCenterID,
+ Email: identity.EmailAddress,
+ FullName: identity.FullName,
+ DocumentIDs: nil,
+ ReportIDs: nil,
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot create trust center access", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // email = &tokenData.Email
- // }
- // if email == nil {
- // return nil, fmt.Errorf("email is required for unauthenticated users")
- // }
-
- // access, err := publicTrustService.TrustCenterAccesses.Request(ctx, &trust.TrustCenterAccessRequest{
- // TrustCenterID: input.TrustCenterID,
- // Email: *email,
- // Name: input.Name,
- // DocumentIDs: nil,
- // ReportIDs: nil,
- // })
- // if err != nil {
- // panic(fmt.Errorf("cannot create trust center access: %w", err))
- // }
-
- // return &types.RequestAccessesPayload{
- // TrustCenterAccess: &types.TrustCenterAccess{
- // ID: access.ID,
- // Email: access.Email,
- // Name: access.Name,
- // CreatedAt: access.CreatedAt,
- // UpdatedAt: access.UpdatedAt,
- // },
- // }, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.RequestAccessesPayload{
+ TrustCenterAccess: &types.TrustCenterAccess{
+ ID: access.ID,
+ Email: access.Email,
+ Name: access.Name,
+ CreatedAt: access.CreatedAt,
+ UpdatedAt: access.UpdatedAt,
+ },
+ }, nil
}
// ExportDocumentPDF is the resolver for the exportDocumentPDF field.
func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.ExportDocumentPDFInput) (*types.ExportDocumentPDFPayload, error) {
- // publicTrustService := r.PublicTrustService(ctx, input.DocumentID.TenantID())
+ trustService := r.TrustService(ctx, input.DocumentID.TenantID())
- // document, err := publicTrustService.Documents.Get(ctx, input.DocumentID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load document: %w", err))
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // pdf, err := publicTrustService.Documents.ExportPDFWithoutWatermark(ctx, input.DocumentID)
- // if err != nil {
- // panic(fmt.Errorf("cannot export document PDF: %w", err))
- // }
+ document, err := trustService.Documents.Get(ctx, input.DocumentID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.ExportDocumentPDFPayload{
- // Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
- // }, nil
- // }
+ if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ pdf, err := trustService.Documents.ExportPDFWithoutWatermark(ctx, input.DocumentID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot export document PDF", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // privateTrustService, err := r.PrivateTrustService(ctx, input.DocumentID.TenantID())
- // if err != nil {
- // panic(fmt.Errorf("cannot export document PDF: %w", err))
- // }
+ return &types.ExportDocumentPDFPayload{
+ Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
+ }, nil
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // ndaExists := true
- // hasAcceptedNDA := false
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return nil, gqlutils.Unauthenticated(ctx, errors.New("unauthenticated"))
+ }
- // trustCenter, _, err := privateTrustService.TrustCenters.Get(ctx, tokenData.TrustCenterID)
- // if err != nil {
- // panic(fmt.Errorf("cannot get trust center: %w", err))
- // }
- // if trustCenter.NonDisclosureAgreementFileID == nil {
- // ndaExists = false
- // }
+ ndaExists := true
+ hasAcceptedNDA := false
- // if ndaExists {
- // tokenData := TokenAccessFromContext(ctx)
- // hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.Email)
- // if err != nil {
- // panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
- // }
- // }
+ trustCenter, _, err := trustService.TrustCenters.Get(
+ ctx,
+ trustCenterInfo.ID,
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ if trustCenter.NonDisclosureAgreementFileID == nil {
+ ndaExists = false
+ }
- // documentAccess, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.Email, input.DocumentID)
- // if err != nil {
- // panic(fmt.Errorf("cannot check document access: %w", err))
- // }
+ if ndaExists {
+ hasAcceptedNDA, err = trustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(
+ ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot check if user has accepted NDA", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
- // if documentAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
- // return nil, fmt.Errorf("access denied: no permission to access this document")
- // }
+ documentAccess, err := trustService.TrustCenterAccesses.LoadDocumentAccess(
+ ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ input.DocumentID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check document access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return nil, nil
+ }
- // if ndaExists && !hasAcceptedNDA {
- // return nil, fmt.Errorf("user has not accepted NDA")
- // }
- // }
+ if documentAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
+ return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this document")
+ }
- // userData := IdentityFromContext(ctx)
- // var userEmail mail.Addr
- // if userData != nil {
- // userEmail = userData.EmailAddress
- // }
- // if tokenData != nil {
- // userEmail = tokenData.Email
- // }
+ if ndaExists && !hasAcceptedNDA {
+ return nil, gqlutils.Forbiddenf(ctx, "user has not accepted NDA")
+ }
- // pdf, err := privateTrustService.Documents.ExportPDF(ctx, input.DocumentID, userEmail)
- // if err != nil {
- // panic(fmt.Errorf("cannot export document PDF: %w", err))
- // }
+ pdf, err := trustService.Documents.ExportPDF(ctx, input.DocumentID, identity.EmailAddress)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot export document PDF", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.ExportDocumentPDFPayload{
- // Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
- // }, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.ExportDocumentPDFPayload{
+ Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
+ }, nil
}
// ExportReportPDF is the resolver for the exportReportPDF field.
func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.ExportReportPDFInput) (*types.ExportReportPDFPayload, error) {
- // publicTrustService := r.PublicTrustService(ctx, input.ReportID.TenantID())
+ trustService := r.TrustService(ctx, input.ReportID.TenantID())
- // audit, err := publicTrustService.Audits.GetByReportID(ctx, input.ReportID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load audit: %w", err))
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // pdf, err := publicTrustService.Reports.ExportPDFWithoutWatermark(ctx, input.ReportID)
- // if err != nil {
- // panic(fmt.Errorf("cannot export report PDF: %w", err))
- // }
+ audit, err := trustService.Audits.GetByReportID(ctx, input.ReportID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.ExportReportPDFPayload{
- // Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
- // }, nil
- // }
+ if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ pdf, err := trustService.Reports.ExportPDFWithoutWatermark(ctx, input.ReportID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot export report PDF", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // privateTrustService, err := r.PrivateTrustService(ctx, input.ReportID.TenantID())
- // if err != nil {
- // return nil, fmt.Errorf("cannot export report PDF: %w", err)
- // }
+ return &types.ExportReportPDFPayload{
+ Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
+ }, nil
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // ndaExists := true
- // hasAcceptedNDA := false
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
+ }
- // trustCenter, _, err := privateTrustService.TrustCenters.Get(ctx, tokenData.TrustCenterID)
- // if err != nil {
- // panic(fmt.Errorf("cannot get trust center: %w", err))
- // }
- // if trustCenter.NonDisclosureAgreementFileID == nil {
- // ndaExists = false
- // }
+ ndaExists := true
+ hasAcceptedNDA := false
- // if ndaExists {
- // hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.Email)
- // if err != nil {
- // panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
- // }
- // }
+ trustCenter, _, err := trustService.TrustCenters.Get(
+ ctx,
+ trustCenterInfo.ID,
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ if trustCenter.NonDisclosureAgreementFileID == nil {
+ ndaExists = false
+ }
- // reportAccess, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.Email, input.ReportID)
- // if err != nil {
- // panic(fmt.Errorf("cannot check report access: %w", err))
- // }
+ if ndaExists {
+ hasAcceptedNDA, err = trustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(
+ ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot check if user has accepted NDA", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
- // if reportAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
- // return nil, fmt.Errorf("access denied: no permission to access this report")
- // }
+ reportAccess, err := trustService.TrustCenterAccesses.LoadReportAccess(
+ ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ input.ReportID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return nil, nil
+ }
- // if ndaExists && !hasAcceptedNDA {
- // return nil, fmt.Errorf("user has not accepted NDA")
- // }
- // }
+ if reportAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
+ return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this report")
+ }
- // userData := IdentityFromContext(ctx)
- // var userEmail mail.Addr
- // if userData != nil {
- // userEmail = userData.EmailAddress
- // }
- // if tokenData != nil {
- // userEmail = tokenData.Email
- // }
+ if ndaExists && !hasAcceptedNDA {
+ return nil, gqlutils.Forbiddenf(ctx, "user has not accepted NDA")
+ }
- // pdf, err := privateTrustService.Reports.ExportPDF(ctx, input.ReportID, userEmail)
- // if err != nil {
- // panic(fmt.Errorf("cannot export report PDF: %w", err))
- // }
+ pdf, err := trustService.Reports.ExportPDF(ctx, input.ReportID, identity.EmailAddress)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot export report PDF", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.ExportReportPDFPayload{
- // Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
- // }, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.ExportReportPDFPayload{
+ Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
+ }, nil
}
// AcceptNonDisclosureAgreement is the resolver for the acceptNonDisclosureAgreement field.
func (r *mutationResolver) AcceptNonDisclosureAgreement(ctx context.Context, input types.AcceptNonDisclosureAgreementInput) (*types.AcceptNonDisclosureAgreementPayload, error) {
- // privateTrustService, err := r.PrivateTrustService(ctx, input.TrustCenterID.TenantID())
- // if err != nil {
- // return nil, fmt.Errorf("cannot accept NDA: %w", err)
- // }
+ trustService := r.TrustService(ctx, input.TrustCenterID.TenantID())
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData == nil {
- // return nil, fmt.Errorf("token not found")
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
+ }
- // err = privateTrustService.TrustCenterAccesses.AcceptNonDisclosureAgreement(ctx, input.TrustCenterID, tokenData.Email)
- // if err != nil {
- // return nil, fmt.Errorf("cannot accept NDA: %w", err)
- // }
+ if err := trustService.TrustCenterAccesses.AcceptNonDisclosureAgreement(ctx, input.TrustCenterID, identity.EmailAddress); err != nil {
+ r.logger.ErrorCtx(ctx, "cannot accept NDA", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.AcceptNonDisclosureAgreementPayload{Success: true}, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.AcceptNonDisclosureAgreementPayload{Success: true}, nil
}
// RequestDocumentAccess is the resolver for the requestDocumentAccess field.
func (r *mutationResolver) RequestDocumentAccess(ctx context.Context, input types.RequestDocumentAccessInput) (*types.RequestAccessesPayload, error) {
- // publicTrustService := r.PublicTrustService(ctx, input.TrustCenterID.TenantID())
+ trustService := r.TrustService(ctx, input.TrustCenterID.TenantID())
- // var (
- // email mail.Addr
- // fullname string
- // )
+ document, err := trustService.Documents.Get(ctx, input.DocumentID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ return nil, gqlutils.Invalidf(
+ ctx,
+ "document is publicly available and does not require access request",
+ )
+ }
- // identity := connect_v1.IdentityFromContext(ctx)
- // if identity != nil {
- // email = identity.EmailAddress
- // fullname = identity.FullName
- // } else if input.Email != nil && input.Name != nil {
- // email = *input.Email
- // fullname = *input.Name
- // } else {
- // return nil, gqlutils.Invalid(fmt.Errorf("email and name are required"), nil)
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ var err error
+ identity, err = r.iam.AuthService.LoadOrCreateIdentity(
+ ctx,
+ &iam.LoadOrCreateIdentityRequest{
+ Email: input.Email,
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load or create identity", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
- // access, err := publicTrustService.TrustCenterAccesses.Request(
- // ctx,
- // &trust.TrustCenterAccessRequest{
- // TrustCenterID: input.TrustCenterID,
- // Email: email,
- // Name: fullname,
- // DocumentIDs: []gid.GID{input.DocumentID},
- // ReportIDs: []gid.GID{},
- // },
- // )
- // if err != nil {
- // panic(fmt.Errorf("cannot request document access: %w", err))
- // }
+ access, err := trustService.TrustCenterAccesses.Request(
+ ctx,
+ &trust.TrustCenterAccessRequest{
+ TrustCenterID: input.TrustCenterID,
+ Email: identity.EmailAddress,
+ FullName: identity.FullName,
+ DocumentIDs: []gid.GID{input.DocumentID},
+ ReportIDs: []gid.GID{},
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot request document access", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.RequestAccessesPayload{
- // TrustCenterAccess: &types.TrustCenterAccess{
- // ID: access.ID,
- // Email: access.Email,
- // Name: access.Name,
- // CreatedAt: access.CreatedAt,
- // UpdatedAt: access.UpdatedAt,
- // },
- // }, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.RequestAccessesPayload{
+ TrustCenterAccess: &types.TrustCenterAccess{
+ ID: access.ID,
+ Email: access.Email,
+ Name: access.Name,
+ CreatedAt: access.CreatedAt,
+ UpdatedAt: access.UpdatedAt,
+ },
+ }, nil
}
// RequestReportAccess is the resolver for the requestReportAccess field.
func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types.RequestReportAccessInput) (*types.RequestAccessesPayload, error) {
- // publicTrustService := r.PublicTrustService(ctx, input.TrustCenterID.TenantID())
+ trustService := r.TrustService(ctx, input.TrustCenterID.TenantID())
- // audit, err := publicTrustService.Audits.GetByReportID(ctx, input.ReportID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load audit: %w", err))
- // }
+ audit, err := trustService.Audits.GetByReportID(ctx, input.ReportID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // return nil, fmt.Errorf("report is publicly available and does not require access request")
- // }
+ if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ return nil, gqlutils.Invalidf(
+ ctx,
+ "report is publicly available and does not require access request",
+ )
+ }
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return nil, fmt.Errorf("session users cannot request trust center access")
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ var err error
+ identity, err = r.iam.AuthService.LoadOrCreateIdentity(
+ ctx,
+ &iam.LoadOrCreateIdentityRequest{
+ Email: input.Email,
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load or create identity", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
- // email := input.Email
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // email = &tokenData.Email
- // }
- // if email == nil {
- // return nil, fmt.Errorf("email is required for unauthenticated users")
- // }
+ access, err := trustService.TrustCenterAccesses.Request(
+ ctx,
+ &trust.TrustCenterAccessRequest{
+ TrustCenterID: input.TrustCenterID,
+ Email: identity.EmailAddress,
+ FullName: identity.FullName,
+ DocumentIDs: []gid.GID{},
+ ReportIDs: []gid.GID{input.ReportID},
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot request report access", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // access, err := publicTrustService.TrustCenterAccesses.Request(ctx, &trust.TrustCenterAccessRequest{
- // TrustCenterID: input.TrustCenterID,
- // Email: *email,
- // Name: input.Name,
- // DocumentIDs: []gid.GID{},
- // ReportIDs: []gid.GID{input.ReportID},
- // })
- // if err != nil {
- // panic(fmt.Errorf("cannot request report access: %w", err))
- // }
-
- // return &types.RequestAccessesPayload{
- // TrustCenterAccess: &types.TrustCenterAccess{
- // ID: access.ID,
- // Email: access.Email,
- // Name: access.Name,
- // CreatedAt: access.CreatedAt,
- // UpdatedAt: access.UpdatedAt,
- // },
- // }, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.RequestAccessesPayload{
+ TrustCenterAccess: &types.TrustCenterAccess{
+ ID: access.ID,
+ Email: access.Email,
+ Name: access.Name,
+ CreatedAt: access.CreatedAt,
+ UpdatedAt: access.UpdatedAt,
+ },
+ }, nil
}
// RequestTrustCenterFileAccess is the resolver for the requestTrustCenterFileAccess field.
func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, input types.RequestTrustCenterFileAccessInput) (*types.RequestAccessesPayload, error) {
- // publicTrustService := r.PublicTrustService(ctx, input.TrustCenterID.TenantID())
+ trustService := r.TrustService(ctx, input.TrustCenterID.TenantID())
- // trustCenterFile, err := publicTrustService.TrustCenterFiles.Get(ctx, input.TrustCenterFileID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load trust center file: %w", err))
- // }
+ trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, input.TrustCenterFileID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // return nil, fmt.Errorf("trust center file is publicly available and does not require access request")
- // }
+ if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ return nil, gqlutils.Invalidf(
+ ctx,
+ "trust center file is publicly available and does not require access request",
+ )
+ }
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return nil, fmt.Errorf("session users cannot request trust center access")
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ var err error
+ identity, err = r.iam.AuthService.LoadOrCreateIdentity(
+ ctx,
+ &iam.LoadOrCreateIdentityRequest{
+ Email: input.Email,
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load or create identity", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
- // email := input.Email
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // email = &tokenData.Email
- // }
- // if email == nil {
- // return nil, fmt.Errorf("email is required for unauthenticated users")
- // }
+ access, err := trustService.TrustCenterAccesses.Request(
+ ctx,
+ &trust.TrustCenterAccessRequest{
+ TrustCenterID: input.TrustCenterID,
+ Email: identity.EmailAddress,
+ FullName: identity.FullName,
+ DocumentIDs: []gid.GID{},
+ ReportIDs: []gid.GID{},
+ TrustCenterFileIDs: []gid.GID{input.TrustCenterFileID},
+ },
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot request trust center file access", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // access, err := publicTrustService.TrustCenterAccesses.Request(ctx, &trust.TrustCenterAccessRequest{
- // TrustCenterID: input.TrustCenterID,
- // Email: *email,
- // Name: input.Name,
- // DocumentIDs: []gid.GID{},
- // ReportIDs: []gid.GID{},
- // TrustCenterFileIDs: []gid.GID{input.TrustCenterFileID},
- // })
- // if err != nil {
- // panic(fmt.Errorf("cannot request trust center file access: %w", err))
- // }
-
- // return &types.RequestAccessesPayload{
- // TrustCenterAccess: &types.TrustCenterAccess{
- // ID: access.ID,
- // Email: access.Email,
- // Name: access.Name,
- // CreatedAt: access.CreatedAt,
- // UpdatedAt: access.UpdatedAt,
- // },
- // }, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.RequestAccessesPayload{
+ TrustCenterAccess: &types.TrustCenterAccess{
+ ID: access.ID,
+ Email: access.Email,
+ Name: access.Name,
+ CreatedAt: access.CreatedAt,
+ UpdatedAt: access.UpdatedAt,
+ },
+ }, nil
}
// ExportTrustCenterFile is the resolver for the exportTrustCenterFile field.
func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input types.ExportTrustCenterFileInput) (*types.ExportTrustCenterFilePayload, error) {
- // publicTrustService := r.PublicTrustService(ctx, input.TrustCenterFileID.TenantID())
+ trustService := r.TrustService(ctx, input.TrustCenterFileID.TenantID())
- // trustCenterFile, err := publicTrustService.TrustCenterFiles.Get(ctx, input.TrustCenterFileID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load trust center file: %w", err))
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // fileData, err := publicTrustService.TrustCenterFiles.ExportFileWithoutWatermark(ctx, input.TrustCenterFileID)
- // if err != nil {
- // panic(fmt.Errorf("cannot export trust center file: %w", err))
- // }
+ trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, input.TrustCenterFileID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.ExportTrustCenterFilePayload{
- // Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(fileData)),
- // }, nil
- // }
+ if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ fileData, err := trustService.TrustCenterFiles.ExportFileWithoutWatermark(ctx, input.TrustCenterFileID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot export trust center file", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // privateTrustService, err := r.PrivateTrustService(ctx, input.TrustCenterFileID.TenantID())
- // if err != nil {
- // return nil, fmt.Errorf("cannot export trust center file: %w", err)
- // }
+ return &types.ExportTrustCenterFilePayload{
+ Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(fileData)),
+ }, nil
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // ndaExists := true
- // hasAcceptedNDA := false
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
+ }
- // trustCenter, _, err := privateTrustService.TrustCenters.Get(ctx, tokenData.TrustCenterID)
- // if err != nil {
- // panic(fmt.Errorf("cannot get trust center: %w", err))
- // }
- // if trustCenter.NonDisclosureAgreementFileID == nil {
- // ndaExists = false
- // }
+ ndaExists := true
+ hasAcceptedNDA := false
- // if ndaExists {
- // hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.Email)
- // if err != nil {
- // panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
- // }
- // }
+ trustCenter, _, err := trustService.TrustCenters.Get(
+ ctx,
+ trustCenterInfo.ID,
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ if trustCenter.NonDisclosureAgreementFileID == nil {
+ ndaExists = false
+ }
- // fileAccess, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.Email, input.TrustCenterFileID)
- // if err != nil {
- // panic(fmt.Errorf("cannot check trust center file access: %w", err))
- // }
+ if ndaExists {
+ hasAcceptedNDA, err = trustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ )
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot check if user has accepted NDA", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
+ }
- // if fileAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
- // return nil, fmt.Errorf("access denied: no permission to access this file")
- // }
+ fileAccess, err := trustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ input.TrustCenterFileID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check trust center file access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return nil, nil
+ }
- // if ndaExists && !hasAcceptedNDA {
- // return nil, fmt.Errorf("user has not accepted NDA")
- // }
- // }
+ if fileAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
+ return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this file")
+ }
- // userData := IdentityFromContext(ctx)
- // var userEmail mail.Addr
- // if userData != nil {
- // userEmail = userData.EmailAddress
- // }
- // if tokenData != nil {
- // userEmail = tokenData.Email
- // }
+ if ndaExists && !hasAcceptedNDA {
+ return nil, gqlutils.Forbiddenf(ctx, "user has not accepted NDA")
+ }
- // fileData, err := privateTrustService.TrustCenterFiles.ExportFile(ctx, input.TrustCenterFileID, userEmail)
- // if err != nil {
- // panic(fmt.Errorf("cannot export trust center file: %w", err))
- // }
+ fileData, err := trustService.TrustCenterFiles.ExportFile(ctx, input.TrustCenterFileID, identity.EmailAddress)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot export trust center file", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
+ }
- // return &types.ExportTrustCenterFilePayload{
- // Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(fileData)),
- // }, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return &types.ExportTrustCenterFilePayload{
+ Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(fileData)),
+ }, nil
}
// LogoURL is the resolver for the logoUrl field.
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- return publicTrustService.Organizations.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
+ return trustService.Organizations.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
+}
+
+// Viewer is the resolver for the viewer field.
+func (r *queryResolver) Viewer(ctx context.Context) (*types.Identity, error) {
+ identity := authn.IdentityFromContext(ctx)
+
+ if identity == nil {
+ return nil, nil
+ }
+
+ return &types.Identity{
+ ID: identity.ID,
+ Email: identity.EmailAddress,
+ FullName: identity.FullName,
+ EmailVerified: identity.EmailAddressVerified,
+ CreatedAt: identity.CreatedAt,
+ UpdatedAt: identity.UpdatedAt,
+ }, nil
}
// Node is the resolver for the node field.
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
- publicTrustService := r.PublicTrustService(ctx, id.TenantID())
+ trustService := r.TrustService(ctx, id.TenantID())
switch id.EntityType() {
case coredata.OrganizationEntityType:
- organization, err := publicTrustService.Organizations.Get(ctx, id)
+ organization, err := trustService.Organizations.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get organization: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
case coredata.DocumentEntityType:
- document, err := publicTrustService.Documents.Get(ctx, id)
+ document, err := trustService.Documents.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get document: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get document", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewDocument(document), nil
case coredata.FrameworkEntityType:
- framework, err := publicTrustService.Frameworks.Get(ctx, id)
+ framework, err := trustService.Frameworks.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get framework: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get framework", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
case coredata.ReportEntityType:
- report, err := publicTrustService.Reports.Get(ctx, id)
+ report, err := trustService.Reports.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get report: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get report", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewReport(report), nil
case coredata.AuditEntityType:
- audit, err := publicTrustService.Audits.Get(ctx, id)
+ audit, err := trustService.Audits.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get audit: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get audit", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewAudit(audit), nil
case coredata.VendorEntityType:
- vendor, err := publicTrustService.Vendors.Get(ctx, id)
+ vendor, err := trustService.Vendors.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get vendor: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewVendor(vendor), nil
case coredata.TrustCenterEntityType:
- trustCenter, file, err := publicTrustService.TrustCenters.Get(ctx, id)
+ trustCenter, file, err := trustService.TrustCenters.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get trust center: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenter(trustCenter, file), nil
case coredata.TrustCenterReferenceEntityType:
- reference, err := publicTrustService.TrustCenterReferences.Get(ctx, id)
+ reference, err := trustService.TrustCenterReferences.Get(ctx, id)
if err != nil {
- panic(fmt.Errorf("cannot get trust center reference: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot get trust center reference", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterReference(reference), nil
default:
- return nil, gqlerror.Errorf("node %q not found", id)
+ return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
}
}
@@ -678,13 +774,13 @@ func (r *queryResolver) TrustCenterBySlug(ctx context.Context, slug string) (*ty
return nil, nil
}
- publicTrustService := r.PublicTrustService(ctx, trustCenter.TenantID)
- trustCenter, file, err := publicTrustService.TrustCenters.Get(ctx, trustCenter.ID)
+ trustService := r.TrustService(ctx, trustCenter.TenantID)
+ trustCenter, file, err := trustService.TrustCenters.Get(ctx, trustCenter.ID)
if err != nil {
panic(fmt.Errorf("cannot get trust center: %w", err))
}
- org, err := publicTrustService.Organizations.Get(ctx, trustCenter.OrganizationID)
+ org, err := trustService.Organizations.Get(ctx, trustCenter.OrganizationID)
if err != nil {
panic(fmt.Errorf("cannot get organization: %w", err))
}
@@ -696,118 +792,97 @@ func (r *queryResolver) TrustCenterBySlug(ctx context.Context, slug string) (*ty
// CurrentTrustCenter is the resolver for the currentTrustCenter field.
func (r *queryResolver) CurrentTrustCenter(ctx context.Context) (*types.TrustCenter, error) {
- // // Get organization and tenant from custom domain context
- // organizationID, ok := GetCustomDomainOrganizationID(ctx)
- // if !ok {
- // return nil, fmt.Errorf("organization not found for custom domain")
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // tenantID, ok := GetCustomDomainTenantID(ctx)
- // if !ok {
- // return nil, fmt.Errorf("tenant not found for custom domain")
- // }
+ trustService := r.TrustService(ctx, trustCenterInfo.ID.TenantID())
- // publicTrustService := r.PublicTrustService(ctx, tenantID)
+ org, err := trustService.Organizations.Get(ctx, trustCenterInfo.OrganizationID)
+ if err != nil {
+ panic(fmt.Errorf("cannot get organization: %w", err))
+ }
- // trustCenter, err := publicTrustService.TrustCenters.GetByOrganizationID(ctx, organizationID)
- // if err != nil {
- // return nil, fmt.Errorf("cannot load trust center: %w", err)
- // }
+ trustCenter, file, err := trustService.TrustCenters.Get(ctx, trustCenterInfo.ID)
+ if err != nil {
+ panic(fmt.Errorf("cannot get trust center: %w", err))
+ }
- // if !trustCenter.Active {
- // return nil, nil
- // }
+ response := types.NewTrustCenter(trustCenter, file)
+ response.Organization = types.NewOrganization(org)
- // trustCenter, file, err := publicTrustService.TrustCenters.Get(ctx, trustCenter.ID)
- // if err != nil {
- // panic(fmt.Errorf("cannot get trust center: %w", err))
- // }
-
- // org, err := publicTrustService.Organizations.Get(ctx, organizationID)
- // if err != nil {
- // panic(fmt.Errorf("cannot get organization: %w", err))
- // }
- // response := types.NewTrustCenter(trustCenter, file)
- // response.Organization = types.NewOrganization(org)
-
- // return response, nil
-
- panic(fmt.Errorf("no user or token data found"))
+ return response, nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report) (bool, error) {
- // publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- // audit, err := publicTrustService.Audits.GetByReportID(ctx, obj.ID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load document: %w", err))
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // return true, nil
- // }
+ audit, err := trustService.Audits.GetByReportID(ctx, obj.ID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
+ return false, gqlutils.Internal(ctx)
+ }
- // privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- // if err != nil {
- // return false, nil
- // }
+ if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ return true, nil
+ }
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return true, nil
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return false, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // reportAccess, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
- // if err != nil {
- // return false, nil
- // }
+ reportAccess, err := trustService.TrustCenterAccesses.LoadReportAccess(ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ obj.ID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return false, nil
+ }
- // return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
- // }
-
- // panic(fmt.Errorf("no user or token data found"))
-
- panic(fmt.Errorf("no user or token data found"))
+ return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// HasUserRequestedAccess is the resolver for the hasUserRequestedAccess field.
func (r *reportResolver) HasUserRequestedAccess(ctx context.Context, obj *types.Report) (bool, error) {
- // privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- // if err != nil {
- // return false, nil
- // }
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return false, nil
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // _, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
- // if err != nil {
- // return false, nil
- // }
- // return true, nil
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return false, nil // User is not authenticated, so no access requested
+ }
- // return false, nil
+ _, err := trustService.TrustCenterAccesses.LoadReportAccess(ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ obj.ID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return false, nil
+ }
- panic(fmt.Errorf("no user or token data found"))
+ return true, nil
}
// NdaFileURL is the resolver for the ndaFileUrl field.
func (r *trustCenterResolver) NdaFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
- privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- if err != nil {
- return nil, nil
- }
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- fileURL, err := privateTrustService.TrustCenters.GenerateNDAFileURL(ctx, obj.ID, 15*time.Minute)
+ fileURL, err := trustService.TrustCenters.GenerateNDAFileURL(ctx, obj.ID, 15*time.Minute)
if err != nil {
+ // FIXME: add error not found check etc
+ // r.logger.ErrorCtx(ctx, "cannot generate NDA file URL", log.Error(err))
+ // return nil, gqlutils.Internal(ctx)
return nil, nil
}
@@ -821,43 +896,32 @@ func (r *trustCenterResolver) Organization(ctx context.Context, obj *types.Trust
// IsUserAuthenticated is the resolver for the isUserAuthenticated field.
func (r *trustCenterResolver) IsUserAuthenticated(ctx context.Context, obj *types.TrustCenter) (bool, error) {
- _, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- if err != nil {
- return false, nil
- }
+ identity := authn.IdentityFromContext(ctx)
- return true, nil
+ return identity != nil, nil
}
// HasAcceptedNonDisclosureAgreement is the resolver for the hasAcceptedNonDisclosureAgreement field.
func (r *trustCenterResolver) HasAcceptedNonDisclosureAgreement(ctx context.Context, obj *types.TrustCenter) (bool, error) {
- // privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- // if err != nil {
- // return false, nil
- // }
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- // userData := IdentityFromContext(ctx)
- // if userData != nil {
- // return true, nil
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return false, nil // User is not authenticated, so no NDA accepted
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // hasAcceptedNDA, err := privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, obj.ID, tokenData.Email)
- // if err != nil {
- // panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
- // }
- // return hasAcceptedNDA, nil
- // }
+ hasAcceptedNDA, err := trustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, obj.ID, identity.EmailAddress)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot check if user has accepted NDA", log.Error(err))
+ return false, gqlutils.Internal(ctx)
+ }
- // panic(fmt.Errorf("no user or token data found"))
-
- panic(fmt.Errorf("no user or token data found"))
+ return hasAcceptedNDA, nil
}
// Documents is the resolver for the documents field.
func (r *trustCenterResolver) Documents(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.DocumentConnection, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldTitle,
@@ -865,9 +929,10 @@ func (r *trustCenterResolver) Documents(ctx context.Context, obj *types.TrustCen
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
- documentPage, err := publicTrustService.Documents.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
+ documentPage, err := trustService.Documents.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
if err != nil {
- panic(fmt.Errorf("cannot list public documents: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot list public documents", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewDocumentConnection(documentPage), nil
@@ -875,7 +940,7 @@ func (r *trustCenterResolver) Documents(ctx context.Context, obj *types.TrustCen
// Audits is the resolver for the audits field.
func (r *trustCenterResolver) Audits(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.AuditConnection, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.AuditOrderField]{
Field: coredata.AuditOrderFieldValidFrom,
@@ -883,9 +948,10 @@ func (r *trustCenterResolver) Audits(ctx context.Context, obj *types.TrustCenter
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
- auditPage, err := publicTrustService.Audits.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
+ auditPage, err := trustService.Audits.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
if err != nil {
- panic(fmt.Errorf("cannot list public audits: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot list public audits", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewAuditConnection(auditPage), nil
@@ -893,7 +959,7 @@ func (r *trustCenterResolver) Audits(ctx context.Context, obj *types.TrustCenter
// Vendors is the resolver for the vendors field.
func (r *trustCenterResolver) Vendors(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.VendorConnection, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.VendorOrderField]{
Field: coredata.VendorOrderFieldName,
@@ -901,9 +967,10 @@ func (r *trustCenterResolver) Vendors(ctx context.Context, obj *types.TrustCente
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
- vendorPage, err := publicTrustService.Vendors.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
+ vendorPage, err := trustService.Vendors.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
if err != nil {
- panic(fmt.Errorf("cannot list public vendors: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot list public vendors", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewVendorConnection(vendorPage), nil
@@ -911,7 +978,7 @@ func (r *trustCenterResolver) Vendors(ctx context.Context, obj *types.TrustCente
// References is the resolver for the references field.
func (r *trustCenterResolver) References(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterReferenceConnection, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.TrustCenterReferenceOrderField]{
Field: coredata.TrustCenterReferenceOrderFieldRank,
@@ -919,9 +986,10 @@ func (r *trustCenterResolver) References(ctx context.Context, obj *types.TrustCe
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
- referencePage, err := publicTrustService.TrustCenterReferences.ListForTrustCenterID(ctx, obj.ID, cursor)
+ referencePage, err := trustService.TrustCenterReferences.ListForTrustCenterID(ctx, obj.ID, cursor)
if err != nil {
- panic(fmt.Errorf("cannot list public trust center references: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot list public trust center references", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterReferenceConnection(referencePage), nil
@@ -929,7 +997,7 @@ func (r *trustCenterResolver) References(ctx context.Context, obj *types.TrustCe
// TrustCenterFiles is the resolver for the trustCenterFiles field.
func (r *trustCenterResolver) TrustCenterFiles(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterFileConnection, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.TrustCenterFileOrderField]{
Field: coredata.TrustCenterFileOrderFieldName,
@@ -937,9 +1005,10 @@ func (r *trustCenterResolver) TrustCenterFiles(ctx context.Context, obj *types.T
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
- trustCenterFilePage, err := publicTrustService.TrustCenterFiles.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
+ trustCenterFilePage, err := trustService.TrustCenterFiles.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
if err != nil {
- panic(fmt.Errorf("cannot list public trust center files: %w", err))
+ r.logger.ErrorCtx(ctx, "cannot list public trust center files", log.Error(err))
+ return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterFileConnection(trustCenterFilePage), nil
@@ -947,73 +1016,71 @@ func (r *trustCenterResolver) TrustCenterFiles(ctx context.Context, obj *types.T
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *types.TrustCenterFile) (bool, error) {
- // publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- // trustCenterFile, err := publicTrustService.TrustCenterFiles.Get(ctx, obj.ID)
- // if err != nil {
- // panic(fmt.Errorf("cannot load trust center file: %w", err))
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
- // return true, nil
- // }
+ trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, obj.ID)
+ if err != nil {
+ r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
+ return false, gqlutils.Internal(ctx)
+ }
- // privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- // if err != nil {
- // return false, nil
- // }
+ if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
+ return true, nil
+ }
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return true, nil
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return false, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
+ }
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // fileAccess, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
- // if err != nil {
- // return false, nil
- // }
+ fileAccess, err := trustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ obj.ID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check trust center file access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return false, nil
+ }
- // return fileAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
- // }
-
- // panic(fmt.Errorf("no user or token data found"))
-
- panic(fmt.Errorf("no user or token data found"))
+ return fileAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// HasUserRequestedAccess is the resolver for the hasUserRequestedAccess field.
func (r *trustCenterFileResolver) HasUserRequestedAccess(ctx context.Context, obj *types.TrustCenterFile) (bool, error) {
- // privateTrustService, err := r.PrivateTrustService(ctx, obj.ID.TenantID())
- // if err != nil {
- // return false, nil
- // }
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- // userData := r.IdentityFromContext(ctx)
- // if userData != nil {
- // return false, nil
- // }
+ trustCenterInfo := TrustCenterFromContext(ctx)
- // tokenData := TokenAccessFromContext(ctx)
- // if tokenData != nil {
- // _, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
- // if err != nil {
- // return false, nil
- // }
- // return true, nil
- // }
+ identity := authn.IdentityFromContext(ctx)
+ if identity == nil {
+ return false, nil // User is not authenticated, so no access requested
+ }
- // return false, nil
+ _, err := trustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx,
+ trustCenterInfo.ID,
+ identity.EmailAddress,
+ obj.ID,
+ )
+ if err != nil {
+ // FIXME check for not found and return without error in this case
+ // r.logger.ErrorCtx(ctx, "cannot check trust center file access", log.Error(err))
+ // return false, gqlutils.Internal(ctx)
+ return false, nil
+ }
- panic(fmt.Errorf("no user or token data found"))
+ return true, nil
}
// LogoURL is the resolver for the logoUrl field.
func (r *trustCenterReferenceResolver) LogoURL(ctx context.Context, obj *types.TrustCenterReference) (string, error) {
- publicTrustService := r.PublicTrustService(ctx, obj.ID.TenantID())
+ trustService := r.TrustService(ctx, obj.ID.TenantID())
- logoURL, err := publicTrustService.TrustCenterReferences.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
+ logoURL, err := trustService.TrustCenterReferences.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
panic(fmt.Errorf("cannot generate logo URL: %w", err))
}
diff --git a/pkg/server/gqlutils/errors.go b/pkg/server/gqlutils/errors.go
index 071e017cf..42cdb82ab 100644
--- a/pkg/server/gqlutils/errors.go
+++ b/pkg/server/gqlutils/errors.go
@@ -58,6 +58,10 @@ func Forbidden(ctx context.Context, err error) *gqlerror.Error {
}
}
+func Forbiddenf(ctx context.Context, format string, a ...any) *gqlerror.Error {
+ return Forbidden(ctx, fmt.Errorf(format, a...))
+}
+
func NotFound(ctx context.Context, err error) *gqlerror.Error {
return &gqlerror.Error{
Message: err.Error(),
@@ -68,6 +72,10 @@ func NotFound(ctx context.Context, err error) *gqlerror.Error {
}
}
+func NotFoundf(ctx context.Context, format string, a ...any) *gqlerror.Error {
+ return NotFound(ctx, fmt.Errorf(format, a...))
+}
+
func Conflict(ctx context.Context, err error) *gqlerror.Error {
return &gqlerror.Error{
Message: err.Error(),
diff --git a/pkg/server/server.go b/pkg/server/server.go
index 7abb1a7e4..55f1866aa 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -15,7 +15,6 @@
package server
import (
- "context"
"errors"
"net/http"
"strings"
@@ -193,7 +192,7 @@ func (s *Server) loadTrustCenterBySlugOrID(next http.Handler) http.Handler {
)
}
- ctx = s.addTrustCenterToContext(ctx, trustCenter.ID.TenantID(), trustCenter.OrganizationID)
+ ctx = trust_v1.ContextWithTrustCenter(ctx, *trustCenter)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
@@ -235,16 +234,20 @@ func (s *Server) loadTrustCenterByDomain(next http.Handler) http.Handler {
log.String("organization_id", organizationID.String()),
)
- ctx = s.addTrustCenterToContext(ctx, organizationID.TenantID(), organizationID)
+ trustCenter, err := s.proboService.LoadTrustCenterByOrganizationID(ctx, organizationID)
+ if err != nil {
+ s.logger.WarnCtx(ctx, "trust center not found",
+ log.Error(err),
+ )
+ http.Error(w, "Trust center not found", http.StatusNotFound)
+ return
+ }
+
+ ctx = trust_v1.ContextWithTrustCenter(ctx, *trustCenter)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
-func (s *Server) addTrustCenterToContext(ctx context.Context, tenantID, organizationID interface{}) context.Context {
- ctx = context.WithValue(ctx, trust_v1.CustomDomainOrganizationIDKey, organizationID)
- return ctx
-}
-
func (s *Server) stripTrustPrefix(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
slugOrId := chi.URLParam(r, "slugOrId")
diff --git a/pkg/trust/trust_center_access_service.go b/pkg/trust/trust_center_access_service.go
index df268403d..a25c6a348 100644
--- a/pkg/trust/trust_center_access_service.go
+++ b/pkg/trust/trust_center_access_service.go
@@ -44,7 +44,7 @@ type (
TrustCenterAccessRequest struct {
TrustCenterID gid.GID
Email mail.Addr
- Name string
+ FullName string
DocumentIDs []gid.GID
ReportIDs []gid.GID
TrustCenterFileIDs []gid.GID
@@ -63,26 +63,6 @@ func (tcar *TrustCenterAccessRequest) Validate() error {
return v.Error()
}
-func (s TrustCenterAccessService) ValidateToken(
- ctx context.Context,
- trustCenterID gid.GID,
- email mail.Addr,
-) error {
- return s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
- access := &coredata.TrustCenterAccess{}
- err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email)
- if err != nil {
- return fmt.Errorf("cannot load trust center access: %w", err)
- }
-
- if !access.Active {
- return fmt.Errorf("trust center access is not active")
- }
-
- return nil
- })
-}
-
func (s TrustCenterAccessService) Request(
ctx context.Context,
req *TrustCenterAccessRequest,
@@ -170,7 +150,7 @@ func (s TrustCenterAccessService) Request(
TenantID: s.svc.scope.GetTenantID(),
TrustCenterID: req.TrustCenterID,
Email: req.Email,
- Name: req.Name,
+ Name: req.FullName,
Active: false,
HasAcceptedNonDisclosureAgreement: false,
CreatedAt: now,