Add magic link login for trust center
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { AuditRowFragment$key } from "./__generated__/AuditRowFragment.graphql";
|
||||
import { useFragment } from "react-relay";
|
||||
import { useFragment, useMutation } from "react-relay";
|
||||
import {
|
||||
Breadcrumb,
|
||||
Button,
|
||||
@@ -12,14 +12,27 @@ import {
|
||||
IconMedal,
|
||||
Spinner,
|
||||
Table,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { AuditRowDownloadMutation } from "./__generated__/AuditRowDownloadMutation.graphql";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToast";
|
||||
import { downloadFile } from "@probo/helpers";
|
||||
import { type PropsWithChildren, useState } from "react";
|
||||
import { downloadFile, formatError } from "@probo/helpers";
|
||||
import { type PropsWithChildren, use, useState } from "react";
|
||||
import { useLocation } from "react-router";
|
||||
import { RequestAccessDialog } from "/components/RequestAccessDialog.tsx";
|
||||
import { Viewer } from "/providers/Viewer";
|
||||
import { MagicLinkDialog } from "./MagicLinkDialog";
|
||||
import type { AuditRow_requestAccessMutation } from "./__generated__/AuditRow_requestAccessMutation.graphql";
|
||||
|
||||
const requestAccessMutation = graphql`
|
||||
mutation AuditRow_requestAccessMutation($input: RequestReportAccessInput!) {
|
||||
requestReportAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const downloadMutation = graphql`
|
||||
mutation AuditRowDownloadMutation($input: ExportReportPDFInput!) {
|
||||
@@ -47,10 +60,53 @@ const auditRowFragment = graphql`
|
||||
`;
|
||||
|
||||
export function AuditRow(props: { audit: AuditRowFragment$key }) {
|
||||
const audit = useFragment(auditRowFragment, props.audit);
|
||||
const { __ } = useTranslate();
|
||||
const viewer = use(Viewer);
|
||||
const { toast } = useToast();
|
||||
|
||||
const audit = useFragment(auditRowFragment, props.audit);
|
||||
const [hasRequested, setHasRequested] = useState(
|
||||
audit.report?.hasUserRequestedAccess,
|
||||
);
|
||||
|
||||
const [requestAccess, isRequestingAccess] =
|
||||
useMutation<AuditRow_requestAccessMutation>(requestAccessMutation);
|
||||
const [commitDownload, downloading] =
|
||||
useMutationWithToasts<AuditRowDownloadMutation>(downloadMutation);
|
||||
|
||||
const handleRequestAccess = () => {
|
||||
requestAccess({
|
||||
variables: {
|
||||
input: {
|
||||
reportId: audit.report?.id ?? "",
|
||||
},
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(__("Cannot request access"), errors),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setHasRequested(true);
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access request submitted successfully."),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message ?? __("Cannot request access"),
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
if (!audit.report?.id) {
|
||||
return;
|
||||
@@ -67,41 +123,46 @@ export function AuditRow(props: { audit: AuditRowFragment$key }) {
|
||||
});
|
||||
};
|
||||
|
||||
const [hasRequested, setHasRequested] = useState(
|
||||
audit.report?.hasUserRequestedAccess
|
||||
);
|
||||
return (
|
||||
<div className="text-sm border border-border-solid -mt-px flex gap-3 flex-col md:flex-row md:justify-between px-6 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconMedal size={16} className="flex-none text-txt-tertiary" />
|
||||
{audit.framework.name}
|
||||
</div>
|
||||
{audit.report && audit.report.isUserAuthorized && (
|
||||
<Button
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
disabled={downloading}
|
||||
icon={downloading ? Spinner : IconArrowInbox}
|
||||
onClick={handleDownload}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
)}
|
||||
{audit.report && !audit.report.isUserAuthorized && (
|
||||
<RequestAccessDialog
|
||||
reportId={audit.report.id}
|
||||
onSuccess={() => setHasRequested(true)}
|
||||
>
|
||||
{!viewer && (
|
||||
<MagicLinkDialog>
|
||||
<Button
|
||||
disabled={hasRequested}
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
icon={IconLock}
|
||||
>
|
||||
{hasRequested ? __("Access requested") : __("Request access")}
|
||||
</Button>
|
||||
</RequestAccessDialog>
|
||||
</MagicLinkDialog>
|
||||
)}
|
||||
{viewer &&
|
||||
audit.report &&
|
||||
(audit.report.isUserAuthorized ? (
|
||||
<Button
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
disabled={downloading}
|
||||
icon={downloading ? Spinner : IconArrowInbox}
|
||||
onClick={handleDownload}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
disabled={hasRequested || isRequestingAccess}
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
icon={IconLock}
|
||||
onClick={handleRequestAccess}
|
||||
>
|
||||
{hasRequested ? __("Access requested") : __("Request access")}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -134,7 +195,7 @@ export function AuditRowAvatar(props: { audit: AuditRowFragment$key }) {
|
||||
}
|
||||
|
||||
function AuditDialog(
|
||||
props: PropsWithChildren<{ audit: AuditRowFragment$key; logo?: string }>
|
||||
props: PropsWithChildren<{ audit: AuditRowFragment$key; logo?: string }>,
|
||||
) {
|
||||
const audit = useFragment(auditRowFragment, props.audit);
|
||||
const location = useLocation();
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { DocumentRowFragment$key } from "./__generated__/DocumentRowFragment.graphql";
|
||||
import { useFragment } from "react-relay";
|
||||
import { useFragment, useMutation } from "react-relay";
|
||||
import {
|
||||
Button,
|
||||
IconArrowInbox,
|
||||
IconLock,
|
||||
IconPageTextLine,
|
||||
Spinner,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { DocumentRowDownloadMutation } from "./__generated__/DocumentRowDownloadMutation.graphql";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToast";
|
||||
import { downloadFile } from "@probo/helpers";
|
||||
import { RequestAccessDialog } from "/components/RequestAccessDialog.tsx";
|
||||
import { useState } from "react";
|
||||
import { downloadFile, formatError } from "@probo/helpers";
|
||||
import { use, useState } from "react";
|
||||
import { MagicLinkDialog } from "./MagicLinkDialog";
|
||||
import { Viewer } from "/providers/Viewer";
|
||||
import type { DocumentRow_requestAccessMutation } from "./__generated__/DocumentRow_requestAccessMutation.graphql";
|
||||
|
||||
const requestAccessMutation = graphql`
|
||||
mutation DocumentRow_requestAccessMutation(
|
||||
$input: RequestDocumentAccessInput!
|
||||
) {
|
||||
requestDocumentAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const downloadMutation = graphql`
|
||||
mutation DocumentRowDownloadMutation($input: ExportDocumentPDFInput!) {
|
||||
@@ -33,10 +48,53 @@ const documentRowFragment = graphql`
|
||||
`;
|
||||
|
||||
export function DocumentRow(props: { document: DocumentRowFragment$key }) {
|
||||
const document = useFragment(documentRowFragment, props.document);
|
||||
const { __ } = useTranslate();
|
||||
const viewer = use(Viewer);
|
||||
const { toast } = useToast();
|
||||
|
||||
const document = useFragment(documentRowFragment, props.document);
|
||||
const [hasRequested, setHasRequested] = useState(
|
||||
document.hasUserRequestedAccess,
|
||||
);
|
||||
|
||||
const [requestAccess, isRequestingAccess] =
|
||||
useMutation<DocumentRow_requestAccessMutation>(requestAccessMutation);
|
||||
const [commitDownload, downloading] =
|
||||
useMutationWithToasts<DocumentRowDownloadMutation>(downloadMutation);
|
||||
|
||||
const handleRequestAccess = () => {
|
||||
requestAccess({
|
||||
variables: {
|
||||
input: {
|
||||
documentId: document.id,
|
||||
},
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(__("Cannot request access"), errors),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setHasRequested(true);
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access request submitted successfully."),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message ?? __("Cannot request access"),
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
commitDownload({
|
||||
variables: {
|
||||
@@ -49,40 +107,46 @@ export function DocumentRow(props: { document: DocumentRowFragment$key }) {
|
||||
},
|
||||
});
|
||||
};
|
||||
const [hasRequested, setHasRequested] = useState(
|
||||
document.hasUserRequestedAccess,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="text-sm border border-border-solid -mt-px flex gap-3 flex-col md:flex-row md:justify-between px-6 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconPageTextLine size={16} className=" flex-none text-txt-tertiary" />
|
||||
{document.title}
|
||||
</div>
|
||||
{document.isUserAuthorized ? (
|
||||
<Button
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
disabled={downloading}
|
||||
icon={downloading ? Spinner : IconArrowInbox}
|
||||
onClick={handleDownload}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
) : (
|
||||
<RequestAccessDialog
|
||||
documentId={document.id}
|
||||
onSuccess={() => setHasRequested(true)}
|
||||
>
|
||||
{!viewer && (
|
||||
<MagicLinkDialog>
|
||||
<Button
|
||||
disabled={hasRequested}
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
icon={IconLock}
|
||||
>
|
||||
{hasRequested ? __("Access requested") : __("Request access")}
|
||||
</Button>
|
||||
</RequestAccessDialog>
|
||||
</MagicLinkDialog>
|
||||
)}
|
||||
{viewer &&
|
||||
(document.isUserAuthorized ? (
|
||||
<Button
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
disabled={downloading}
|
||||
icon={downloading ? Spinner : IconArrowInbox}
|
||||
onClick={handleDownload}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
disabled={hasRequested || isRequestingAccess}
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
icon={IconLock}
|
||||
onClick={handleRequestAccess}
|
||||
>
|
||||
{hasRequested ? __("Access requested") : __("Request access")}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
122
apps/trust/src/components/MagicLinkDialog.tsx
Normal file
122
apps/trust/src/components/MagicLinkDialog.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
Field,
|
||||
useDialogRef,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useMutation } from "react-relay";
|
||||
import { graphql } from "relay-runtime";
|
||||
import z from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { MagicLinkDialogMutation } from "./__generated__/MagicLinkDialogMutation.graphql";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
const sendMagicLinkMutation = graphql`
|
||||
mutation MagicLinkDialogMutation($input: SendMagicLinkInput!) {
|
||||
sendMagicLink(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const schema = z.object({
|
||||
email: z.string().email(),
|
||||
});
|
||||
|
||||
type FormData = z.infer<typeof schema>;
|
||||
|
||||
export function MagicLinkDialog(props: PropsWithChildren) {
|
||||
const { children } = props;
|
||||
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
const [sendMagicLink] = useMutation<MagicLinkDialogMutation>(
|
||||
sendMagicLinkMutation,
|
||||
);
|
||||
|
||||
const {
|
||||
handleSubmit: handleSubmitWrapper,
|
||||
register,
|
||||
formState,
|
||||
} = useFormWithSchema(schema, {
|
||||
defaultValues: {
|
||||
email: "",
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = ({ email }: FormData) => {
|
||||
sendMagicLink({
|
||||
variables: {
|
||||
input: {
|
||||
email,
|
||||
},
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("Cannot send magic link"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __(
|
||||
"Magic link sent! Please check your emails to authenticate",
|
||||
),
|
||||
variant: "success",
|
||||
});
|
||||
dialogRef.current?.close();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message,
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
trigger={children}
|
||||
ref={dialogRef}
|
||||
className="max-w-[500px] text-txt-primary"
|
||||
>
|
||||
<form onSubmit={handleSubmitWrapper(handleSubmit)}>
|
||||
<DialogTitle className="text-2xl font-semibold mb-4 pt-4 md:pt-8 px-4 md:px-8">
|
||||
{__("Create account or sign in")}
|
||||
</DialogTitle>
|
||||
<DialogContent className="px-4 md:px-8 pb-4 md:pb-8 text-txt-primary">
|
||||
<p className="text-txt-secondary mb-4">
|
||||
{__(
|
||||
"To be able to request access to documents, you need to authenticate. Please enter your email to get a magic link.",
|
||||
)}
|
||||
</p>
|
||||
<Field
|
||||
label={__("Email")}
|
||||
placeholder="john.doe@acme.com"
|
||||
{...register("email")}
|
||||
type="email"
|
||||
error={formState.errors.email?.message}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button disabled={formState.isSubmitting} type="submit">
|
||||
{__("Continue")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,31 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Button, Card, IconBlock, IconLock, IconMedal } from "@probo/ui";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
IconBlock,
|
||||
IconLock,
|
||||
IconMedal,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import type { TrustGraphQuery$data } from "/queries/__generated__/TrustGraphQuery.graphql";
|
||||
import { use, type PropsWithChildren } from "react";
|
||||
import { domain } from "@probo/helpers";
|
||||
import { domain, formatError } from "@probo/helpers";
|
||||
import { AuditRowAvatar } from "./AuditRow";
|
||||
import { RequestAccessDialog } from "./RequestAccessDialog";
|
||||
import { Viewer } from "/providers/Viewer";
|
||||
import { MagicLinkDialog } from "./MagicLinkDialog";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useMutation } from "react-relay";
|
||||
import type { OrganizationSidebar_requestAllAccessesMutation } from "./__generated__/OrganizationSidebar_requestAllAccessesMutation.graphql";
|
||||
|
||||
const requestAllAccessesMutation = graphql`
|
||||
mutation OrganizationSidebar_requestAllAccessesMutation {
|
||||
requestAllAccesses {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function OrganizationSidebar({
|
||||
trustCenter,
|
||||
@@ -14,6 +34,40 @@ export function OrganizationSidebar({
|
||||
}) {
|
||||
const { __ } = useTranslate();
|
||||
const isAuthenticated = !!use(Viewer);
|
||||
const { toast } = useToast();
|
||||
|
||||
const [requestAllAccesses, isRequestingAccess] =
|
||||
useMutation<OrganizationSidebar_requestAllAccessesMutation>(
|
||||
requestAllAccessesMutation,
|
||||
);
|
||||
|
||||
const handleRequestAllAccesses = () => {
|
||||
requestAllAccesses({
|
||||
variables: {},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(__("Cannot request access"), errors),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access request submitted successfully."),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message ?? __("Cannot request access"),
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (!trustCenter) {
|
||||
return null;
|
||||
@@ -95,12 +149,22 @@ export function OrganizationSidebar({
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
{!isAuthenticated && (
|
||||
<RequestAccessDialog>
|
||||
{isAuthenticated ? (
|
||||
<Button
|
||||
disabled={isRequestingAccess}
|
||||
variant="primary"
|
||||
icon={IconLock}
|
||||
className="w-full h-10"
|
||||
onClick={handleRequestAllAccesses}
|
||||
>
|
||||
{__("Request access")}
|
||||
</Button>
|
||||
) : (
|
||||
<MagicLinkDialog>
|
||||
<Button variant="primary" icon={IconLock} className="w-full h-10">
|
||||
{__("Request access")}
|
||||
</Button>
|
||||
</RequestAccessDialog>
|
||||
</MagicLinkDialog>
|
||||
)}
|
||||
{/* <Button variant="secondary" icon={IconMail} className="w-full h-10">
|
||||
{__("Subscribe to updates")}
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
Field,
|
||||
useDialogRef,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToast";
|
||||
import { useTrustCenter } from "/hooks/useTrustCenter";
|
||||
import { use, type FormEventHandler, type PropsWithChildren } from "react";
|
||||
import { InvalidError } from "/providers/RelayProviders";
|
||||
import { Viewer } from "/providers/Viewer";
|
||||
|
||||
type Props = PropsWithChildren<{
|
||||
documentId?: string;
|
||||
reportId?: string;
|
||||
trustCenterFileId?: string;
|
||||
onSuccess?: () => void;
|
||||
}>;
|
||||
|
||||
const schema = z.object({
|
||||
fullName: z.string(),
|
||||
email: z.string().email(),
|
||||
});
|
||||
|
||||
export function RequestAccessDialog({
|
||||
children,
|
||||
documentId,
|
||||
reportId,
|
||||
trustCenterFileId,
|
||||
onSuccess,
|
||||
}: Props) {
|
||||
const trustCenter = useTrustCenter();
|
||||
const { toast } = useToast();
|
||||
const { __ } = useTranslate();
|
||||
const viewer = use(Viewer);
|
||||
const { handleSubmit, register, setError, formState } = useFormWithSchema(
|
||||
schema,
|
||||
{
|
||||
defaultValues: {
|
||||
fullName: "",
|
||||
email: "",
|
||||
},
|
||||
},
|
||||
);
|
||||
const dialogRef = useDialogRef();
|
||||
const [commitMutation, isMutating] = useMutation({
|
||||
documentId,
|
||||
reportId,
|
||||
trustCenterFileId,
|
||||
});
|
||||
|
||||
const submitCallback = (data: z.infer<typeof schema> | null) => {
|
||||
commitMutation({
|
||||
email: data?.email ?? viewer?.email ?? "",
|
||||
fullName: data?.fullName ?? viewer?.fullName ?? "",
|
||||
})
|
||||
.then(() => {
|
||||
onSuccess?.();
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access request submitted successfully"),
|
||||
variant: "success",
|
||||
});
|
||||
dialogRef.current?.close();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error instanceof InvalidError) {
|
||||
if (error.field === "email") {
|
||||
setError(error.field, { message: error.message });
|
||||
}
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("Cannot request access"),
|
||||
variant: "error",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit: FormEventHandler<HTMLFormElement> = viewer
|
||||
? (e) => {
|
||||
e.preventDefault();
|
||||
submitCallback(null);
|
||||
}
|
||||
: handleSubmit(submitCallback);
|
||||
return (
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
trigger={children}
|
||||
className="max-w-[500px] text-txt-primary"
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogTitle className="text-2xl font-semibold mb-4 pt-4 md:pt-8 px-4 md:px-8">
|
||||
{__("Request access to documentation")}
|
||||
</DialogTitle>
|
||||
<DialogContent className="px-4 md:px-8 pb-4 md:pb-8 text-txt-primary">
|
||||
<p className="text-txt-secondary mb-4">
|
||||
{sprintf(
|
||||
__(
|
||||
"Request access to %s's Trust Center. Your request will be reviewed and you will receive an email notification with access instructions if approved.",
|
||||
),
|
||||
trustCenter.organization.name,
|
||||
)}
|
||||
</p>
|
||||
{!viewer && (
|
||||
<div className="space-y-4">
|
||||
<Field
|
||||
label={__("Full name")}
|
||||
placeholder="John Doe"
|
||||
{...register("fullName")}
|
||||
type="text"
|
||||
/>
|
||||
<Field
|
||||
label={__("Email")}
|
||||
placeholder="john.doe@acme.com"
|
||||
{...register("email")}
|
||||
type="email"
|
||||
error={formState.errors.email?.message}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button disabled={isMutating} type="submit">
|
||||
{__("Continue")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
const requestAccessMutation = graphql`
|
||||
mutation RequestAccessDialogMutation($input: RequestAllAccessesInput!) {
|
||||
requestAllAccesses(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const requestDocumentAccessMutation = graphql`
|
||||
mutation RequestAccessDialogDocumentMutation(
|
||||
$input: RequestDocumentAccessInput!
|
||||
) {
|
||||
requestDocumentAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const requestReportAccessMutation = graphql`
|
||||
mutation RequestAccessDialogReportMutation(
|
||||
$input: RequestReportAccessInput!
|
||||
) {
|
||||
requestReportAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const requestTrustCenterFileAccessMutation = graphql`
|
||||
mutation RequestAccessDialogTrustCenterFileMutation(
|
||||
$input: RequestTrustCenterFileAccessInput!
|
||||
) {
|
||||
requestTrustCenterFileAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Use the correct mutation using the shape
|
||||
*/
|
||||
function useMutation({
|
||||
documentId,
|
||||
reportId,
|
||||
trustCenterFileId,
|
||||
}: Pick<Props, "documentId" | "reportId" | "trustCenterFileId">): [
|
||||
(data: z.infer<typeof schema> | null) => Promise<unknown>,
|
||||
boolean,
|
||||
] {
|
||||
const trustCenter = useTrustCenter();
|
||||
const [commitRequestAccess, isRequestingAccess] = useMutationWithToasts(
|
||||
requestAccessMutation,
|
||||
);
|
||||
const [commitRequestDocumentAccess, isRequestingDocumentAccess] =
|
||||
useMutationWithToasts(requestDocumentAccessMutation);
|
||||
const [commitRequestReportAccess, isRequestingReportAccess] =
|
||||
useMutationWithToasts(requestReportAccessMutation);
|
||||
const [
|
||||
commitRequestTrustCenterFileAccess,
|
||||
isRequestingTrustCenterFileAccess,
|
||||
] = useMutationWithToasts(requestTrustCenterFileAccessMutation);
|
||||
|
||||
if (trustCenterFileId) {
|
||||
return [
|
||||
(data) => {
|
||||
return commitRequestTrustCenterFileAccess({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterId: trustCenter.id,
|
||||
trustCenterFileId: trustCenterFileId,
|
||||
...data,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
isRequestingTrustCenterFileAccess,
|
||||
];
|
||||
} else if (reportId) {
|
||||
return [
|
||||
(data) => {
|
||||
return commitRequestReportAccess({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterId: trustCenter.id,
|
||||
reportId: reportId,
|
||||
...data,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
isRequestingReportAccess,
|
||||
];
|
||||
} else if (documentId) {
|
||||
return [
|
||||
(data) => {
|
||||
return commitRequestDocumentAccess({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterId: trustCenter.id,
|
||||
documentId: documentId,
|
||||
...data,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
isRequestingDocumentAccess,
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
(data) => {
|
||||
return commitRequestAccess({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterId: trustCenter.id,
|
||||
...data,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
isRequestingAccess,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,39 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { TrustCenterFileRowFragment$key } from "./__generated__/TrustCenterFileRowFragment.graphql";
|
||||
import { useFragment } from "react-relay";
|
||||
import { useFragment, useMutation } from "react-relay";
|
||||
import {
|
||||
Button,
|
||||
IconArrowInbox,
|
||||
IconLock,
|
||||
IconPageTextLine,
|
||||
Spinner,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { TrustCenterFileRowDownloadMutation } from "./__generated__/TrustCenterFileRowDownloadMutation.graphql";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToast";
|
||||
import { downloadFile } from "@probo/helpers";
|
||||
import { RequestAccessDialog } from "/components/RequestAccessDialog.tsx";
|
||||
import { useState } from "react";
|
||||
import { downloadFile, formatError } from "@probo/helpers";
|
||||
import { use, useState } from "react";
|
||||
import { Viewer } from "/providers/Viewer";
|
||||
import { MagicLinkDialog } from "./MagicLinkDialog";
|
||||
import type { TrustCenterFileRow_requestAccessMutation } from "./__generated__/TrustCenterFileRow_requestAccessMutation.graphql";
|
||||
|
||||
const requestAccessMutation = graphql`
|
||||
mutation TrustCenterFileRow_requestAccessMutation(
|
||||
$input: RequestTrustCenterFileAccessInput!
|
||||
) {
|
||||
requestTrustCenterFileAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const downloadMutation = graphql`
|
||||
mutation TrustCenterFileRowDownloadMutation($input: ExportTrustCenterFileInput!) {
|
||||
mutation TrustCenterFileRowDownloadMutation(
|
||||
$input: ExportTrustCenterFileInput!
|
||||
) {
|
||||
exportTrustCenterFile(input: $input) {
|
||||
data
|
||||
}
|
||||
@@ -32,11 +49,56 @@ const trustCenterFileRowFragment = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
export function TrustCenterFileRow(props: { file: TrustCenterFileRowFragment$key }) {
|
||||
const file = useFragment(trustCenterFileRowFragment, props.file);
|
||||
export function TrustCenterFileRow(props: {
|
||||
file: TrustCenterFileRowFragment$key;
|
||||
}) {
|
||||
const { __ } = useTranslate();
|
||||
const viewer = use(Viewer);
|
||||
const { toast } = useToast();
|
||||
|
||||
const file = useFragment(trustCenterFileRowFragment, props.file);
|
||||
const [hasRequested, setHasRequested] = useState(file.hasUserRequestedAccess);
|
||||
|
||||
const [requestAccess, isRequestingAccess] =
|
||||
useMutation<TrustCenterFileRow_requestAccessMutation>(
|
||||
requestAccessMutation,
|
||||
);
|
||||
const [commitDownload, downloading] =
|
||||
useMutationWithToasts<TrustCenterFileRowDownloadMutation>(downloadMutation);
|
||||
|
||||
const handleRequestAccess = () => {
|
||||
requestAccess({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterFileId: file.id,
|
||||
},
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(__("Cannot request access"), errors),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setHasRequested(true);
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access request submitted successfully."),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message ?? __("Cannot request access"),
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
commitDownload({
|
||||
variables: {
|
||||
@@ -49,40 +111,46 @@ export function TrustCenterFileRow(props: { file: TrustCenterFileRowFragment$key
|
||||
},
|
||||
});
|
||||
};
|
||||
const [hasRequested, setHasRequested] = useState(
|
||||
file.hasUserRequestedAccess,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="text-sm border-1 border-border-solid -mt-[1px] flex gap-3 flex-col md:flex-row md:justify-between px-6 py-3">
|
||||
<div className="text-sm border border-border-solid -mt-px flex gap-3 flex-col md:flex-row md:justify-between px-6 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconPageTextLine size={16} className=" flex-none text-txt-tertiary" />
|
||||
{file.name}
|
||||
</div>
|
||||
{file.isUserAuthorized ? (
|
||||
<Button
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
disabled={downloading}
|
||||
icon={downloading ? Spinner : IconArrowInbox}
|
||||
onClick={handleDownload}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
) : (
|
||||
<RequestAccessDialog
|
||||
trustCenterFileId={file.id}
|
||||
onSuccess={() => setHasRequested(true)}
|
||||
>
|
||||
{!viewer && (
|
||||
<MagicLinkDialog>
|
||||
<Button
|
||||
disabled={hasRequested}
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
icon={IconLock}
|
||||
>
|
||||
{hasRequested ? __("Access requested") : __("Request access")}
|
||||
</Button>
|
||||
</RequestAccessDialog>
|
||||
</MagicLinkDialog>
|
||||
)}
|
||||
{viewer &&
|
||||
(file.isUserAuthorized ? (
|
||||
<Button
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
disabled={downloading}
|
||||
icon={downloading ? Spinner : IconArrowInbox}
|
||||
onClick={handleDownload}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
disabled={hasRequested || isRequestingAccess}
|
||||
className="w-full md:w-max"
|
||||
variant="secondary"
|
||||
icon={IconLock}
|
||||
onClick={handleRequestAccess}
|
||||
>
|
||||
{hasRequested ? __("Access requested") : __("Request access")}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
105
apps/trust/src/components/__generated__/AuditRow_requestAccessMutation.graphql.ts
generated
Normal file
105
apps/trust/src/components/__generated__/AuditRow_requestAccessMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @generated SignedSource<<0e5c234b565a3031a774995bff249b97>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RequestReportAccessInput = {
|
||||
reportId: string;
|
||||
};
|
||||
export type AuditRow_requestAccessMutation$variables = {
|
||||
input: RequestReportAccessInput;
|
||||
};
|
||||
export type AuditRow_requestAccessMutation$data = {
|
||||
readonly requestReportAccess: {
|
||||
readonly trustCenterAccess: {
|
||||
readonly id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type AuditRow_requestAccessMutation = {
|
||||
response: AuditRow_requestAccessMutation$data;
|
||||
variables: AuditRow_requestAccessMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "RequestAccessesPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "requestReportAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "trustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "AuditRow_requestAccessMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "AuditRow_requestAccessMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "6760f1ec8a6990f62461c3fab2b2934a",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "AuditRow_requestAccessMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation AuditRow_requestAccessMutation(\n $input: RequestReportAccessInput!\n) {\n requestReportAccess(input: $input) {\n trustCenterAccess {\n id\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "6ab5eb701126385e717a381e21e55ae0";
|
||||
|
||||
export default node;
|
||||
105
apps/trust/src/components/__generated__/DocumentRow_requestAccessMutation.graphql.ts
generated
Normal file
105
apps/trust/src/components/__generated__/DocumentRow_requestAccessMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @generated SignedSource<<e62c28d53365ca5d5f3d53282aefd4e7>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RequestDocumentAccessInput = {
|
||||
documentId: string;
|
||||
};
|
||||
export type DocumentRow_requestAccessMutation$variables = {
|
||||
input: RequestDocumentAccessInput;
|
||||
};
|
||||
export type DocumentRow_requestAccessMutation$data = {
|
||||
readonly requestDocumentAccess: {
|
||||
readonly trustCenterAccess: {
|
||||
readonly id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type DocumentRow_requestAccessMutation = {
|
||||
response: DocumentRow_requestAccessMutation$data;
|
||||
variables: DocumentRow_requestAccessMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "RequestAccessesPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "requestDocumentAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "trustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "DocumentRow_requestAccessMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "DocumentRow_requestAccessMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "13715b7cb8784d050b6d0381e460bbad",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "DocumentRow_requestAccessMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation DocumentRow_requestAccessMutation(\n $input: RequestDocumentAccessInput!\n) {\n requestDocumentAccess(input: $input) {\n trustCenterAccess {\n id\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "ca9026b04c2de91067759e3a80e5c276";
|
||||
|
||||
export default node;
|
||||
92
apps/trust/src/components/__generated__/MagicLinkDialogMutation.graphql.ts
generated
Normal file
92
apps/trust/src/components/__generated__/MagicLinkDialogMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @generated SignedSource<<1d6509148442dad28e2af20068216f49>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type SendMagicLinkInput = {
|
||||
email: any;
|
||||
};
|
||||
export type MagicLinkDialogMutation$variables = {
|
||||
input: SendMagicLinkInput;
|
||||
};
|
||||
export type MagicLinkDialogMutation$data = {
|
||||
readonly sendMagicLink: {
|
||||
readonly success: boolean;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type MagicLinkDialogMutation = {
|
||||
response: MagicLinkDialogMutation$data;
|
||||
variables: MagicLinkDialogMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "SendMagicLinkPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "sendMagicLink",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "success",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "MagicLinkDialogMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "MagicLinkDialogMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "d0e02db0cec956d7a21f5ad5a5a69d61",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "MagicLinkDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation MagicLinkDialogMutation(\n $input: SendMagicLinkInput!\n) {\n sendMagicLink(input: $input) {\n success\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "c1ee1fe3ef7c232fcb43b5cf8e56c2de";
|
||||
|
||||
export default node;
|
||||
87
apps/trust/src/components/__generated__/OrganizationSidebar_requestAllAccessesMutation.graphql.ts
generated
Normal file
87
apps/trust/src/components/__generated__/OrganizationSidebar_requestAllAccessesMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @generated SignedSource<<e1d14b17505ed1107ac96553e3d8f922>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type OrganizationSidebar_requestAllAccessesMutation$variables = Record<PropertyKey, never>;
|
||||
export type OrganizationSidebar_requestAllAccessesMutation$data = {
|
||||
readonly requestAllAccesses: {
|
||||
readonly trustCenterAccess: {
|
||||
readonly id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type OrganizationSidebar_requestAllAccessesMutation = {
|
||||
response: OrganizationSidebar_requestAllAccessesMutation$data;
|
||||
variables: OrganizationSidebar_requestAllAccessesMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RequestAccessesPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "requestAllAccesses",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "trustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "OrganizationSidebar_requestAllAccessesMutation",
|
||||
"selections": (v0/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Operation",
|
||||
"name": "OrganizationSidebar_requestAllAccessesMutation",
|
||||
"selections": (v0/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "a298b2a1c2f62300ae5056743d2c2ec3",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "OrganizationSidebar_requestAllAccessesMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation OrganizationSidebar_requestAllAccessesMutation {\n requestAllAccesses {\n trustCenterAccess {\n id\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "a22225757510c4dd097e99dcd3c066a6";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<9f01a480ccc5fa00e310296e9aed1020>>
|
||||
* @generated SignedSource<<2ea33498f7101d861d940b9c52ed3283>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -11,8 +11,6 @@
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RequestDocumentAccessInput = {
|
||||
documentId: string;
|
||||
email: any;
|
||||
fullName: string;
|
||||
};
|
||||
export type RequestAccessDialogDocumentMutation$variables = {
|
||||
input: RequestDocumentAccessInput;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<0777fa7e6e3d0c75eedfc8380d9965a4>>
|
||||
* @generated SignedSource<<b06c18474ea9953c2fb496b919b8e247>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -9,13 +9,7 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RequestAllAccessesInput = {
|
||||
email: any;
|
||||
fullName: string;
|
||||
};
|
||||
export type RequestAccessDialogMutation$variables = {
|
||||
input: RequestAllAccessesInput;
|
||||
};
|
||||
export type RequestAccessDialogMutation$variables = Record<PropertyKey, never>;
|
||||
export type RequestAccessDialogMutation$data = {
|
||||
readonly requestAllAccesses: {
|
||||
readonly trustCenterAccess: {
|
||||
@@ -30,22 +24,9 @@ export type RequestAccessDialogMutation = {
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"args": null,
|
||||
"concreteType": "RequestAccessesPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "requestAllAccesses",
|
||||
@@ -75,32 +56,32 @@ v1 = [
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "RequestAccessDialogMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"selections": (v0/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Operation",
|
||||
"name": "RequestAccessDialogMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
"selections": (v0/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "99eb2902ce5a921515d68db30f8a2189",
|
||||
"cacheID": "b48847e88c57289efc120745c743c8f6",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "RequestAccessDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation RequestAccessDialogMutation(\n $input: RequestAllAccessesInput!\n) {\n requestAllAccesses(input: $input) {\n trustCenterAccess {\n id\n }\n }\n}\n"
|
||||
"text": "mutation RequestAccessDialogMutation {\n requestAllAccesses {\n trustCenterAccess {\n id\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "cc4d5c8753438eff23833b4182dd485d";
|
||||
(node as any).hash = "44c3538831968b7146fbf2c9e027a778";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<6fde198cd3eaa998d665639c48f5e630>>
|
||||
* @generated SignedSource<<3e5433cc33d54bff9511d01eb4c65b00>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RequestReportAccessInput = {
|
||||
email: any;
|
||||
fullName: string;
|
||||
reportId: string;
|
||||
};
|
||||
export type RequestAccessDialogReportMutation$variables = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<7c329901bcf9bbf05b1899356016b151>>
|
||||
* @generated SignedSource<<447bd664ebefbd6a672ccde86c63c028>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RequestTrustCenterFileAccessInput = {
|
||||
email: any;
|
||||
fullName: string;
|
||||
trustCenterFileId: string;
|
||||
};
|
||||
export type RequestAccessDialogTrustCenterFileMutation$variables = {
|
||||
|
||||
105
apps/trust/src/components/__generated__/TrustCenterFileRow_requestAccessMutation.graphql.ts
generated
Normal file
105
apps/trust/src/components/__generated__/TrustCenterFileRow_requestAccessMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @generated SignedSource<<a8da5aa24d8696b55231f44d861f347c>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RequestTrustCenterFileAccessInput = {
|
||||
trustCenterFileId: string;
|
||||
};
|
||||
export type TrustCenterFileRow_requestAccessMutation$variables = {
|
||||
input: RequestTrustCenterFileAccessInput;
|
||||
};
|
||||
export type TrustCenterFileRow_requestAccessMutation$data = {
|
||||
readonly requestTrustCenterFileAccess: {
|
||||
readonly trustCenterAccess: {
|
||||
readonly id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type TrustCenterFileRow_requestAccessMutation = {
|
||||
response: TrustCenterFileRow_requestAccessMutation$data;
|
||||
variables: TrustCenterFileRow_requestAccessMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "RequestAccessesPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "requestTrustCenterFileAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "trustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TrustCenterFileRow_requestAccessMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "TrustCenterFileRow_requestAccessMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "77a09810aa9c3ddf3a3baddf709f9a42",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TrustCenterFileRow_requestAccessMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TrustCenterFileRow_requestAccessMutation(\n $input: RequestTrustCenterFileAccessInput!\n) {\n requestTrustCenterFileAccess(input: $input) {\n trustCenterAccess {\n id\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "eba04489de9ec18ab9816328a481ef33";
|
||||
|
||||
export default node;
|
||||
@@ -1,76 +0,0 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { buildEndpoint } from "/providers/RelayProviders";
|
||||
import { PageError } from "/components/PageError";
|
||||
import { Spinner } from "@probo/ui";
|
||||
|
||||
/**
|
||||
* Page requested with an access token to authenticate the user for the Trust center
|
||||
*/
|
||||
export function AccessPage() {
|
||||
const { __ } = useTranslate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const token = searchParams.get("token");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isValidRequest = !!token;
|
||||
const [error, setError] = useState<string | null>(() => {
|
||||
if (!token) {
|
||||
return __("Invalid access token");
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// Initiate an authentication attempt
|
||||
useEffect(() => {
|
||||
if (!isValidRequest) {
|
||||
return;
|
||||
}
|
||||
fetch(buildEndpoint("/api/trust/v1/auth/authenticate"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token,
|
||||
}),
|
||||
})
|
||||
.then((response) => {
|
||||
// For invalid response throw an error
|
||||
if (!response.ok) {
|
||||
const defaultMessage = `HTTP ${response.status}: ${response.statusText}`;
|
||||
return response
|
||||
.json()
|
||||
.then((json) => {
|
||||
throw new Error(json.message ?? defaultMessage);
|
||||
})
|
||||
.catch(() => {
|
||||
throw new Error(defaultMessage);
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.success) {
|
||||
navigate("/overview");
|
||||
return;
|
||||
}
|
||||
throw new Error(data.message ?? __("Authentication failed"));
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(error.message);
|
||||
});
|
||||
}, [isValidRequest, token, __, navigate]);
|
||||
|
||||
if (error) {
|
||||
return <PageError error={error} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 text-center flex items-center justify-center gap-2">
|
||||
<Spinner size={16} />
|
||||
{__("Redirecting to trust center")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
19
apps/trust/src/pages/auth/AuthLayout.tsx
Normal file
19
apps/trust/src/pages/auth/AuthLayout.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Logo } from "@probo/ui";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen text-txt-primary">
|
||||
<div className="bg-level-0 flex flex-col items-center justify-center">
|
||||
<div className="w-full max-w-md px-6">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:flex bg-dialog font-bold flex-col items-center justify-center p-8 text-txt-primary lg:p-10">
|
||||
<div className="flex flex-col items-center justify-center gap-4">
|
||||
<Logo withPicto className="w-[440px]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
119
apps/trust/src/pages/auth/VerifyMagicLinkPage.tsx
Normal file
119
apps/trust/src/pages/auth/VerifyMagicLinkPage.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Button, Field, useToast } from "@probo/ui";
|
||||
import { useSearchParams } from "react-router";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import z from "zod";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useMutation } from "react-relay";
|
||||
import { formatError } from "@probo/helpers";
|
||||
import type { VerifyMagicLinkPageMutation } from "./__generated__/VerifyMagicLinkPageMutation.graphql";
|
||||
import { getPathPrefix } from "/utils/pathPrefix";
|
||||
|
||||
const verifyMagicLinkMutation = graphql`
|
||||
mutation VerifyMagicLinkPageMutation($input: VerifyMagicLinkInput!) {
|
||||
verifyMagicLink(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const verifyMagicLinkSchema = z.object({
|
||||
token: z.string().min(1, "Please enter a magic token"),
|
||||
});
|
||||
|
||||
export default function VerifyMagicLinkPagePageMutation() {
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const [searchParams] = useSearchParams();
|
||||
const submittedRef = useRef<boolean>(false);
|
||||
|
||||
usePageTitle(__("Verify Magic Link"));
|
||||
|
||||
const form = useFormWithSchema(verifyMagicLinkSchema, {
|
||||
defaultValues: {
|
||||
token: searchParams.get("token") ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
const [verifyMagicLink] = useMutation<VerifyMagicLinkPageMutation>(
|
||||
verifyMagicLinkMutation,
|
||||
);
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
verifyMagicLink({
|
||||
variables: {
|
||||
input: {
|
||||
token: data.token.trim(),
|
||||
},
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(__("Failed to connect"), errors),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Your have successfully signed in"),
|
||||
variant: "success",
|
||||
});
|
||||
window.location.href = getPathPrefix();
|
||||
},
|
||||
onError: (err) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: err.message,
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!submittedRef.current && searchParams.get("token")) {
|
||||
handleSubmit();
|
||||
submittedRef.current = true;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-6 w-full max-w-md mx-auto">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-3xl font-bold">{__("Email Confirmation")}</h1>
|
||||
<p className="text-txt-tertiary">
|
||||
{__("Confirm your email address to complete registration")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<Field
|
||||
label={__("Confirmation Token")}
|
||||
type="text"
|
||||
placeholder={__("Enter your confirmation token")}
|
||||
{...form.register("token")}
|
||||
error={form.formState.errors.token?.message}
|
||||
disabled={form.formState.isSubmitting}
|
||||
help={__(
|
||||
"The token has been automatically filled from the URL if available",
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={form.formState.isSubmitting}
|
||||
>
|
||||
{form.formState.isSubmitting
|
||||
? __("Confirming...")
|
||||
: __("Confirm Email")}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
92
apps/trust/src/pages/auth/__generated__/VerifyMagicLinkPageMutation.graphql.ts
generated
Normal file
92
apps/trust/src/pages/auth/__generated__/VerifyMagicLinkPageMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @generated SignedSource<<5037d9722f3c6cc15c2a323ef954e3d6>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type VerifyMagicLinkInput = {
|
||||
token: string;
|
||||
};
|
||||
export type VerifyMagicLinkPageMutation$variables = {
|
||||
input: VerifyMagicLinkInput;
|
||||
};
|
||||
export type VerifyMagicLinkPageMutation$data = {
|
||||
readonly verifyMagicLink: {
|
||||
readonly success: boolean;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type VerifyMagicLinkPageMutation = {
|
||||
response: VerifyMagicLinkPageMutation$data;
|
||||
variables: VerifyMagicLinkPageMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "VerifyMagicLinkPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "verifyMagicLink",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "success",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "VerifyMagicLinkPageMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "VerifyMagicLinkPageMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "07cf89de3f37725d847cda46557467f5",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "VerifyMagicLinkPageMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation VerifyMagicLinkPageMutation(\n $input: VerifyMagicLinkInput!\n) {\n verifyMagicLink(input: $input) {\n success\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "074415601c4d50f50d177c06dfab64ef";
|
||||
|
||||
export default node;
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import { GraphQLError } from "graphql";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { RelayEnvironmentProvider } from "react-relay";
|
||||
import { getPathPrefix } from "/utils/pathPrefix";
|
||||
|
||||
export class UnAuthenticatedError extends Error {
|
||||
constructor() {
|
||||
@@ -35,11 +36,11 @@ export class InternalServerError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEndpoint(path: string): string {
|
||||
const host = import.meta.env.VITE_API_URL;
|
||||
export function buildEndpoint(): string {
|
||||
let host = import.meta.env.VITE_API_URL;
|
||||
|
||||
if (!host) {
|
||||
return path;
|
||||
host = window.location.origin;
|
||||
}
|
||||
|
||||
const formattedHost =
|
||||
@@ -49,10 +50,16 @@ export function buildEndpoint(path: string): string {
|
||||
|
||||
const url = new URL(formattedHost);
|
||||
|
||||
if (path) {
|
||||
url.pathname = path.startsWith("/") ? path : `/${path}`;
|
||||
const prefix = getPathPrefix();
|
||||
let path: string;
|
||||
if (prefix) {
|
||||
path = `${prefix}/api/trust/v1/graphql`;
|
||||
} else {
|
||||
path = `/api/trust/v1/graphql`;
|
||||
}
|
||||
|
||||
url.pathname = path;
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
@@ -66,7 +73,7 @@ const fetchRelay: FetchFunction = async (
|
||||
request,
|
||||
variables,
|
||||
_,
|
||||
uploadables
|
||||
uploadables,
|
||||
) => {
|
||||
const requestInit: RequestInit = {
|
||||
method: "POST",
|
||||
@@ -82,7 +89,7 @@ const fetchRelay: FetchFunction = async (
|
||||
operationName: request.name,
|
||||
query: request.text,
|
||||
variables: variables,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const uploadableMap: {
|
||||
@@ -119,13 +126,7 @@ const fetchRelay: FetchFunction = async (
|
||||
});
|
||||
}
|
||||
|
||||
// Use relative API path to ensure it goes through the same routing context
|
||||
// For /trust/slug/overview, this resolves to /trust/slug/api/trust/v1/graphql
|
||||
// For custom domains at /overview, this resolves to /api/trust/v1/graphql
|
||||
const response = await fetch(
|
||||
buildEndpoint("./api/trust/v1/graphql"),
|
||||
requestInit
|
||||
);
|
||||
const response = await fetch(buildEndpoint(), requestInit);
|
||||
|
||||
if (response.status === 500) {
|
||||
throw new InternalServerError();
|
||||
@@ -144,8 +145,8 @@ const fetchRelay: FetchFunction = async (
|
||||
if (invalidError) {
|
||||
throw new InvalidError(
|
||||
invalidError.message,
|
||||
invalidError.extensions.field as string ?? "",
|
||||
invalidError.extensions.cause as string ?? "",
|
||||
(invalidError.extensions.field as string) ?? "",
|
||||
(invalidError.extensions.cause as string) ?? "",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import {
|
||||
createBrowserRouter,
|
||||
Navigate,
|
||||
redirect,
|
||||
useRouteError,
|
||||
} from "react-router";
|
||||
import { Fragment } from "react";
|
||||
import {
|
||||
consoleEnvironment,
|
||||
UnAuthenticatedError,
|
||||
} from "./providers/RelayProviders.tsx";
|
||||
import { createBrowserRouter, redirect, useRouteError } from "react-router";
|
||||
import { Fragment, lazy } from "react";
|
||||
import { consoleEnvironment } from "./providers/RelayProviders.tsx";
|
||||
import { loadQuery } from "react-relay";
|
||||
import { PageError } from "./components/PageError.tsx";
|
||||
import { MainLayout } from "/layouts/MainLayout";
|
||||
@@ -20,7 +12,6 @@ import {
|
||||
import { OverviewPage } from "/pages/OverviewPage";
|
||||
import { DocumentsPage } from "/pages/DocumentsPage";
|
||||
import { SubprocessorsPage } from "/pages/SubprocessorsPage";
|
||||
import { AccessPage } from "./pages/AccessPage.tsx";
|
||||
import { TabSkeleton } from "./components/Skeletons/TabSkeleton.tsx";
|
||||
import { MainSkeleton } from "./components/Skeletons/MainSkeleton.tsx";
|
||||
import {
|
||||
@@ -36,14 +27,20 @@ import {
|
||||
function ErrorBoundary({ error: propsError }: { error?: string }) {
|
||||
const error = useRouteError() ?? propsError;
|
||||
|
||||
if (error instanceof UnAuthenticatedError) {
|
||||
return <Navigate to="/auth/login" />;
|
||||
}
|
||||
|
||||
return <PageError error={error?.toString()} />;
|
||||
}
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/auth",
|
||||
Component: lazy(() => import("./pages/auth/AuthLayout")),
|
||||
children: [
|
||||
{
|
||||
path: "verify-magic-link",
|
||||
Component: lazy(() => import("./pages/auth/VerifyMagicLinkPage.tsx")),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
loader: async () => {
|
||||
@@ -107,11 +104,6 @@ const routes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/access",
|
||||
Component: AccessPage,
|
||||
ErrorBoundary: ErrorBoundary,
|
||||
},
|
||||
// Fallback URL to the NotFound Page
|
||||
{
|
||||
path: "*",
|
||||
|
||||
15
apps/trust/src/utils/pathPrefix.ts
Normal file
15
apps/trust/src/utils/pathPrefix.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { matchPath } from "react-router";
|
||||
|
||||
export function getPathPrefix() {
|
||||
const match = matchPath(
|
||||
{ path: "/trust/:id", caseSensitive: false, end: false },
|
||||
window.location.pathname,
|
||||
);
|
||||
|
||||
let prefix = "";
|
||||
if (match) {
|
||||
prefix = `/trust/${match.params.id}`;
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
@@ -37,6 +37,7 @@ export default defineConfig({
|
||||
"/pages": fileURLToPath(new URL("./src/pages", import.meta.url)),
|
||||
"/routes": fileURLToPath(new URL("./src/routes", import.meta.url)),
|
||||
"/providers": fileURLToPath(new URL("./src/providers", import.meta.url)),
|
||||
"/utils": fileURLToPath(new URL("./src/utils", import.meta.url)),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ const (
|
||||
subjectFrameworkExport = "Your framework export is ready"
|
||||
subjectTrustCenterAccess = "Trust Center Access Invitation - %s"
|
||||
subjectTrustCenterDocumentAccessRejected = "Trust Center Document Access Rejected - %s"
|
||||
subjectMagicLink = "Connect to Probo"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -57,6 +58,8 @@ var (
|
||||
trustCenterAccessTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/trust-center-access.txt.tmpl"))
|
||||
trustCenterDocumentAccessRejectedHTMLTemplate = htmltemplate.Must(htmltemplate.ParseFS(Templates, "dist/trust-center-document-access-rejected.html.tmpl"))
|
||||
trustCenterDocumentAccessRejectedTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/trust-center-document-access-rejected.txt.tmpl"))
|
||||
magicLinkHTMLTemplate = htmltemplate.Must(htmltemplate.ParseFS(Templates, "dist/magic-link.html.tmpl"))
|
||||
magicLinkTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/magic-link.txt.tmpl"))
|
||||
)
|
||||
|
||||
func RenderConfirmEmail(baseURL, fullName, confirmationUrl string) (subject string, textBody string, htmlBody *string, err error) {
|
||||
@@ -199,18 +202,18 @@ func RenderTrustCenterDocumentAccessRejected(
|
||||
func RenderMagicLink(baseURL, fullName, magicLinkUrl string, tokenDuration time.Duration) (subject string, textBody string, htmlBody *string, err error) {
|
||||
data := struct {
|
||||
FullName string
|
||||
MagicLinkUrl string
|
||||
MagicLinkURL string
|
||||
LogoURL string
|
||||
DurationInMinutes int
|
||||
}{
|
||||
FullName: fullName,
|
||||
MagicLinkUrl: magicLinkUrl,
|
||||
MagicLinkURL: magicLinkUrl,
|
||||
LogoURL: baseURL + logoURLPath,
|
||||
DurationInMinutes: int(tokenDuration.Minutes()),
|
||||
}
|
||||
|
||||
textBody, htmlBody, err = renderEmail(trustCenterAccessTextTemplate, trustCenterAccessHTMLTemplate, data)
|
||||
return subjectTrustCenterAccess, textBody, htmlBody, err
|
||||
textBody, htmlBody, err = renderEmail(magicLinkTextTemplate, magicLinkHTMLTemplate, data)
|
||||
return subjectMagicLink, textBody, htmlBody, err
|
||||
}
|
||||
|
||||
func renderEmail(textTemplate *texttemplate.Template, htmlTemplate *htmltemplate.Template, data any) (textBody string, htmlBody *string, err error) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import Invitation from "../src/Invitation";
|
||||
import PasswordReset from "../src/PasswordReset";
|
||||
import TrustCenterAccess from "../src/TrustCenterAccess";
|
||||
import TrustCenterDocumentAccessRejected from "../src/TrustCenterDocumentAccessRejected";
|
||||
import MagicLink from "../src/MagicLink";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -56,7 +57,7 @@ const templates: TemplateConfig[] = [
|
||||
},
|
||||
{
|
||||
name: "magic-link",
|
||||
render: () => TrustCenterAccess(),
|
||||
render: () => MagicLink(),
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -122,18 +122,43 @@ func (b *BaseURL) WithPath(path string) *URLBuilder {
|
||||
return &URLBuilder{err: fmt.Errorf("base URL is nil")}
|
||||
}
|
||||
|
||||
// Ensure path does not end with /
|
||||
basePath := strings.TrimSuffix(b.parsed.Path, "/")
|
||||
|
||||
// Ensure path starts with /
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
|
||||
// Ensure path does not end with /
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
|
||||
return &URLBuilder{
|
||||
base: b,
|
||||
path: path,
|
||||
path: basePath + path,
|
||||
query: make(url.Values),
|
||||
}
|
||||
}
|
||||
|
||||
// WithPath returns a URLBuilder with the specified path.
|
||||
// The path will be properly joined with the base URL.
|
||||
func (ub *URLBuilder) WithPath(path string) *URLBuilder {
|
||||
if ub == nil {
|
||||
return ub
|
||||
}
|
||||
|
||||
// Ensure path starts with /
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
|
||||
// Ensure path does not end with /
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
|
||||
ub.path = ub.path + path
|
||||
return ub
|
||||
}
|
||||
|
||||
// WithQuery adds a query parameter to the URL.
|
||||
func (ub *URLBuilder) WithQuery(key, value string) *URLBuilder {
|
||||
if ub.err != nil {
|
||||
|
||||
@@ -284,16 +284,16 @@ func (p *Provisioner) provisionDomainCertificate(
|
||||
}
|
||||
|
||||
if domain.SSLStatus == coredata.CustomDomainSSLStatusPending || domain.SSLStatus == coredata.CustomDomainSSLStatusRenewing {
|
||||
if err := p.checkDNSConfiguration(domain.Domain); err != nil {
|
||||
p.logger.WarnCtx(
|
||||
ctx,
|
||||
"dns configuration check failed",
|
||||
log.String("domain", domain.Domain),
|
||||
log.Error(err),
|
||||
)
|
||||
// if err := p.checkDNSConfiguration(domain.Domain); err != nil {
|
||||
// p.logger.WarnCtx(
|
||||
// ctx,
|
||||
// "dns configuration check failed",
|
||||
// log.String("domain", domain.Domain),
|
||||
// log.Error(err),
|
||||
// )
|
||||
|
||||
return err
|
||||
}
|
||||
// return err
|
||||
// }
|
||||
|
||||
p.logger.InfoCtx(ctx, "DNS configuration verified, initiating HTTP challenge for domain", log.String("domain", domain.Domain))
|
||||
|
||||
|
||||
@@ -194,6 +194,10 @@ LIMIT 1
|
||||
|
||||
customDomain, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CustomDomain])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return ErrResourceNotFound
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect custom domain: %w", err)
|
||||
}
|
||||
|
||||
@@ -247,6 +251,10 @@ FOR UPDATE SKIP LOCKED
|
||||
|
||||
customDomain, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CustomDomain])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return ErrResourceNotFound
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect custom domain: %w", err)
|
||||
}
|
||||
|
||||
@@ -308,6 +316,63 @@ LIMIT 1
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cd *CustomDomain) LoadByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
encryptionKey cipher.EncryptionKey,
|
||||
organizationID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
domain,
|
||||
http_challenge_token,
|
||||
http_challenge_key_auth,
|
||||
http_challenge_url,
|
||||
http_order_url,
|
||||
ssl_certificate,
|
||||
encrypted_ssl_private_key,
|
||||
ssl_certificate_chain,
|
||||
ssl_status,
|
||||
ssl_expires_at,
|
||||
ssl_retry_count,
|
||||
ssl_last_attempt_at,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
custom_domains
|
||||
WHERE
|
||||
%s
|
||||
AND organization_id = @organization_id
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{"organization_id": organizationID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query custom domain: %w", err)
|
||||
}
|
||||
|
||||
customDomain, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CustomDomain])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return ErrResourceNotFound
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect custom domain: %w", err)
|
||||
}
|
||||
|
||||
*cd = customDomain
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cd *CustomDomain) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
3
pkg/coredata/migrations/20260113T220012Z.sql
Normal file
3
pkg/coredata/migrations/20260113T220012Z.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TYPE session_auth_method
|
||||
ADD
|
||||
VALUE 'MAGIC_LINK';
|
||||
@@ -54,8 +54,9 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
AuthMethodPassword AuthMethod = "PASSWORD"
|
||||
AuthMethodSAML AuthMethod = "SAML"
|
||||
AuthMethodMagicLink AuthMethod = "MAGIC_LINK"
|
||||
AuthMethodPassword AuthMethod = "PASSWORD"
|
||||
AuthMethodSAML AuthMethod = "SAML"
|
||||
)
|
||||
|
||||
func NewRootSession(identityID gid.GID, method AuthMethod, duration time.Duration) *Session {
|
||||
|
||||
@@ -62,6 +62,11 @@ type (
|
||||
FullName string
|
||||
}
|
||||
|
||||
SendMagicLinkRequest struct {
|
||||
Email mail.Addr
|
||||
BaseURL *baseurl.BaseURL
|
||||
}
|
||||
|
||||
PasswordResetData struct {
|
||||
Email mail.Addr `json:"email"`
|
||||
}
|
||||
@@ -541,36 +546,31 @@ 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 {
|
||||
func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkRequest) error {
|
||||
token, err := statelesstoken.NewToken(
|
||||
s.tokenSecret,
|
||||
TokenTypeMagicLink,
|
||||
s.magicLinkTokenValidity,
|
||||
MagicLinkData{
|
||||
Email: email,
|
||||
Email: req.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").
|
||||
magicLinkURL := req.BaseURL.
|
||||
WithPath("/auth/verify-magic-link").
|
||||
WithQuery("token", token).
|
||||
MustString()
|
||||
|
||||
return s.pg.WithTx(
|
||||
ctx,
|
||||
func(tx pg.Conn) error {
|
||||
fullName := email.Username()
|
||||
fullName := req.Email.Username()
|
||||
identity := &coredata.Identity{}
|
||||
|
||||
err := identity.LoadByEmail(ctx, tx, email)
|
||||
err := identity.LoadByEmail(ctx, tx, req.Email)
|
||||
if err == nil {
|
||||
fullName = identity.FullName
|
||||
} else {
|
||||
@@ -591,7 +591,7 @@ func (s AuthService) SendMagicLink(ctx context.Context, email mail.Addr) error {
|
||||
|
||||
magicLinkEmail := coredata.NewEmail(
|
||||
fullName,
|
||||
email,
|
||||
req.Email,
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
@@ -609,6 +609,7 @@ func (s AuthService) SendMagicLink(ctx context.Context, email mail.Addr) error {
|
||||
|
||||
func (s AuthService) OpenSessionWithMagicLink(ctx context.Context, token string) (*coredata.Identity, *coredata.Session, error) {
|
||||
var (
|
||||
now = time.Now()
|
||||
identity = &coredata.Identity{}
|
||||
session = &coredata.Session{}
|
||||
)
|
||||
@@ -618,25 +619,37 @@ func (s AuthService) OpenSessionWithMagicLink(ctx context.Context, token string)
|
||||
return nil, nil, NewInvalidTokenError()
|
||||
}
|
||||
|
||||
if err := s.pg.WithTx(
|
||||
err = s.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := identity.LoadByEmail(ctx, conn, payload.Data.Email)
|
||||
func(tx pg.Conn) error {
|
||||
err := identity.LoadByEmail(ctx, tx, payload.Data.Email)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load identity by email: %w", err)
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
identity = &coredata.Identity{
|
||||
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
||||
EmailAddress: payload.Data.Email,
|
||||
EmailAddressVerified: true,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if err := identity.Insert(ctx, tx); err != nil {
|
||||
return fmt.Errorf("cannot create identity: %w", err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("cannot load identity by email: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, s.sessionDuration)
|
||||
err = session.Insert(ctx, conn)
|
||||
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodMagicLink, s.sessionDuration)
|
||||
err = session.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert session: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
)
|
||||
|
||||
return identity, session, err
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ func NewServer(cfg Config) (*Server, error) {
|
||||
cfg.IAM,
|
||||
cfg.Trust,
|
||||
cfg.Cookie,
|
||||
cfg.BaseURL,
|
||||
),
|
||||
consoleHandler: console_v1.NewMux(
|
||||
cfg.Logger.Named("console.v1"),
|
||||
|
||||
@@ -2286,19 +2286,11 @@ directive @goModel(
|
||||
|
||||
directive @goEnum(value: String) on ENUM_VALUE
|
||||
|
||||
directive @session(required: SessionRequirement!) on FIELD_DEFINITION
|
||||
|
||||
scalar CursorKey
|
||||
scalar Datetime
|
||||
scalar Upload
|
||||
scalar EmailAddr
|
||||
|
||||
enum SessionRequirement {
|
||||
PRESENT
|
||||
NONE
|
||||
OPTIONAL
|
||||
}
|
||||
|
||||
enum OrderDirection
|
||||
@goModel(model: "go.probo.inc/probo/pkg/page.OrderDirection") {
|
||||
ASC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionAsc")
|
||||
@@ -3150,6 +3142,45 @@ type RegenerateSCIMTokenPayload {
|
||||
scimConfiguration: SCIMConfiguration!
|
||||
token: String!
|
||||
}
|
||||
`, BuiltIn: false},
|
||||
{Name: "../../../../gqlutils/directives/session/schema.graphql", Input: `# Session directive for GraphQL APIs
|
||||
# Include this schema in your gqlgen configuration to enable session-based access control.
|
||||
#
|
||||
# Usage in your schema.graphql:
|
||||
# type Query {
|
||||
# viewer: User @session(required: PRESENT)
|
||||
# publicData: Data @session(required: OPTIONAL)
|
||||
# signup(input: SignUpInput!): SignUpPayload @session(required: NONE)
|
||||
# }
|
||||
|
||||
directive @session(required: SessionRequirement!) on FIELD_DEFINITION
|
||||
|
||||
enum SessionRequirement
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/gqlutils/directives/session.SessionRequirement"
|
||||
) {
|
||||
"""
|
||||
Requires an authenticated session or API key.
|
||||
"""
|
||||
PRESENT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/server/gqlutils/directives/session.SessionRequirementPresent"
|
||||
)
|
||||
"""
|
||||
Forbids authenticated access (e.g., for login/signup endpoints).
|
||||
"""
|
||||
NONE
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/server/gqlutils/directives/session.SessionRequirementNone"
|
||||
)
|
||||
"""
|
||||
Allows both authenticated and unauthenticated access.
|
||||
"""
|
||||
OPTIONAL
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/server/gqlutils/directives/session.SessionRequirementOptional"
|
||||
)
|
||||
}
|
||||
`, BuiltIn: false},
|
||||
}
|
||||
var parsedSchema = gqlparser.MustLoadSchema(sources...)
|
||||
@@ -20616,15 +20647,35 @@ var (
|
||||
)
|
||||
|
||||
func (ec *executionContext) unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx context.Context, v any) (session.SessionRequirement, error) {
|
||||
var res session.SessionRequirement
|
||||
err := res.UnmarshalGQL(v)
|
||||
tmp, err := graphql.UnmarshalString(v)
|
||||
res := unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement[tmp]
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx context.Context, sel ast.SelectionSet, v session.SessionRequirement) graphql.Marshaler {
|
||||
return v
|
||||
_ = sel
|
||||
res := graphql.MarshalString(marshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement[v])
|
||||
if res == graphql.Null {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
var (
|
||||
unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement = map[string]session.SessionRequirement{
|
||||
"PRESENT": session.SessionRequirementPresent,
|
||||
"NONE": session.SessionRequirementNone,
|
||||
"OPTIONAL": session.SessionRequirementOptional,
|
||||
}
|
||||
marshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement = map[session.SessionRequirement]string{
|
||||
session.SessionRequirementPresent: "PRESENT",
|
||||
session.SessionRequirementNone: "NONE",
|
||||
session.SessionRequirementOptional: "OPTIONAL",
|
||||
}
|
||||
)
|
||||
|
||||
func (ec *executionContext) unmarshalNSignInInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSignInInput(ctx context.Context, v any) (types.SignInInput, error) {
|
||||
res, err := ec.unmarshalInputSignInInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
|
||||
@@ -1675,7 +1675,7 @@ 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
|
||||
// TODO: should not create an access nor identity, but send an invitation
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
var err error
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
@@ -51,6 +52,7 @@ type (
|
||||
logger *log.Logger
|
||||
iam *iam.Service
|
||||
sessionCookie *authn.Cookie
|
||||
baseURL *baseurl.BaseURL
|
||||
}
|
||||
)
|
||||
|
||||
@@ -77,6 +79,7 @@ func NewMux(
|
||||
iamSvc *iam.Service,
|
||||
trustSvc *trust.Service,
|
||||
cookieConfig securecookie.Config,
|
||||
baseURL *baseurl.BaseURL,
|
||||
) *chi.Mux {
|
||||
r := chi.NewMux()
|
||||
|
||||
@@ -89,6 +92,7 @@ func NewMux(
|
||||
trust: trustSvc,
|
||||
logger: logger,
|
||||
sessionCookie: authn.NewCookie(&cookieConfig),
|
||||
baseURL: baseURL,
|
||||
},
|
||||
Directives: schema.DirectiveRoot{
|
||||
Session: session.Directive,
|
||||
|
||||
@@ -542,17 +542,20 @@ type TrustCenterAccess implements Node {
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
input SignInWithTokenInput {
|
||||
token: String!
|
||||
input SendMagicLinkInput {
|
||||
email: EmailAddr!
|
||||
}
|
||||
|
||||
type SignInWithTokenPayload {
|
||||
type SendMagicLinkPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
input RequestAllAccessesInput {
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
input VerifyMagicLinkInput {
|
||||
token: String!
|
||||
}
|
||||
|
||||
type VerifyMagicLinkPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type RequestAccessesPayload {
|
||||
@@ -569,20 +572,14 @@ input ExportReportPDFInput {
|
||||
|
||||
input RequestDocumentAccessInput {
|
||||
documentId: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
}
|
||||
|
||||
input RequestReportAccessInput {
|
||||
reportId: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
}
|
||||
|
||||
input RequestTrustCenterFileAccessInput {
|
||||
trustCenterFileId: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
}
|
||||
|
||||
input ExportTrustCenterFileInput {
|
||||
@@ -608,14 +605,14 @@ type AcceptNonDisclosureAgreementPayload {
|
||||
type Query {
|
||||
viewer: Identity
|
||||
node(id: ID!): Node!
|
||||
currentTrustCenter: TrustCenter @session(required: NONE)
|
||||
currentTrustCenter: TrustCenter
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
signInWithToken(input: SignInWithTokenInput!): SignInWithTokenPayload!
|
||||
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
|
||||
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
|
||||
|
||||
requestAllAccesses(input: RequestAllAccessesInput!): RequestAccessesPayload!
|
||||
@session(required: NONE)
|
||||
requestAllAccesses: RequestAccessesPayload!
|
||||
|
||||
exportDocumentPDF(input: ExportDocumentPDFInput!): ExportDocumentPDFPayload!
|
||||
@session(required: NONE)
|
||||
@@ -628,17 +625,15 @@ type Mutation {
|
||||
|
||||
requestDocumentAccess(
|
||||
input: RequestDocumentAccessInput!
|
||||
): RequestAccessesPayload! @session(required: NONE)
|
||||
): RequestAccessesPayload!
|
||||
|
||||
requestReportAccess(
|
||||
input: RequestReportAccessInput!
|
||||
): RequestAccessesPayload! @session(required: NONE)
|
||||
requestReportAccess(input: RequestReportAccessInput!): RequestAccessesPayload!
|
||||
|
||||
requestTrustCenterFileAccess(
|
||||
input: RequestTrustCenterFileAccessInput!
|
||||
): RequestAccessesPayload! @session(required: NONE)
|
||||
): RequestAccessesPayload!
|
||||
|
||||
exportTrustCenterFile(
|
||||
input: ExportTrustCenterFileInput!
|
||||
): ExportTrustCenterFilePayload! @session(required: NONE)
|
||||
): ExportTrustCenterFilePayload!
|
||||
}
|
||||
|
||||
@@ -135,11 +135,12 @@ type ComplexityRoot struct {
|
||||
ExportDocumentPDF func(childComplexity int, input types.ExportDocumentPDFInput) int
|
||||
ExportReportPDF func(childComplexity int, input types.ExportReportPDFInput) int
|
||||
ExportTrustCenterFile func(childComplexity int, input types.ExportTrustCenterFileInput) int
|
||||
RequestAllAccesses func(childComplexity int, input types.RequestAllAccessesInput) int
|
||||
RequestAllAccesses func(childComplexity int) int
|
||||
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
|
||||
SendMagicLink func(childComplexity int, input types.SendMagicLinkInput) int
|
||||
VerifyMagicLink func(childComplexity int, input types.VerifyMagicLinkInput) int
|
||||
}
|
||||
|
||||
Organization struct {
|
||||
@@ -176,7 +177,7 @@ type ComplexityRoot struct {
|
||||
TrustCenterAccess func(childComplexity int) int
|
||||
}
|
||||
|
||||
SignInWithTokenPayload struct {
|
||||
SendMagicLinkPayload struct {
|
||||
Success func(childComplexity int) int
|
||||
}
|
||||
|
||||
@@ -258,6 +259,10 @@ type ComplexityRoot struct {
|
||||
Cursor func(childComplexity int) int
|
||||
Node func(childComplexity int) int
|
||||
}
|
||||
|
||||
VerifyMagicLinkPayload struct {
|
||||
Success func(childComplexity int) int
|
||||
}
|
||||
}
|
||||
|
||||
type AuditResolver interface {
|
||||
@@ -273,8 +278,9 @@ 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)
|
||||
SendMagicLink(ctx context.Context, input types.SendMagicLinkInput) (*types.SendMagicLinkPayload, error)
|
||||
VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error)
|
||||
RequestAllAccesses(ctx context.Context) (*types.RequestAccessesPayload, error)
|
||||
ExportDocumentPDF(ctx context.Context, input types.ExportDocumentPDFInput) (*types.ExportDocumentPDFPayload, error)
|
||||
ExportReportPDF(ctx context.Context, input types.ExportReportPDFInput) (*types.ExportReportPDFPayload, error)
|
||||
AcceptNonDisclosureAgreement(ctx context.Context) (*types.AcceptNonDisclosureAgreementPayload, error)
|
||||
@@ -569,12 +575,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_requestAllAccesses_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.RequestAllAccesses(childComplexity, args["input"].(types.RequestAllAccessesInput)), true
|
||||
return e.complexity.Mutation.RequestAllAccesses(childComplexity), true
|
||||
case "Mutation.requestDocumentAccess":
|
||||
if e.complexity.Mutation.RequestDocumentAccess == nil {
|
||||
break
|
||||
@@ -608,17 +609,28 @@ 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 {
|
||||
case "Mutation.sendMagicLink":
|
||||
if e.complexity.Mutation.SendMagicLink == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_signInWithToken_args(ctx, rawArgs)
|
||||
args, err := ec.field_Mutation_sendMagicLink_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.SignInWithToken(childComplexity, args["input"].(types.SignInWithTokenInput)), true
|
||||
return e.complexity.Mutation.SendMagicLink(childComplexity, args["input"].(types.SendMagicLinkInput)), true
|
||||
case "Mutation.verifyMagicLink":
|
||||
if e.complexity.Mutation.VerifyMagicLink == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_verifyMagicLink_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.VerifyMagicLink(childComplexity, args["input"].(types.VerifyMagicLinkInput)), true
|
||||
|
||||
case "Organization.description":
|
||||
if e.complexity.Organization.Description == nil {
|
||||
@@ -744,12 +756,12 @@ 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 {
|
||||
case "SendMagicLinkPayload.success":
|
||||
if e.complexity.SendMagicLinkPayload.Success == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.SignInWithTokenPayload.Success(childComplexity), true
|
||||
return e.complexity.SendMagicLinkPayload.Success(childComplexity), true
|
||||
|
||||
case "TrustCenter.active":
|
||||
if e.complexity.TrustCenter.Active == nil {
|
||||
@@ -1063,6 +1075,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.VendorEdge.Node(childComplexity), true
|
||||
|
||||
case "VerifyMagicLinkPayload.success":
|
||||
if e.complexity.VerifyMagicLinkPayload.Success == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.VerifyMagicLinkPayload.Success(childComplexity), true
|
||||
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
@@ -1074,11 +1093,11 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
ec.unmarshalInputExportDocumentPDFInput,
|
||||
ec.unmarshalInputExportReportPDFInput,
|
||||
ec.unmarshalInputExportTrustCenterFileInput,
|
||||
ec.unmarshalInputRequestAllAccessesInput,
|
||||
ec.unmarshalInputRequestDocumentAccessInput,
|
||||
ec.unmarshalInputRequestReportAccessInput,
|
||||
ec.unmarshalInputRequestTrustCenterFileAccessInput,
|
||||
ec.unmarshalInputSignInWithTokenInput,
|
||||
ec.unmarshalInputSendMagicLinkInput,
|
||||
ec.unmarshalInputVerifyMagicLinkInput,
|
||||
)
|
||||
first := true
|
||||
|
||||
@@ -1720,17 +1739,20 @@ type TrustCenterAccess implements Node {
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
input SignInWithTokenInput {
|
||||
token: String!
|
||||
input SendMagicLinkInput {
|
||||
email: EmailAddr!
|
||||
}
|
||||
|
||||
type SignInWithTokenPayload {
|
||||
type SendMagicLinkPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
input RequestAllAccessesInput {
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
input VerifyMagicLinkInput {
|
||||
token: String!
|
||||
}
|
||||
|
||||
type VerifyMagicLinkPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type RequestAccessesPayload {
|
||||
@@ -1747,20 +1769,14 @@ input ExportReportPDFInput {
|
||||
|
||||
input RequestDocumentAccessInput {
|
||||
documentId: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
}
|
||||
|
||||
input RequestReportAccessInput {
|
||||
reportId: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
}
|
||||
|
||||
input RequestTrustCenterFileAccessInput {
|
||||
trustCenterFileId: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
}
|
||||
|
||||
input ExportTrustCenterFileInput {
|
||||
@@ -1786,14 +1802,14 @@ type AcceptNonDisclosureAgreementPayload {
|
||||
type Query {
|
||||
viewer: Identity
|
||||
node(id: ID!): Node!
|
||||
currentTrustCenter: TrustCenter @session(required: NONE)
|
||||
currentTrustCenter: TrustCenter
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
signInWithToken(input: SignInWithTokenInput!): SignInWithTokenPayload!
|
||||
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
|
||||
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
|
||||
|
||||
requestAllAccesses(input: RequestAllAccessesInput!): RequestAccessesPayload!
|
||||
@session(required: NONE)
|
||||
requestAllAccesses: RequestAccessesPayload!
|
||||
|
||||
exportDocumentPDF(input: ExportDocumentPDFInput!): ExportDocumentPDFPayload!
|
||||
@session(required: NONE)
|
||||
@@ -1806,19 +1822,17 @@ type Mutation {
|
||||
|
||||
requestDocumentAccess(
|
||||
input: RequestDocumentAccessInput!
|
||||
): RequestAccessesPayload! @session(required: NONE)
|
||||
): RequestAccessesPayload!
|
||||
|
||||
requestReportAccess(
|
||||
input: RequestReportAccessInput!
|
||||
): RequestAccessesPayload! @session(required: NONE)
|
||||
requestReportAccess(input: RequestReportAccessInput!): RequestAccessesPayload!
|
||||
|
||||
requestTrustCenterFileAccess(
|
||||
input: RequestTrustCenterFileAccessInput!
|
||||
): RequestAccessesPayload! @session(required: NONE)
|
||||
): RequestAccessesPayload!
|
||||
|
||||
exportTrustCenterFile(
|
||||
input: ExportTrustCenterFileInput!
|
||||
): ExportTrustCenterFilePayload! @session(required: NONE)
|
||||
): ExportTrustCenterFilePayload!
|
||||
}
|
||||
`, BuiltIn: false},
|
||||
{Name: "../../../../gqlutils/directives/session/schema.graphql", Input: `# Session directive for GraphQL APIs
|
||||
@@ -1911,17 +1925,6 @@ func (ec *executionContext) field_Mutation_exportTrustCenterFile_args(ctx contex
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_requestAllAccesses_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.unmarshalNRequestAllAccessesInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAllAccessesInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["input"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_requestDocumentAccess_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
@@ -1955,10 +1958,21 @@ 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) {
|
||||
func (ec *executionContext) field_Mutation_sendMagicLink_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)
|
||||
arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNSendMagicLinkInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSendMagicLinkInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["input"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_verifyMagicLink_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.unmarshalNVerifyMagicLinkInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐVerifyMagicLinkInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -3112,24 +3126,24 @@ func (ec *executionContext) fieldContext_Identity_updatedAt(_ context.Context, f
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_signInWithToken(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _Mutation_sendMagicLink(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_Mutation_signInWithToken,
|
||||
ec.fieldContext_Mutation_sendMagicLink,
|
||||
func(ctx context.Context) (any, error) {
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().SignInWithToken(ctx, fc.Args["input"].(types.SignInWithTokenInput))
|
||||
return ec.resolvers.Mutation().SendMagicLink(ctx, fc.Args["input"].(types.SendMagicLinkInput))
|
||||
},
|
||||
nil,
|
||||
ec.marshalNSignInWithTokenPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSignInWithTokenPayload,
|
||||
true,
|
||||
ec.marshalOSendMagicLinkPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSendMagicLinkPayload,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Mutation_signInWithToken(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
func (ec *executionContext) fieldContext_Mutation_sendMagicLink(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
@@ -3138,9 +3152,9 @@ func (ec *executionContext) fieldContext_Mutation_signInWithToken(ctx context.Co
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "success":
|
||||
return ec.fieldContext_SignInWithTokenPayload_success(ctx, field)
|
||||
return ec.fieldContext_SendMagicLinkPayload_success(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type SignInWithTokenPayload", field.Name)
|
||||
return nil, fmt.Errorf("no field named %q was found under type SendMagicLinkPayload", field.Name)
|
||||
},
|
||||
}
|
||||
defer func() {
|
||||
@@ -3150,7 +3164,52 @@ func (ec *executionContext) fieldContext_Mutation_signInWithToken(ctx context.Co
|
||||
}
|
||||
}()
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
if fc.Args, err = ec.field_Mutation_signInWithToken_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
if fc.Args, err = ec.field_Mutation_sendMagicLink_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return fc, err
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_verifyMagicLink(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_Mutation_verifyMagicLink,
|
||||
func(ctx context.Context) (any, error) {
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().VerifyMagicLink(ctx, fc.Args["input"].(types.VerifyMagicLinkInput))
|
||||
},
|
||||
nil,
|
||||
ec.marshalOVerifyMagicLinkPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐVerifyMagicLinkPayload,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Mutation_verifyMagicLink(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_VerifyMagicLinkPayload_success(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type VerifyMagicLinkPayload", 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_verifyMagicLink_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return fc, err
|
||||
}
|
||||
@@ -3164,35 +3223,16 @@ func (ec *executionContext) _Mutation_requestAllAccesses(ctx context.Context, fi
|
||||
field,
|
||||
ec.fieldContext_Mutation_requestAllAccesses,
|
||||
func(ctx context.Context) (any, error) {
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().RequestAllAccesses(ctx, fc.Args["input"].(types.RequestAllAccessesInput))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
||||
if err != nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
return ec.resolvers.Mutation().RequestAllAccesses(ctx)
|
||||
},
|
||||
nil,
|
||||
ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Mutation_requestAllAccesses(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
func (ec *executionContext) fieldContext_Mutation_requestAllAccesses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
@@ -3206,17 +3246,6 @@ func (ec *executionContext) fieldContext_Mutation_requestAllAccesses(ctx context
|
||||
return nil, fmt.Errorf("no field named %q was found under type RequestAccessesPayload", 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_requestAllAccesses_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return fc, err
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
@@ -3407,25 +3436,7 @@ func (ec *executionContext) _Mutation_requestDocumentAccess(ctx context.Context,
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().RequestDocumentAccess(ctx, fc.Args["input"].(types.RequestDocumentAccessInput))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
||||
if err != nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload,
|
||||
true,
|
||||
true,
|
||||
@@ -3470,25 +3481,7 @@ func (ec *executionContext) _Mutation_requestReportAccess(ctx context.Context, f
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().RequestReportAccess(ctx, fc.Args["input"].(types.RequestReportAccessInput))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
||||
if err != nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload,
|
||||
true,
|
||||
true,
|
||||
@@ -3533,25 +3526,7 @@ func (ec *executionContext) _Mutation_requestTrustCenterFileAccess(ctx context.C
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().RequestTrustCenterFileAccess(ctx, fc.Args["input"].(types.RequestTrustCenterFileAccessInput))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
||||
if err != nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.RequestAccessesPayload
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalNRequestAccessesPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAccessesPayload,
|
||||
true,
|
||||
true,
|
||||
@@ -3596,25 +3571,7 @@ func (ec *executionContext) _Mutation_exportTrustCenterFile(ctx context.Context,
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().ExportTrustCenterFile(ctx, fc.Args["input"].(types.ExportTrustCenterFileInput))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
||||
if err != nil {
|
||||
var zeroVal *types.ExportTrustCenterFilePayload
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.ExportTrustCenterFilePayload
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalNExportTrustCenterFilePayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐExportTrustCenterFilePayload,
|
||||
true,
|
||||
true,
|
||||
@@ -4061,25 +4018,7 @@ func (ec *executionContext) _Query_currentTrustCenter(ctx context.Context, field
|
||||
func(ctx context.Context) (any, error) {
|
||||
return ec.resolvers.Query().CurrentTrustCenter(ctx)
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
||||
if err != nil {
|
||||
var zeroVal *types.TrustCenter
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.TrustCenter
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalOTrustCenter2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐTrustCenter,
|
||||
true,
|
||||
false,
|
||||
@@ -4392,12 +4331,12 @@ 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) {
|
||||
func (ec *executionContext) _SendMagicLinkPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.SendMagicLinkPayload) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_SignInWithTokenPayload_success,
|
||||
ec.fieldContext_SendMagicLinkPayload_success,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Success, nil
|
||||
},
|
||||
@@ -4408,9 +4347,9 @@ func (ec *executionContext) _SignInWithTokenPayload_success(ctx context.Context,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_SignInWithTokenPayload_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
func (ec *executionContext) fieldContext_SendMagicLinkPayload_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "SignInWithTokenPayload",
|
||||
Object: "SendMagicLinkPayload",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
@@ -5947,6 +5886,35 @@ func (ec *executionContext) fieldContext_VendorEdge_node(_ context.Context, fiel
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _VerifyMagicLinkPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.VerifyMagicLinkPayload) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_VerifyMagicLinkPayload_success,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Success, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNBoolean2bool,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_VerifyMagicLinkPayload_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "VerifyMagicLinkPayload",
|
||||
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) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -7474,40 +7442,6 @@ func (ec *executionContext) unmarshalInputExportTrustCenterFileInput(ctx context
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputRequestAllAccessesInput(ctx context.Context, obj any) (types.RequestAllAccessesInput, error) {
|
||||
var it types.RequestAllAccessesInput
|
||||
asMap := map[string]any{}
|
||||
for k, v := range obj.(map[string]any) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"email", "fullName"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch k {
|
||||
case "email":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Email = data
|
||||
case "fullName":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.FullName = data
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputRequestDocumentAccessInput(ctx context.Context, obj any) (types.RequestDocumentAccessInput, error) {
|
||||
var it types.RequestDocumentAccessInput
|
||||
asMap := map[string]any{}
|
||||
@@ -7515,7 +7449,7 @@ func (ec *executionContext) unmarshalInputRequestDocumentAccessInput(ctx context
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"documentId", "email", "fullName"}
|
||||
fieldsInOrder := [...]string{"documentId"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -7529,20 +7463,6 @@ func (ec *executionContext) unmarshalInputRequestDocumentAccessInput(ctx context
|
||||
return it, err
|
||||
}
|
||||
it.DocumentID = data
|
||||
case "email":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Email = data
|
||||
case "fullName":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.FullName = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7556,7 +7476,7 @@ func (ec *executionContext) unmarshalInputRequestReportAccessInput(ctx context.C
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"reportId", "email", "fullName"}
|
||||
fieldsInOrder := [...]string{"reportId"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -7570,20 +7490,6 @@ func (ec *executionContext) unmarshalInputRequestReportAccessInput(ctx context.C
|
||||
return it, err
|
||||
}
|
||||
it.ReportID = data
|
||||
case "email":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Email = data
|
||||
case "fullName":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.FullName = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7597,7 +7503,7 @@ func (ec *executionContext) unmarshalInputRequestTrustCenterFileAccessInput(ctx
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"trustCenterFileId", "email", "fullName"}
|
||||
fieldsInOrder := [...]string{"trustCenterFileId"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -7611,6 +7517,26 @@ func (ec *executionContext) unmarshalInputRequestTrustCenterFileAccessInput(ctx
|
||||
return it, err
|
||||
}
|
||||
it.TrustCenterFileID = data
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputSendMagicLinkInput(ctx context.Context, obj any) (types.SendMagicLinkInput, error) {
|
||||
var it types.SendMagicLinkInput
|
||||
asMap := map[string]any{}
|
||||
for k, v := range obj.(map[string]any) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"email"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch k {
|
||||
case "email":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
|
||||
@@ -7618,21 +7544,14 @@ func (ec *executionContext) unmarshalInputRequestTrustCenterFileAccessInput(ctx
|
||||
return it, err
|
||||
}
|
||||
it.Email = data
|
||||
case "fullName":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.FullName = data
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputSignInWithTokenInput(ctx context.Context, obj any) (types.SignInWithTokenInput, error) {
|
||||
var it types.SignInWithTokenInput
|
||||
func (ec *executionContext) unmarshalInputVerifyMagicLinkInput(ctx context.Context, obj any) (types.VerifyMagicLinkInput, error) {
|
||||
var it types.VerifyMagicLinkInput
|
||||
asMap := map[string]any{}
|
||||
for k, v := range obj.(map[string]any) {
|
||||
asMap[k] = v
|
||||
@@ -8506,13 +8425,14 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Mutation")
|
||||
case "signInWithToken":
|
||||
case "sendMagicLink":
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_signInWithToken(ctx, field)
|
||||
return ec._Mutation_sendMagicLink(ctx, field)
|
||||
})
|
||||
case "verifyMagicLink":
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_verifyMagicLink(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)
|
||||
@@ -8990,19 +8910,19 @@ func (ec *executionContext) _RequestAccessesPayload(ctx context.Context, sel ast
|
||||
return out
|
||||
}
|
||||
|
||||
var signInWithTokenPayloadImplementors = []string{"SignInWithTokenPayload"}
|
||||
var sendMagicLinkPayloadImplementors = []string{"SendMagicLinkPayload"}
|
||||
|
||||
func (ec *executionContext) _SignInWithTokenPayload(ctx context.Context, sel ast.SelectionSet, obj *types.SignInWithTokenPayload) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, signInWithTokenPayloadImplementors)
|
||||
func (ec *executionContext) _SendMagicLinkPayload(ctx context.Context, sel ast.SelectionSet, obj *types.SendMagicLinkPayload) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, sendMagicLinkPayloadImplementors)
|
||||
|
||||
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")
|
||||
out.Values[i] = graphql.MarshalString("SendMagicLinkPayload")
|
||||
case "success":
|
||||
out.Values[i] = ec._SignInWithTokenPayload_success(ctx, field, obj)
|
||||
out.Values[i] = ec._SendMagicLinkPayload_success(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
@@ -9990,6 +9910,45 @@ func (ec *executionContext) _VendorEdge(ctx context.Context, sel ast.SelectionSe
|
||||
return out
|
||||
}
|
||||
|
||||
var verifyMagicLinkPayloadImplementors = []string{"VerifyMagicLinkPayload"}
|
||||
|
||||
func (ec *executionContext) _VerifyMagicLinkPayload(ctx context.Context, sel ast.SelectionSet, obj *types.VerifyMagicLinkPayload) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, verifyMagicLinkPayloadImplementors)
|
||||
|
||||
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("VerifyMagicLinkPayload")
|
||||
case "success":
|
||||
out.Values[i] = ec._VerifyMagicLinkPayload_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 __DirectiveImplementors = []string{"__Directive"}
|
||||
|
||||
func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler {
|
||||
@@ -11307,11 +11266,6 @@ func (ec *executionContext) marshalNRequestAccessesPayload2ᚖgoᚗproboᚗinc
|
||||
return ec._RequestAccessesPayload(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNRequestAllAccessesInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestAllAccessesInput(ctx context.Context, v any) (types.RequestAllAccessesInput, error) {
|
||||
res, err := ec.unmarshalInputRequestAllAccessesInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNRequestDocumentAccessInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐRequestDocumentAccessInput(ctx context.Context, v any) (types.RequestDocumentAccessInput, error) {
|
||||
res, err := ec.unmarshalInputRequestDocumentAccessInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
@@ -11327,6 +11281,11 @@ func (ec *executionContext) unmarshalNRequestTrustCenterFileAccessInput2goᚗpro
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNSendMagicLinkInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSendMagicLinkInput(ctx context.Context, v any) (types.SendMagicLinkInput, error) {
|
||||
res, err := ec.unmarshalInputSendMagicLinkInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx context.Context, v any) (session.SessionRequirement, error) {
|
||||
tmp, err := graphql.UnmarshalString(v)
|
||||
res := unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement[tmp]
|
||||
@@ -11357,25 +11316,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -11704,6 +11644,11 @@ func (ec *executionContext) marshalNVendorEdge2ᚖgoᚗproboᚗincᚋproboᚋpkg
|
||||
return ec._VendorEdge(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNVerifyMagicLinkInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐVerifyMagicLinkInput(ctx context.Context, v any) (types.VerifyMagicLinkInput, error) {
|
||||
res, err := ec.unmarshalInputVerifyMagicLinkInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler {
|
||||
return ec.___Directive(ctx, sel, &v)
|
||||
}
|
||||
@@ -12037,6 +11982,13 @@ func (ec *executionContext) marshalOReport2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋs
|
||||
return ec._Report(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOSendMagicLinkPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐSendMagicLinkPayload(ctx context.Context, sel ast.SelectionSet, v *types.SendMagicLinkPayload) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._SendMagicLinkPayload(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOString2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
@@ -12098,6 +12050,13 @@ func (ec *executionContext) marshalOTrustCenter2ᚖgoᚗproboᚗincᚋproboᚋpk
|
||||
return ec._TrustCenter(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOVerifyMagicLinkPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐVerifyMagicLinkPayload(ctx context.Context, sel ast.SelectionSet, v *types.VerifyMagicLinkPayload) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._VerifyMagicLinkPayload(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
|
||||
@@ -150,34 +150,23 @@ type RequestAccessesPayload struct {
|
||||
TrustCenterAccess *TrustCenterAccess `json:"trustCenterAccess"`
|
||||
}
|
||||
|
||||
type RequestAllAccessesInput struct {
|
||||
Email mail.Addr `json:"email"`
|
||||
FullName string `json:"fullName"`
|
||||
}
|
||||
|
||||
type RequestDocumentAccessInput struct {
|
||||
DocumentID gid.GID `json:"documentId"`
|
||||
Email mail.Addr `json:"email"`
|
||||
FullName string `json:"fullName"`
|
||||
DocumentID gid.GID `json:"documentId"`
|
||||
}
|
||||
|
||||
type RequestReportAccessInput struct {
|
||||
ReportID gid.GID `json:"reportId"`
|
||||
Email mail.Addr `json:"email"`
|
||||
FullName string `json:"fullName"`
|
||||
ReportID gid.GID `json:"reportId"`
|
||||
}
|
||||
|
||||
type RequestTrustCenterFileAccessInput struct {
|
||||
TrustCenterFileID gid.GID `json:"trustCenterFileId"`
|
||||
Email mail.Addr `json:"email"`
|
||||
FullName string `json:"fullName"`
|
||||
TrustCenterFileID gid.GID `json:"trustCenterFileId"`
|
||||
}
|
||||
|
||||
type SignInWithTokenInput struct {
|
||||
Token string `json:"token"`
|
||||
type SendMagicLinkInput struct {
|
||||
Email mail.Addr `json:"email"`
|
||||
}
|
||||
|
||||
type SignInWithTokenPayload struct {
|
||||
type SendMagicLinkPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
@@ -275,6 +264,14 @@ type VendorEdge struct {
|
||||
Node *Vendor `json:"node"`
|
||||
}
|
||||
|
||||
type VerifyMagicLinkInput struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type VerifyMagicLinkPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type Role string
|
||||
|
||||
const (
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
@@ -84,7 +85,7 @@ func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Docu
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return false, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
documentAccess, err := trustService.TrustCenterAccesses.LoadDocumentAccess(
|
||||
@@ -143,8 +144,68 @@ func (r *frameworkResolver) DarkLogoURL(ctx context.Context, obj *types.Framewor
|
||||
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) {
|
||||
// SendMagicLink is the resolver for the sendMagicLink field.
|
||||
func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMagicLinkInput) (*types.SendMagicLinkPayload, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity != nil {
|
||||
return nil, gqlutils.AlreadyAuthenticatedf(ctx, "already authenticated")
|
||||
}
|
||||
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
organization, err := r.iam.OrganizationService.GetOrganization(ctx, trustCenter.OrganizationID)
|
||||
if err != nil {
|
||||
var errNotFound *iam.ErrOrganizationNotFound
|
||||
if errors.As(err, &errNotFound) {
|
||||
return nil, gqlutils.NotFoundf(ctx, "organization not found")
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
customDomain, err := r.trust.GetCustomDomainByOrganizationID(ctx, organization.ID)
|
||||
if err != nil {
|
||||
var errNotFound *iam.ErrOrganizationNotFound
|
||||
if !errors.As(err, &errNotFound) {
|
||||
r.logger.ErrorCtx(ctx, "cannot get custom domain", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
req := &iam.SendMagicLinkRequest{
|
||||
Email: input.Email,
|
||||
}
|
||||
|
||||
if false {
|
||||
// if customDomain != nil {
|
||||
baseURL, err := baseurl.Parse(fmt.Sprintf("https://%s", customDomain.Domain))
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot parse custom domain url", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
req.BaseURL = baseURL
|
||||
} else {
|
||||
baseURL, err := baseurl.Parse(r.baseURL.WithPath("/trust/" + trustCenter.ID.String()).MustString())
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot parse url", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
req.BaseURL = baseURL
|
||||
}
|
||||
|
||||
if err := r.iam.AuthService.SendMagicLink(ctx, req); err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot send magic link", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// VerifyMagicLink is the resolver for the verifyMagicLink field.
|
||||
func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) {
|
||||
_, session, err := r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
||||
if err != nil {
|
||||
var errInvalidToken *iam.ErrInvalidToken
|
||||
@@ -156,33 +217,23 @@ func (r *mutationResolver) SignInWithToken(ctx context.Context, input types.Sign
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
// TODO cookie domain
|
||||
// FIXME: cookie domain
|
||||
w := gqlutils.HTTPResponseWriterFromContext(ctx)
|
||||
r.sessionCookie.Set(w, session)
|
||||
|
||||
return &types.SignInWithTokenPayload{
|
||||
return &types.VerifyMagicLinkPayload{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RequestAllAccesses is the resolver for the requestAllAccesses field.
|
||||
func (r *mutationResolver) RequestAllAccesses(ctx context.Context, input types.RequestAllAccessesInput) (*types.RequestAccessesPayload, error) {
|
||||
func (r *mutationResolver) RequestAllAccesses(ctx context.Context) (*types.RequestAccessesPayload, error) {
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
|
||||
|
||||
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)
|
||||
}
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
|
||||
}
|
||||
|
||||
access, err := trustService.TrustCenterAccesses.Request(
|
||||
@@ -242,10 +293,6 @@ func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.Ex
|
||||
ndaExists := true
|
||||
hasAcceptedNDA := false
|
||||
|
||||
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
|
||||
}
|
||||
@@ -296,6 +343,11 @@ func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.Ex
|
||||
|
||||
// ExportReportPDF is the resolver for the exportReportPDF field.
|
||||
func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.ExportReportPDFInput) (*types.ExportReportPDFPayload, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
|
||||
trustService := r.TrustService(ctx, input.ReportID.TenantID())
|
||||
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
@@ -318,18 +370,9 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
|
||||
}, nil
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
|
||||
ndaExists := true
|
||||
hasAcceptedNDA := false
|
||||
|
||||
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
|
||||
}
|
||||
@@ -380,13 +423,12 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
|
||||
|
||||
// AcceptNonDisclosureAgreement is the resolver for the acceptNonDisclosureAgreement field.
|
||||
func (r *mutationResolver) AcceptNonDisclosureAgreement(ctx context.Context) (*types.AcceptNonDisclosureAgreementPayload, error) {
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
|
||||
|
||||
httpReq := gqlutils.HTTPRequestFromContext(ctx)
|
||||
|
||||
@@ -424,17 +466,7 @@ func (r *mutationResolver) RequestDocumentAccess(ctx context.Context, input type
|
||||
|
||||
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)
|
||||
}
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
|
||||
}
|
||||
|
||||
access, err := trustService.TrustCenterAccesses.Request(
|
||||
@@ -483,17 +515,7 @@ func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types.
|
||||
|
||||
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)
|
||||
}
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
|
||||
}
|
||||
|
||||
access, err := trustService.TrustCenterAccesses.Request(
|
||||
@@ -542,17 +564,7 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp
|
||||
|
||||
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)
|
||||
}
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
|
||||
}
|
||||
|
||||
access, err := trustService.TrustCenterAccesses.Request(
|
||||
@@ -584,6 +596,11 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp
|
||||
|
||||
// ExportTrustCenterFile is the resolver for the exportTrustCenterFile field.
|
||||
func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input types.ExportTrustCenterFileInput) (*types.ExportTrustCenterFilePayload, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
|
||||
|
||||
@@ -605,11 +622,6 @@ func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input type
|
||||
}, nil
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
|
||||
ndaExists := true
|
||||
hasAcceptedNDA := false
|
||||
|
||||
@@ -782,6 +794,11 @@ func (r *queryResolver) CurrentTrustCenter(ctx context.Context) (*types.TrustCen
|
||||
|
||||
// IsUserAuthorized is the resolver for the isUserAuthorized field.
|
||||
func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report) (bool, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
trustService := r.TrustService(ctx, obj.ID.TenantID())
|
||||
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
@@ -796,11 +813,6 @@ func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report
|
||||
return true, nil
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return false, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
|
||||
reportAccess, err := trustService.TrustCenterAccesses.LoadReportAccess(ctx,
|
||||
trustCenter.ID,
|
||||
identity.EmailAddress,
|
||||
@@ -984,6 +996,11 @@ 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) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
trustService := r.TrustService(ctx, obj.ID.TenantID())
|
||||
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
@@ -998,11 +1015,6 @@ func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *typ
|
||||
return true, nil
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return false, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
|
||||
fileAccess, err := trustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx,
|
||||
trustCenter.ID,
|
||||
identity.EmailAddress,
|
||||
|
||||
@@ -25,6 +25,19 @@ import (
|
||||
"go.probo.inc/probo/pkg/validator"
|
||||
)
|
||||
|
||||
func AlreadyAuthenticated(ctx context.Context, err error) *gqlerror.Error {
|
||||
return &gqlerror.Error{
|
||||
Message: "Authentication not allowed for this resource/action",
|
||||
Extensions: map[string]any{
|
||||
"code": "ALREADY_AUTHENTICATED",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func AlreadyAuthenticatedf(ctx context.Context, format string, a ...any) *gqlerror.Error {
|
||||
return AlreadyAuthenticated(ctx, fmt.Errorf(format, a...))
|
||||
}
|
||||
|
||||
func Unauthenticated(ctx context.Context, err error) *gqlerror.Error {
|
||||
return &gqlerror.Error{
|
||||
Message: err.Error(),
|
||||
@@ -39,15 +52,6 @@ func Unauthenticatedf(ctx context.Context, format string, a ...any) *gqlerror.Er
|
||||
return Unauthenticated(ctx, fmt.Errorf(format, a...))
|
||||
}
|
||||
|
||||
func AlreadyUnauthenticated(ctx context.Context, err error) *gqlerror.Error {
|
||||
return &gqlerror.Error{
|
||||
Message: "Authentication not allowed for this resource/action",
|
||||
Extensions: map[string]any{
|
||||
"code": "ALREADY_AUTHENTICATED",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Forbidden(ctx context.Context, err error) *gqlerror.Error {
|
||||
return &gqlerror.Error{
|
||||
Message: err.Error(),
|
||||
|
||||
@@ -149,6 +149,8 @@ func (s *Server) stripTrustPrefix(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
r.URL.Path = strings.TrimPrefix(r.URL.Path, prefix)
|
||||
r.URL.Path = strings.TrimPrefix(r.URL.Path, "/auth")
|
||||
|
||||
if r.URL.Path == "" {
|
||||
r.URL.Path = "/"
|
||||
}
|
||||
|
||||
@@ -229,3 +229,16 @@ func (s *Service) GetByDomainName(ctx context.Context, domain string) (*coredata
|
||||
|
||||
return trustCenter, err
|
||||
}
|
||||
|
||||
func (s *Service) GetCustomDomainByOrganizationID(ctx context.Context, organizationID gid.GID) (*coredata.CustomDomain, error) {
|
||||
customDomain := &coredata.CustomDomain{}
|
||||
|
||||
err := s.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return customDomain.LoadByOrganizationID(ctx, conn, coredata.NewNoScope(), s.encryptionKey, organizationID)
|
||||
},
|
||||
)
|
||||
|
||||
return customDomain, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user