Replug SAML configurations
Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
graphql,
|
||||
usePreloadedQuery,
|
||||
useQueryLoader,
|
||||
type PreloadedQuery,
|
||||
} from "react-relay";
|
||||
import type { SAMLSettingsPageQuery } from "./__generated__/SAMLSettingsPageQuery.graphql";
|
||||
import { Suspense, use, useState } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
import { Breadcrumb, Button, Dialog, useDialogRef } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { SAMLConfigurationList } from "./_components/SAMLConfigurationList";
|
||||
import { NewSAMLConfigurationForm } from "./_components/NewSAMLConfigurationForm";
|
||||
import {
|
||||
EditSAMLConfigurationForm,
|
||||
samlConfigurationFormQuery,
|
||||
} from "./_components/EditSAMLConfigurationForm";
|
||||
import type { EditSAMLConfigurationFormQuery } from "./_components/__generated__/EditSAMLConfigurationFormQuery.graphql";
|
||||
import { SAMLDomainVerifyDialog } from "./_components/SAMLDomainVerifyDialog";
|
||||
|
||||
export const samlSettingsPageQuery = graphql`
|
||||
query SAMLSettingsPageQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) @required(action: THROW) {
|
||||
...SAMLConfigurationListFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function SAMLSettingsPage(props: {
|
||||
queryRef: PreloadedQuery<SAMLSettingsPageQuery>;
|
||||
}) {
|
||||
const { queryRef } = props;
|
||||
|
||||
const formDialogRef = useDialogRef();
|
||||
const domainDialogRef = useDialogRef();
|
||||
const [isEditing, setIsEditing] = useState<boolean>();
|
||||
const [domainVerificationToken, setDomainVerificationToken] =
|
||||
useState<string>();
|
||||
|
||||
const { __ } = useTranslate();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
const { organization } = usePreloadedQuery(samlSettingsPageQuery, queryRef);
|
||||
const [formQueryRef, loadFormQuery] =
|
||||
useQueryLoader<EditSAMLConfigurationFormQuery>(samlConfigurationFormQuery);
|
||||
|
||||
const handleOpenFormDialog = (samlConfigurationId?: string) => {
|
||||
setIsEditing(!!samlConfigurationId);
|
||||
if (samlConfigurationId) {
|
||||
loadFormQuery({ samlConfigurationId }, { fetchPolicy: "network-only" });
|
||||
}
|
||||
formDialogRef.current?.open();
|
||||
};
|
||||
const handleCloseFormDialog = () => {
|
||||
setIsEditing(false);
|
||||
formDialogRef.current?.close();
|
||||
};
|
||||
|
||||
const handleOpenVerifyDomainDialog = (domainVerificationToken: string) => {
|
||||
setDomainVerificationToken(domainVerificationToken);
|
||||
domainDialogRef.current?.open();
|
||||
};
|
||||
const handleCloseVerifyDomainDialog = () => {
|
||||
setDomainVerificationToken("");
|
||||
formDialogRef.current?.close();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-base font-medium">{__("SAML Single Sign-On")}</h2>
|
||||
{isAuthorized("Organization", "createSAMLConfiguration") && (
|
||||
<Button onClick={() => handleOpenFormDialog()}>
|
||||
{__("Add Configuration")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<SAMLConfigurationList
|
||||
fKey={organization}
|
||||
onNew={handleOpenFormDialog}
|
||||
onEdit={(id: string) => handleOpenFormDialog(id)}
|
||||
onVerifyDomain={handleOpenVerifyDomainDialog}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
ref={formDialogRef}
|
||||
onClose={handleCloseFormDialog}
|
||||
title={<Breadcrumb items={[__("SAML Settings"), __("Configure")]} />}
|
||||
>
|
||||
{isEditing ? (
|
||||
<Suspense>
|
||||
{formQueryRef && (
|
||||
<EditSAMLConfigurationForm
|
||||
queryRef={formQueryRef}
|
||||
onUpdate={handleCloseFormDialog}
|
||||
/>
|
||||
)}
|
||||
</Suspense>
|
||||
) : (
|
||||
<NewSAMLConfigurationForm onCreate={handleCloseFormDialog} />
|
||||
)}
|
||||
</Dialog>
|
||||
|
||||
<Dialog
|
||||
ref={domainDialogRef}
|
||||
onClose={handleCloseVerifyDomainDialog}
|
||||
title={
|
||||
<Breadcrumb items={[__("SAML Settings"), __("Verify Domain")]} />
|
||||
}
|
||||
>
|
||||
{domainVerificationToken && (
|
||||
<SAMLDomainVerifyDialog
|
||||
key={domainVerificationToken}
|
||||
domainVerificationToken={domainVerificationToken}
|
||||
/>
|
||||
)}
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useQueryLoader } from "react-relay";
|
||||
import { SAMLSettingsPage, samlSettingsPageQuery } from "./SAMLSettingsPage";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { IAMRelayProvider } from "/providers/IAMRelayProvider";
|
||||
import { useEffect } from "react";
|
||||
import type { SAMLSettingsPageQuery } from "./__generated__/SAMLSettingsPageQuery.graphql";
|
||||
|
||||
function SAMLSettingsPageLoader() {
|
||||
const organizationId = useOrganizationId();
|
||||
const [queryRef, loadQuery] = useQueryLoader<SAMLSettingsPageQuery>(
|
||||
samlSettingsPageQuery,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadQuery({
|
||||
organizationId,
|
||||
});
|
||||
}, [loadQuery, organizationId]);
|
||||
|
||||
if (!queryRef) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <SAMLSettingsPage queryRef={queryRef} />;
|
||||
}
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<IAMRelayProvider>
|
||||
<SAMLSettingsPageLoader />
|
||||
</IAMRelayProvider>
|
||||
);
|
||||
}
|
||||
245
apps/console/src/pages/iam/organizations/settings/__generated__/SAMLSettingsPageQuery.graphql.ts
generated
Normal file
245
apps/console/src/pages/iam/organizations/settings/__generated__/SAMLSettingsPageQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* @generated SignedSource<<57eb2489b4c62cb4b1df1a61bc135313>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type SAMLSettingsPageQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type SAMLSettingsPageQuery$data = {
|
||||
readonly organization: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"SAMLConfigurationListFragment">;
|
||||
};
|
||||
};
|
||||
export type SAMLSettingsPageQuery = {
|
||||
response: SAMLSettingsPageQuery$data;
|
||||
variables: SAMLSettingsPageQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "organizationId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "organizationId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 1000
|
||||
}
|
||||
],
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "SAMLSettingsPageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"kind": "RequiredField",
|
||||
"field": {
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "SAMLConfigurationListFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
"action": "THROW"
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "SAMLSettingsPageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v3/*: any*/),
|
||||
"concreteType": "SAMLConfigurationConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "samlConfigurations",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfigurationEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfiguration",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "emailDomain",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "enforcementPolicy",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerificationToken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerifiedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "testLoginUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "samlConfigurations(first:1000)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v3/*: any*/),
|
||||
"filters": null,
|
||||
"handle": "connection",
|
||||
"key": "SAMLConfigurationListFragment_samlConfigurations",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "samlConfigurations"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
},
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "45740d8e7e6fba1ec2576fd62487e41e",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "SAMLSettingsPageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query SAMLSettingsPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ...SAMLConfigurationListFragment\n id\n }\n}\n\nfragment SAMLConfigurationListFragment on Organization {\n samlConfigurations(first: 1000) {\n edges {\n node {\n id\n emailDomain\n enforcementPolicy\n domainVerificationToken\n domainVerifiedAt\n testLoginUrl\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d2d6ef2a90f81a75df7ab0dd83ef52cf";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,111 @@
|
||||
import { usePreloadedQuery, type PreloadedQuery } from "react-relay";
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { EditSAMLConfigurationForm_updateMutation } from "./__generated__/EditSAMLConfigurationForm_updateMutation.graphql";
|
||||
import { useCallback } from "react";
|
||||
import type { EditSAMLConfigurationFormQuery } from "./__generated__/EditSAMLConfigurationFormQuery.graphql";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import {
|
||||
SAMLConfigurationForm,
|
||||
type SAMLConfigurationFormData,
|
||||
} from "./SAMLConfigurationForm";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
|
||||
export const samlConfigurationFormQuery = graphql`
|
||||
query EditSAMLConfigurationFormQuery($samlConfigurationId: ID!) {
|
||||
samlConfiguration: node(id: $samlConfigurationId) @required(action: THROW) {
|
||||
__typename
|
||||
... on SAMLConfiguration {
|
||||
id
|
||||
emailDomain
|
||||
enforcementPolicy
|
||||
domainVerificationToken
|
||||
domainVerifiedAt
|
||||
testLoginUrl
|
||||
idpEntityId
|
||||
idpSsoUrl
|
||||
idpCertificate
|
||||
attributeMappings {
|
||||
email
|
||||
firstName
|
||||
lastName
|
||||
role
|
||||
}
|
||||
autoSignupEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const updateSAMLConfigurationMutation = graphql`
|
||||
mutation EditSAMLConfigurationForm_updateMutation(
|
||||
$input: UpdateSAMLConfigurationInput!
|
||||
) {
|
||||
updateSAMLConfiguration(input: $input) {
|
||||
samlConfiguration {
|
||||
id
|
||||
emailDomain
|
||||
enforcementPolicy
|
||||
domainVerificationToken
|
||||
domainVerifiedAt
|
||||
testLoginUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function EditSAMLConfigurationForm(props: {
|
||||
onUpdate: () => void;
|
||||
queryRef: PreloadedQuery<EditSAMLConfigurationFormQuery>;
|
||||
}) {
|
||||
const { onUpdate, queryRef } = props;
|
||||
|
||||
const organizationId = useOrganizationId();
|
||||
|
||||
const { samlConfiguration } =
|
||||
usePreloadedQuery<EditSAMLConfigurationFormQuery>(
|
||||
samlConfigurationFormQuery,
|
||||
queryRef,
|
||||
);
|
||||
if (samlConfiguration.__typename !== "SAMLConfiguration") {
|
||||
throw new Error("node is not a SAML configuration");
|
||||
}
|
||||
|
||||
const [update, isUpdating] =
|
||||
useMutationWithToasts<EditSAMLConfigurationForm_updateMutation>(
|
||||
updateSAMLConfigurationMutation,
|
||||
{
|
||||
successMessage: "SAML configuration updated successfully.",
|
||||
errorMessage: "Failed to update SAML configuration. Please try again.",
|
||||
},
|
||||
);
|
||||
|
||||
const handleUpdate = useCallback(
|
||||
(data: SAMLConfigurationFormData) => {
|
||||
update({
|
||||
variables: {
|
||||
input: {
|
||||
samlConfigurationId: samlConfiguration.id,
|
||||
organizationId,
|
||||
idpEntityId: data.idpEntityId,
|
||||
idpSsoUrl: data.idpSsoUrl,
|
||||
idpCertificate: data.idpCertificate,
|
||||
autoSignupEnabled: data.autoSignupEnabled,
|
||||
enforcementPolicy: data.enforcementPolicy,
|
||||
attributeMappings: data.attributeMappings,
|
||||
},
|
||||
},
|
||||
onCompleted: onUpdate,
|
||||
});
|
||||
},
|
||||
[onUpdate, organizationId, samlConfiguration.id, update],
|
||||
);
|
||||
|
||||
return (
|
||||
<SAMLConfigurationForm
|
||||
disabled={isUpdating}
|
||||
initialValues={samlConfiguration}
|
||||
isEditing
|
||||
onSubmit={handleUpdate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { ConnectionHandler, graphql } from "react-relay";
|
||||
import type { NewSAMLConfigurationForm_createMutation } from "./__generated__/NewSAMLConfigurationForm_createMutation.graphql";
|
||||
import {
|
||||
SAMLConfigurationForm,
|
||||
type SAMLConfigurationFormData,
|
||||
} from "./SAMLConfigurationForm";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { useCallback } from "react";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
|
||||
const createSAMLConfigurationMutation = graphql`
|
||||
mutation NewSAMLConfigurationForm_createMutation(
|
||||
$input: CreateSAMLConfigurationInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createSAMLConfiguration(input: $input) {
|
||||
samlConfigurationEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
id
|
||||
emailDomain
|
||||
enforcementPolicy
|
||||
domainVerificationToken
|
||||
domainVerifiedAt
|
||||
testLoginUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function NewSAMLConfigurationForm(props: { onCreate: () => void }) {
|
||||
const { onCreate } = props;
|
||||
const organizationId = useOrganizationId();
|
||||
|
||||
const [create, isCreating] =
|
||||
useMutationWithToasts<NewSAMLConfigurationForm_createMutation>(
|
||||
createSAMLConfigurationMutation,
|
||||
{
|
||||
successMessage: "SAML configuration created successfully.",
|
||||
errorMessage: "Failed to create SAML configuration",
|
||||
},
|
||||
);
|
||||
|
||||
const handleCreate = useCallback(
|
||||
(data: SAMLConfigurationFormData) => {
|
||||
const connectionID = ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
"SAMLConfigurationListFragment_samlConfigurations",
|
||||
);
|
||||
|
||||
create({
|
||||
variables: {
|
||||
input: {
|
||||
organizationId,
|
||||
emailDomain: data.emailDomain,
|
||||
idpEntityId: data.idpEntityId,
|
||||
idpSsoUrl: data.idpSsoUrl,
|
||||
idpCertificate: data.idpCertificate,
|
||||
autoSignupEnabled: data.autoSignupEnabled,
|
||||
attributeMappings: data.attributeMappings,
|
||||
},
|
||||
connections: [connectionID],
|
||||
},
|
||||
onCompleted: onCreate,
|
||||
});
|
||||
},
|
||||
[organizationId, create, onCreate],
|
||||
);
|
||||
|
||||
return (
|
||||
<SAMLConfigurationForm onSubmit={handleCreate} disabled={isCreating} />
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Field,
|
||||
Label,
|
||||
Option,
|
||||
Select,
|
||||
Textarea,
|
||||
} from "@probo/ui";
|
||||
import { Controller } from "react-hook-form";
|
||||
import z from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
|
||||
const defaultValues: SAMLConfigurationFormData = {
|
||||
emailDomain: "",
|
||||
enforcementPolicy: "OPTIONAL" as const,
|
||||
idpEntityId: "",
|
||||
idpSsoUrl: "",
|
||||
idpCertificate: "",
|
||||
attributeMappings: {
|
||||
email: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
|
||||
firstName:
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
|
||||
lastName: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
|
||||
role: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role",
|
||||
},
|
||||
autoSignupEnabled: false,
|
||||
};
|
||||
|
||||
const getEnforcementPolicyLabel = (
|
||||
policy: string,
|
||||
__: (key: string) => string,
|
||||
) => {
|
||||
switch (policy) {
|
||||
case "OFF":
|
||||
return __(
|
||||
"Your team members can't use single sign-on and must use their password",
|
||||
);
|
||||
case "REQUIRED":
|
||||
return __("Your team members must use single sign-on to log in");
|
||||
case "OPTIONAL":
|
||||
default:
|
||||
return __(
|
||||
"Your team members may use either single sign-on or their password to log in",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const samlConfigSchema = z.object({
|
||||
emailDomain: z
|
||||
.string()
|
||||
.min(1, "Email domain is required")
|
||||
.regex(
|
||||
/^[a-z0-9.-]+\.[a-z]{2,}$/i,
|
||||
"Must be a valid domain (e.g., example.com)",
|
||||
),
|
||||
enforcementPolicy: z.enum(["OFF", "OPTIONAL", "REQUIRED"]),
|
||||
spCertificate: z.string().optional(),
|
||||
spPrivateKey: z.string().optional(),
|
||||
idpEntityId: z.string().min(1, "IdP Entity ID is required"),
|
||||
idpSsoUrl: z.string().url("IdP SSO URL must be a valid URL"),
|
||||
idpCertificate: z.string().min(1, "IdP Certificate is required"),
|
||||
attributeMappings: z.object({
|
||||
email: z.string().optional(),
|
||||
firstName: z.string().optional(),
|
||||
lastName: z.string().optional(),
|
||||
role: z.string().optional(),
|
||||
}),
|
||||
autoSignupEnabled: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export type SAMLConfigurationFormData = z.infer<typeof samlConfigSchema>;
|
||||
|
||||
export function SAMLConfigurationForm(props: {
|
||||
isEditing?: boolean;
|
||||
disabled: boolean;
|
||||
initialValues?: SAMLConfigurationFormData;
|
||||
onSubmit: (data: SAMLConfigurationFormData) => void;
|
||||
}) {
|
||||
const {
|
||||
disabled,
|
||||
initialValues = defaultValues,
|
||||
isEditing,
|
||||
onSubmit,
|
||||
} = props;
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const form = useFormWithSchema(samlConfigSchema, {
|
||||
defaultValues: initialValues,
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
await form.handleSubmit(onSubmit)();
|
||||
form.reset(form.getValues());
|
||||
}}
|
||||
>
|
||||
<DialogContent padded className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-base font-medium mb-4">
|
||||
{__("Basic Configuration")}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Field
|
||||
{...form.register("emailDomain")}
|
||||
label={__("Email Domain") + " *"}
|
||||
placeholder="example.com"
|
||||
disabled={isEditing}
|
||||
error={form.formState.errors.emailDomain?.message}
|
||||
/>
|
||||
<p className="text-xs text-gray-600 mt-1">
|
||||
{isEditing
|
||||
? __("Email domain cannot be changed after creation")
|
||||
: __(
|
||||
"The email domain this SAML configuration applies to (e.g., example.com)",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
{isEditing && (
|
||||
<div>
|
||||
<Label htmlFor="enforcementPolicy">
|
||||
{__("Enforcement Policy") + " *"}
|
||||
</Label>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="enforcementPolicy"
|
||||
render={({ field }) => (
|
||||
<div className="mt-2">
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<Option value="OPTIONAL">{__("Optional")}</Option>
|
||||
<Option value="REQUIRED">{__("Required")}</Option>
|
||||
<Option value="OFF">{__("Off")}</Option>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
{form.watch("enforcementPolicy") && (
|
||||
<p className="text-xs text-gray-600 mt-2">
|
||||
{getEnforcementPolicyLabel(
|
||||
form.watch("enforcementPolicy"),
|
||||
__,
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-base font-medium mb-4">
|
||||
{__("Identity Provider Configuration")}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<Field
|
||||
{...form.register("idpEntityId")}
|
||||
label={__("IdP Entity ID") + " *"}
|
||||
placeholder="https://idp.example.com/metadata"
|
||||
error={form.formState.errors.idpEntityId?.message}
|
||||
/>
|
||||
<Field
|
||||
{...form.register("idpSsoUrl")}
|
||||
label={__("IdP SSO URL") + " *"}
|
||||
placeholder="https://idp.example.com/sso"
|
||||
error={form.formState.errors.idpSsoUrl?.message}
|
||||
/>
|
||||
<div>
|
||||
<Label htmlFor="idpCertificate">
|
||||
{__("IdP X.509 Certificate") + " *"}
|
||||
</Label>
|
||||
<Textarea
|
||||
{...form.register("idpCertificate")}
|
||||
id="idpCertificate"
|
||||
rows={6}
|
||||
placeholder="-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----"
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
{form.formState.errors.idpCertificate && (
|
||||
<p className="text-sm text-red-600 mt-1">
|
||||
{form.formState.errors.idpCertificate.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-base font-medium mb-4">
|
||||
{__("Attribute Mapping")}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<Field
|
||||
{...form.register("attributeMappings.email")}
|
||||
label={__("Email Attribute")}
|
||||
placeholder={defaultValues.attributeMappings.email}
|
||||
error={form.formState.errors.attributeMappings?.email?.message}
|
||||
/>
|
||||
<Field
|
||||
{...form.register("attributeMappings.firstName")}
|
||||
label={__("First Name Attribute")}
|
||||
placeholder={defaultValues.attributeMappings.firstName}
|
||||
error={
|
||||
form.formState.errors.attributeMappings?.firstName?.message
|
||||
}
|
||||
/>
|
||||
<Field
|
||||
{...form.register("attributeMappings.lastName")}
|
||||
label={__("Last Name Attribute")}
|
||||
placeholder={defaultValues.attributeMappings.lastName}
|
||||
error={form.formState.errors.attributeMappings?.lastName?.message}
|
||||
/>
|
||||
<Field
|
||||
{...form.register("attributeMappings.role")}
|
||||
label={__("Role Attribute")}
|
||||
placeholder={defaultValues.attributeMappings.role}
|
||||
error={form.formState.errors.attributeMappings?.role?.message}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="autoSignupEnabled"
|
||||
render={({ field }) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={field.value ?? false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<Label htmlFor="autoSignupEnabled" className="cursor-pointer">
|
||||
{__("Enable automatic user signup via SAML")}
|
||||
</Label>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button type="submit" disabled={disabled}>
|
||||
{isEditing ? __("Update Configuration") : __("Create Configuration")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { use } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Table,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useFragment } from "react-relay";
|
||||
import type {
|
||||
SAMLConfigurationListFragment$data,
|
||||
SAMLConfigurationListFragment$key,
|
||||
} from "./__generated__/SAMLConfigurationListFragment.graphql";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import type { SAMLConfigurationList_deleteMutation } from "./__generated__/SAMLConfigurationList_deleteMutation.graphql";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import type { NodeOf } from "/types";
|
||||
import { useCopy } from "@probo/hooks";
|
||||
|
||||
const fragment = graphql`
|
||||
fragment SAMLConfigurationListFragment on Organization {
|
||||
samlConfigurations(first: 1000)
|
||||
@required(action: THROW)
|
||||
@connection(key: "SAMLConfigurationListFragment_samlConfigurations") {
|
||||
edges @required(action: THROW) {
|
||||
node {
|
||||
id
|
||||
emailDomain
|
||||
enforcementPolicy
|
||||
domainVerificationToken
|
||||
domainVerifiedAt
|
||||
testLoginUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const deleteMutation = graphql`
|
||||
mutation SAMLConfigurationList_deleteMutation(
|
||||
$input: DeleteSAMLConfigurationInput!
|
||||
) {
|
||||
deleteSAMLConfiguration(input: $input) {
|
||||
deletedSamlConfigurationId
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function SAMLConfigurationList(props: {
|
||||
fKey: SAMLConfigurationListFragment$key;
|
||||
onNew: () => void;
|
||||
onEdit: (id: string) => void;
|
||||
onVerifyDomain: (dnsVerificationToken: string) => void;
|
||||
}) {
|
||||
const { fKey, onNew, onEdit, onVerifyDomain } = props;
|
||||
|
||||
const organizationId = useOrganizationId();
|
||||
const { __ } = useTranslate();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
const confirm = useConfirm();
|
||||
const [isCopied, copy] = useCopy();
|
||||
|
||||
const {
|
||||
samlConfigurations: { edges: samlConfigurations },
|
||||
} = useFragment<SAMLConfigurationListFragment$key>(fragment, fKey);
|
||||
|
||||
const [deleteSAMLConfiguration] =
|
||||
useMutationWithToasts<SAMLConfigurationList_deleteMutation>(
|
||||
deleteMutation,
|
||||
{
|
||||
successMessage: "SAML configuration deleted successfully.",
|
||||
errorMessage: "Failed to delete SAML configuration. Please try again.",
|
||||
},
|
||||
);
|
||||
|
||||
const handleDelete = (
|
||||
config: NodeOf<SAMLConfigurationListFragment$data["samlConfigurations"]>,
|
||||
) => {
|
||||
confirm(
|
||||
async () => {
|
||||
deleteSAMLConfiguration({
|
||||
variables: {
|
||||
input: {
|
||||
organizationId,
|
||||
samlConfigurationId: config.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
{
|
||||
title: __("Delete SAML Configuration"),
|
||||
message: __(
|
||||
"Are you sure you want to delete the SAML configuration for " +
|
||||
config.emailDomain +
|
||||
"? This action cannot be undone.",
|
||||
),
|
||||
label: __("Delete"),
|
||||
variant: "danger",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
if (samlConfigurations.length === 0) {
|
||||
return (
|
||||
<Card padded>
|
||||
<div className="text-center py-12">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
{__("No SAML Configurations")}
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-6">
|
||||
{__(
|
||||
"Set up SAML 2.0 single sign-on for your organization by adding a configuration for each email domain.",
|
||||
)}
|
||||
</p>
|
||||
{isAuthorized("Organization", "createSAMLConfiguration") && (
|
||||
<Button onClick={onNew}>
|
||||
{__("Add Your First Configuration")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Email Domain")}</Th>
|
||||
<Th>{__("Domain Status")}</Th>
|
||||
<Th>{__("SAML Status")}</Th>
|
||||
<Th>{__("Enforcement")}</Th>
|
||||
<Th>{__("SSO URL")}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{samlConfigurations.map(({ node: config }) => (
|
||||
<Tr key={config.id}>
|
||||
<Td>
|
||||
<button
|
||||
onClick={() => onEdit(config.id)}
|
||||
className="font-semibold text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
{config.emailDomain}
|
||||
</button>
|
||||
</Td>
|
||||
<Td>
|
||||
<span
|
||||
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
|
||||
config.domainVerifiedAt
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-yellow-100 text-yellow-800"
|
||||
}`}
|
||||
>
|
||||
{config.domainVerifiedAt
|
||||
? __("Verified")
|
||||
: __("Pending Verification")}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<span
|
||||
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
|
||||
config.enforcementPolicy !== "OFF"
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{config.enforcementPolicy !== "OFF"
|
||||
? __("Enabled")
|
||||
: __("Disabled")}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>{config.enforcementPolicy}</Td>
|
||||
<Td>
|
||||
{config.domainVerifiedAt && config.enforcementPolicy !== "OFF" ? (
|
||||
<button
|
||||
onClick={() => copy(config.testLoginUrl)}
|
||||
className="text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
{isCopied ? __("Copied!") : __("Copy URL")}
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-gray-400">—</span>
|
||||
)}
|
||||
</Td>
|
||||
<Td width={180} className="text-end">
|
||||
<div className="flex gap-2 justify-end">
|
||||
{config.domainVerifiedAt ? (
|
||||
<>
|
||||
{isAuthorized(
|
||||
"SAMLConfiguration",
|
||||
"updateSAMLConfiguration",
|
||||
) && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => onEdit(config.id)}
|
||||
>
|
||||
{__("Edit")}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{isAuthorized("Organization", "verifyDomain") &&
|
||||
!!config.domainVerificationToken && (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() =>
|
||||
onVerifyDomain(config.domainVerificationToken!)
|
||||
}
|
||||
>
|
||||
{__("Verify Domain")}
|
||||
</Button>
|
||||
)}
|
||||
{isAuthorized("Organization", "deleteOrganization") && (
|
||||
<Button
|
||||
variant="danger"
|
||||
onClick={() => handleDelete(config)}
|
||||
>
|
||||
{__("Delete")}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useCopy } from "@probo/hooks";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { DialogContent, Button } from "@probo/ui";
|
||||
|
||||
export function SAMLDomainVerifyDialog(props: {
|
||||
domainVerificationToken: string;
|
||||
}) {
|
||||
const { domainVerificationToken } = props;
|
||||
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const dnsRecord = `probo-verification=${domainVerificationToken}`;
|
||||
const [isCopied, copy] = useCopy();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogContent padded className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-base font-medium mb-4">
|
||||
{__("Verify Domain Ownership")}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
{__(
|
||||
"Add the following TXT record to your domain's DNS configuration to verify ownership:",
|
||||
)}
|
||||
</p>
|
||||
<div className="bg-gray-50 rounded-lg p-4 mb-4">
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<span className="font-semibold text-sm">
|
||||
{__("Host/Name:")}
|
||||
</span>
|
||||
<code className="ml-2 bg-white px-2 py-1 rounded text-sm">
|
||||
@
|
||||
</code>
|
||||
<span className="ml-2 text-xs text-gray-600">
|
||||
{__("or use your domain name")}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-semibold text-sm">{__("Type:")}</span>
|
||||
<code className="ml-2 bg-white px-2 py-1 rounded text-sm">
|
||||
TXT
|
||||
</code>
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-semibold text-sm">{__("Value:")}</span>
|
||||
<div className="mt-1 flex items-center gap-2">
|
||||
<code className="flex-1 bg-white px-2 py-1 rounded text-sm break-all font-mono">
|
||||
{dnsRecord}
|
||||
</code>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => copy(dnsRecord)}
|
||||
>
|
||||
{isCopied ? __("Copied!") : __("Copy")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||
<p className="text-sm text-blue-800">
|
||||
<strong>{__("Note:")}</strong>{" "}
|
||||
{__(
|
||||
"DNS changes may take up to 48 hours to propagate, but typically complete within a few minutes.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* @generated SignedSource<<3da2840acb86d86a80715539be18fba0>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type SAMLEnforcementPolicy = "OFF" | "OPTIONAL" | "REQUIRED";
|
||||
export type EditSAMLConfigurationFormQuery$variables = {
|
||||
samlConfigurationId: string;
|
||||
};
|
||||
export type EditSAMLConfigurationFormQuery$data = {
|
||||
readonly samlConfiguration: {
|
||||
readonly __typename: "SAMLConfiguration";
|
||||
readonly attributeMappings: {
|
||||
readonly email: string;
|
||||
readonly firstName: string;
|
||||
readonly lastName: string;
|
||||
readonly role: string;
|
||||
};
|
||||
readonly autoSignupEnabled: boolean;
|
||||
readonly domainVerificationToken: string | null | undefined;
|
||||
readonly domainVerifiedAt: any | null | undefined;
|
||||
readonly emailDomain: string;
|
||||
readonly enforcementPolicy: SAMLEnforcementPolicy;
|
||||
readonly id: string;
|
||||
readonly idpCertificate: string;
|
||||
readonly idpEntityId: string;
|
||||
readonly idpSsoUrl: string;
|
||||
readonly testLoginUrl: string;
|
||||
} | {
|
||||
// This will never be '%other', but we need some
|
||||
// value in case none of the concrete values match.
|
||||
readonly __typename: "%other";
|
||||
};
|
||||
};
|
||||
export type EditSAMLConfigurationFormQuery = {
|
||||
response: EditSAMLConfigurationFormQuery$data;
|
||||
variables: EditSAMLConfigurationFormQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "samlConfigurationId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "samlConfigurationId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "emailDomain",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "enforcementPolicy",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerificationToken",
|
||||
"storageKey": null
|
||||
},
|
||||
v7 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerifiedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
v8 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "testLoginUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
v9 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "idpEntityId",
|
||||
"storageKey": null
|
||||
},
|
||||
v10 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "idpSsoUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
v11 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "idpCertificate",
|
||||
"storageKey": null
|
||||
},
|
||||
v12 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLAttributeMappings",
|
||||
"kind": "LinkedField",
|
||||
"name": "attributeMappings",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "firstName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "lastName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "role",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
v13 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "autoSignupEnabled",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "EditSAMLConfigurationFormQuery",
|
||||
"selections": [
|
||||
{
|
||||
"kind": "RequiredField",
|
||||
"field": {
|
||||
"alias": "samlConfiguration",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
(v11/*: any*/),
|
||||
(v12/*: any*/),
|
||||
(v13/*: any*/)
|
||||
],
|
||||
"type": "SAMLConfiguration",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
"action": "THROW"
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "EditSAMLConfigurationFormQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "samlConfiguration",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
(v11/*: any*/),
|
||||
(v12/*: any*/),
|
||||
(v13/*: any*/)
|
||||
],
|
||||
"type": "SAMLConfiguration",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "47c1ce92e8ba0395fa803b343d093879",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "EditSAMLConfigurationFormQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query EditSAMLConfigurationFormQuery(\n $samlConfigurationId: ID!\n) {\n samlConfiguration: node(id: $samlConfigurationId) {\n __typename\n ... on SAMLConfiguration {\n id\n emailDomain\n enforcementPolicy\n domainVerificationToken\n domainVerifiedAt\n testLoginUrl\n idpEntityId\n idpSsoUrl\n idpCertificate\n attributeMappings {\n email\n firstName\n lastName\n role\n }\n autoSignupEnabled\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d31326e3e4423e62d8acde5735d9b578";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* @generated SignedSource<<33cdb4d30c06225b656ba2b3f27830eb>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type SAMLEnforcementPolicy = "OFF" | "OPTIONAL" | "REQUIRED";
|
||||
export type UpdateSAMLConfigurationInput = {
|
||||
attributeMappings?: SAMLAttributeMappingsInput | null | undefined;
|
||||
autoSignupEnabled?: boolean | null | undefined;
|
||||
enforcementPolicy: SAMLEnforcementPolicy;
|
||||
idpCertificate?: string | null | undefined;
|
||||
idpEntityId?: string | null | undefined;
|
||||
idpSsoUrl?: string | null | undefined;
|
||||
organizationId: string;
|
||||
samlConfigurationId: string;
|
||||
};
|
||||
export type SAMLAttributeMappingsInput = {
|
||||
email?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
role?: string | null | undefined;
|
||||
};
|
||||
export type EditSAMLConfigurationForm_updateMutation$variables = {
|
||||
input: UpdateSAMLConfigurationInput;
|
||||
};
|
||||
export type EditSAMLConfigurationForm_updateMutation$data = {
|
||||
readonly updateSAMLConfiguration: {
|
||||
readonly samlConfiguration: {
|
||||
readonly domainVerificationToken: string | null | undefined;
|
||||
readonly domainVerifiedAt: any | null | undefined;
|
||||
readonly emailDomain: string;
|
||||
readonly enforcementPolicy: SAMLEnforcementPolicy;
|
||||
readonly id: string;
|
||||
readonly testLoginUrl: string;
|
||||
} | null | undefined;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type EditSAMLConfigurationForm_updateMutation = {
|
||||
response: EditSAMLConfigurationForm_updateMutation$data;
|
||||
variables: EditSAMLConfigurationForm_updateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "UpdateSAMLConfigurationPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateSAMLConfiguration",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfiguration",
|
||||
"kind": "LinkedField",
|
||||
"name": "samlConfiguration",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "emailDomain",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "enforcementPolicy",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerificationToken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerifiedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "testLoginUrl",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "EditSAMLConfigurationForm_updateMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "EditSAMLConfigurationForm_updateMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "56d29623294c243a01bfbc2ab4310d28",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "EditSAMLConfigurationForm_updateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation EditSAMLConfigurationForm_updateMutation(\n $input: UpdateSAMLConfigurationInput!\n) {\n updateSAMLConfiguration(input: $input) {\n samlConfiguration {\n id\n emailDomain\n enforcementPolicy\n domainVerificationToken\n domainVerifiedAt\n testLoginUrl\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "b98e701514c179bba0f0ce63aae0c0b8";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* @generated SignedSource<<0d0fdbf079f1a72545fa363ce60cd2f7>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type SAMLEnforcementPolicy = "OFF" | "OPTIONAL" | "REQUIRED";
|
||||
export type CreateSAMLConfigurationInput = {
|
||||
attributeMappings?: SAMLAttributeMappingsInput | null | undefined;
|
||||
autoSignupEnabled: boolean;
|
||||
emailDomain: string;
|
||||
idpCertificate: string;
|
||||
idpEntityId: string;
|
||||
idpSsoUrl: string;
|
||||
organizationId: string;
|
||||
};
|
||||
export type SAMLAttributeMappingsInput = {
|
||||
email?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
role?: string | null | undefined;
|
||||
};
|
||||
export type NewSAMLConfigurationForm_createMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreateSAMLConfigurationInput;
|
||||
};
|
||||
export type NewSAMLConfigurationForm_createMutation$data = {
|
||||
readonly createSAMLConfiguration: {
|
||||
readonly samlConfigurationEdge: {
|
||||
readonly node: {
|
||||
readonly domainVerificationToken: string | null | undefined;
|
||||
readonly domainVerifiedAt: any | null | undefined;
|
||||
readonly emailDomain: string;
|
||||
readonly enforcementPolicy: SAMLEnforcementPolicy;
|
||||
readonly id: string;
|
||||
readonly testLoginUrl: string;
|
||||
};
|
||||
};
|
||||
} | null | undefined;
|
||||
};
|
||||
export type NewSAMLConfigurationForm_createMutation = {
|
||||
response: NewSAMLConfigurationForm_createMutation$data;
|
||||
variables: NewSAMLConfigurationForm_createMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfigurationEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "samlConfigurationEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfiguration",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "emailDomain",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "enforcementPolicy",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerificationToken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerifiedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "testLoginUrl",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "NewSAMLConfigurationForm_createMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateSAMLConfigurationPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createSAMLConfiguration",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "NewSAMLConfigurationForm_createMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateSAMLConfigurationPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createSAMLConfiguration",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "samlConfigurationEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "969d5fb34995f5cce359c792cbe1ff49",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "NewSAMLConfigurationForm_createMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation NewSAMLConfigurationForm_createMutation(\n $input: CreateSAMLConfigurationInput!\n) {\n createSAMLConfiguration(input: $input) {\n samlConfigurationEdge {\n node {\n id\n emailDomain\n enforcementPolicy\n domainVerificationToken\n domainVerifiedAt\n testLoginUrl\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "5a3b2e5d219b40ca096eece75d7b63b3";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* @generated SignedSource<<12fade1fd1f7f3ae53595970b6c2c47e>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type SAMLEnforcementPolicy = "OFF" | "OPTIONAL" | "REQUIRED";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type SAMLConfigurationListFragment$data = {
|
||||
readonly samlConfigurations: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly domainVerificationToken: string | null | undefined;
|
||||
readonly domainVerifiedAt: any | null | undefined;
|
||||
readonly emailDomain: string;
|
||||
readonly enforcementPolicy: SAMLEnforcementPolicy;
|
||||
readonly id: string;
|
||||
readonly testLoginUrl: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly " $fragmentType": "SAMLConfigurationListFragment";
|
||||
};
|
||||
export type SAMLConfigurationListFragment$key = {
|
||||
readonly " $data"?: SAMLConfigurationListFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"SAMLConfigurationListFragment">;
|
||||
};
|
||||
|
||||
const node: ReaderFragment = {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": {
|
||||
"connection": [
|
||||
{
|
||||
"count": null,
|
||||
"cursor": null,
|
||||
"direction": "forward",
|
||||
"path": [
|
||||
"samlConfigurations"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "SAMLConfigurationListFragment",
|
||||
"selections": [
|
||||
{
|
||||
"kind": "RequiredField",
|
||||
"field": {
|
||||
"alias": "samlConfigurations",
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfigurationConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__SAMLConfigurationListFragment_samlConfigurations_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "RequiredField",
|
||||
"field": {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfigurationEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SAMLConfiguration",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "emailDomain",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "enforcementPolicy",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerificationToken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "domainVerifiedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "testLoginUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
"action": "THROW"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
"action": "THROW"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
};
|
||||
|
||||
(node as any).hash = "79203ccae6f8e3a1967b463f0f3f3c76";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @generated SignedSource<<4ce15b49974b7e512f334f47765caaa4>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type DeleteSAMLConfigurationInput = {
|
||||
organizationId: string;
|
||||
samlConfigurationId: string;
|
||||
};
|
||||
export type SAMLConfigurationList_deleteMutation$variables = {
|
||||
input: DeleteSAMLConfigurationInput;
|
||||
};
|
||||
export type SAMLConfigurationList_deleteMutation$data = {
|
||||
readonly deleteSAMLConfiguration: {
|
||||
readonly deletedSamlConfigurationId: string;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type SAMLConfigurationList_deleteMutation = {
|
||||
response: SAMLConfigurationList_deleteMutation$data;
|
||||
variables: SAMLConfigurationList_deleteMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "DeleteSAMLConfigurationPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteSAMLConfiguration",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deletedSamlConfigurationId",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "SAMLConfigurationList_deleteMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "SAMLConfigurationList_deleteMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "cb46bb31e5d10f0ddad38795a72b1ab1",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "SAMLConfigurationList_deleteMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation SAMLConfigurationList_deleteMutation(\n $input: DeleteSAMLConfigurationInput!\n) {\n deleteSAMLConfiguration(input: $input) {\n deletedSamlConfigurationId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "2ebbe76aae843f00570f7ae1848ee995";
|
||||
|
||||
export default node;
|
||||
@@ -220,12 +220,13 @@ const routes = [
|
||||
() => import("./pages/organizations/settings/DomainSettingsTab"),
|
||||
),
|
||||
},
|
||||
// {
|
||||
// path: "saml-sso",
|
||||
// Component: lazy(
|
||||
// () => import("./pages/organizations/settings/SAMLSettingsTab")
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
path: "saml-sso",
|
||||
Component: lazy(
|
||||
() =>
|
||||
import("./pages/iam/organizations/settings/SAMLSettingsPageLoader"),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
...riskRoutes,
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
"value": "admin@example.com",
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
]
|
||||
},
|
||||
{
|
||||
"username": "viewer@example.com",
|
||||
|
||||
@@ -4,3 +4,4 @@ export { useRefSync } from "./useRefSync";
|
||||
export { useList } from "./useList";
|
||||
export { useStateWithRef } from "./useStateWithRef";
|
||||
export { useCleanup } from "./useCleanup";
|
||||
export { useCopy } from "./useCopy";
|
||||
|
||||
18
packages/hooks/src/useCopy.ts
Normal file
18
packages/hooks/src/useCopy.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
export function useCopy(): [boolean, (value: string) => void] {
|
||||
const [copiedValue, setCopiedValue] = useState<string>();
|
||||
const lastCopiedValueRef = useRef<string>("");
|
||||
|
||||
const handleCopy = (value: string) => {
|
||||
lastCopiedValueRef.current = value;
|
||||
navigator.clipboard.writeText(value);
|
||||
setCopiedValue(value);
|
||||
setTimeout(() => {
|
||||
setCopiedValue(undefined);
|
||||
lastCopiedValueRef.current = "";
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return [copiedValue === lastCopiedValueRef.current, handleCopy];
|
||||
}
|
||||
@@ -133,11 +133,8 @@ var IAMAdminPolicy = policy.NewPolicy(
|
||||
ActionIAMInvitationGet,
|
||||
ActionIAMInvitationDelete,
|
||||
).WithSID("invitation-admin-access"),
|
||||
// Can view SAML configurations
|
||||
policy.Allow(
|
||||
ActionIAMSAMLConfigurationGet,
|
||||
ActionIAMSAMLConfigurationList,
|
||||
).WithSID("saml-viewer-access"),
|
||||
// Can view and update SAML configurations
|
||||
policy.Allow(ActionIAMSAMLConfigurationGet).WithSID("saml-configuration-admin-access"),
|
||||
// Cannot delete organization
|
||||
policy.Deny(ActionIAMOrganizationDelete).WithSID("deny-org-delete"),
|
||||
// Cannot remove members (only owner can)
|
||||
|
||||
@@ -99,6 +99,14 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetEntityID() string {
|
||||
return fmt.Sprintf("%s/connect/saml/metadata", s.baseURL)
|
||||
}
|
||||
|
||||
func (s *Service) GetAcsURL() string {
|
||||
return fmt.Sprintf("%s/connect/saml/consume", s.baseURL)
|
||||
}
|
||||
|
||||
func (s *Service) GenerateSpMetadata() ([]byte, error) {
|
||||
sp := s.baseServiceProvider()
|
||||
return xml.MarshalIndent(sp.Metadata(), "", " ")
|
||||
|
||||
@@ -219,3 +219,31 @@ func (s *Service) GetSession(ctx context.Context, sessionID gid.GID) (*coredata.
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetSAMLconfiguration(ctx context.Context, samlConfigurationID gid.GID) (*coredata.SAMLConfiguration, error) {
|
||||
var (
|
||||
scope = coredata.NewScopeFromObjectID(samlConfigurationID)
|
||||
samlConfiguration = &coredata.SAMLConfiguration{}
|
||||
)
|
||||
|
||||
err := s.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := samlConfiguration.LoadByID(ctx, conn, scope, samlConfigurationID)
|
||||
if err != nil {
|
||||
if err == coredata.ErrResourceNotFound {
|
||||
return saml.NewSAMLConfigurationNotFoundError(samlConfigurationID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load SAML configuration: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return samlConfiguration, nil
|
||||
}
|
||||
|
||||
@@ -300,8 +300,7 @@ type SAMLConfiguration implements Node {
|
||||
autoSignupEnabled: Boolean!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
spMetadataUrl: String!
|
||||
testLoginUrl: String!
|
||||
testLoginUrl: String! @goField(forceResolver: true)
|
||||
attributeMappings: SAMLAttributeMappings!
|
||||
}
|
||||
|
||||
@@ -640,7 +639,7 @@ input UpdateSAMLConfigurationInput {
|
||||
idpSsoUrl: String
|
||||
idpCertificate: String
|
||||
autoSignupEnabled: Boolean
|
||||
enforcementPolicy: SAMLEnforcementPolicy
|
||||
enforcementPolicy: SAMLEnforcementPolicy!
|
||||
attributeMappings: SAMLAttributeMappingsInput
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ type ResolverRoot interface {
|
||||
Organization() OrganizationResolver
|
||||
PersonalAPIKeyConnection() PersonalAPIKeyConnectionResolver
|
||||
Query() QueryResolver
|
||||
SAMLConfiguration() SAMLConfigurationResolver
|
||||
SAMLConfigurationConnection() SAMLConfigurationConnectionResolver
|
||||
Session() SessionResolver
|
||||
SessionConnection() SessionConnectionResolver
|
||||
@@ -339,7 +340,6 @@ type ComplexityRoot struct {
|
||||
IdpCertificate func(childComplexity int) int
|
||||
IdpEntityID func(childComplexity int) int
|
||||
IdpSsoURL func(childComplexity int) int
|
||||
SpMetadataURL func(childComplexity int) int
|
||||
TestLoginURL func(childComplexity int) int
|
||||
UpdatedAt func(childComplexity int) int
|
||||
}
|
||||
@@ -485,6 +485,9 @@ type QueryResolver interface {
|
||||
Viewer(ctx context.Context) (*types.Identity, error)
|
||||
CheckSSOAvailability(ctx context.Context, email string) (*types.SSOAvailability, error)
|
||||
}
|
||||
type SAMLConfigurationResolver interface {
|
||||
TestLoginURL(ctx context.Context, obj *types.SAMLConfiguration) (string, error)
|
||||
}
|
||||
type SAMLConfigurationConnectionResolver interface {
|
||||
TotalCount(ctx context.Context, obj *types.SAMLConfigurationConnection) (*int, error)
|
||||
}
|
||||
@@ -1636,12 +1639,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
}
|
||||
|
||||
return e.complexity.SAMLConfiguration.IdpSsoURL(childComplexity), true
|
||||
case "SAMLConfiguration.spMetadataUrl":
|
||||
if e.complexity.SAMLConfiguration.SpMetadataURL == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.SAMLConfiguration.SpMetadataURL(childComplexity), true
|
||||
case "SAMLConfiguration.testLoginUrl":
|
||||
if e.complexity.SAMLConfiguration.TestLoginURL == nil {
|
||||
break
|
||||
@@ -2278,8 +2275,7 @@ type SAMLConfiguration implements Node {
|
||||
autoSignupEnabled: Boolean!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
spMetadataUrl: String!
|
||||
testLoginUrl: String!
|
||||
testLoginUrl: String! @goField(forceResolver: true)
|
||||
attributeMappings: SAMLAttributeMappings!
|
||||
}
|
||||
|
||||
@@ -2618,7 +2614,7 @@ input UpdateSAMLConfigurationInput {
|
||||
idpSsoUrl: String
|
||||
idpCertificate: String
|
||||
autoSignupEnabled: Boolean
|
||||
enforcementPolicy: SAMLEnforcementPolicy
|
||||
enforcementPolicy: SAMLEnforcementPolicy!
|
||||
attributeMappings: SAMLAttributeMappingsInput
|
||||
}
|
||||
|
||||
@@ -9532,35 +9528,6 @@ func (ec *executionContext) fieldContext_SAMLConfiguration_updatedAt(_ context.C
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _SAMLConfiguration_spMetadataUrl(ctx context.Context, field graphql.CollectedField, obj *types.SAMLConfiguration) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_SAMLConfiguration_spMetadataUrl,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.SpMetadataURL, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNString2string,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_SAMLConfiguration_spMetadataUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "SAMLConfiguration",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _SAMLConfiguration_testLoginUrl(ctx context.Context, field graphql.CollectedField, obj *types.SAMLConfiguration) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -9568,7 +9535,7 @@ func (ec *executionContext) _SAMLConfiguration_testLoginUrl(ctx context.Context,
|
||||
field,
|
||||
ec.fieldContext_SAMLConfiguration_testLoginUrl,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.TestLoginURL, nil
|
||||
return ec.resolvers.SAMLConfiguration().TestLoginURL(ctx, obj)
|
||||
},
|
||||
nil,
|
||||
ec.marshalNString2string,
|
||||
@@ -9581,8 +9548,8 @@ func (ec *executionContext) fieldContext_SAMLConfiguration_testLoginUrl(_ contex
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "SAMLConfiguration",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
@@ -9778,8 +9745,6 @@ func (ec *executionContext) fieldContext_SAMLConfigurationEdge_node(_ context.Co
|
||||
return ec.fieldContext_SAMLConfiguration_createdAt(ctx, field)
|
||||
case "updatedAt":
|
||||
return ec.fieldContext_SAMLConfiguration_updatedAt(ctx, field)
|
||||
case "spMetadataUrl":
|
||||
return ec.fieldContext_SAMLConfiguration_spMetadataUrl(ctx, field)
|
||||
case "testLoginUrl":
|
||||
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||
case "attributeMappings":
|
||||
@@ -10707,8 +10672,6 @@ func (ec *executionContext) fieldContext_UpdateSAMLConfigurationPayload_samlConf
|
||||
return ec.fieldContext_SAMLConfiguration_createdAt(ctx, field)
|
||||
case "updatedAt":
|
||||
return ec.fieldContext_SAMLConfiguration_updatedAt(ctx, field)
|
||||
case "spMetadataUrl":
|
||||
return ec.fieldContext_SAMLConfiguration_spMetadataUrl(ctx, field)
|
||||
case "testLoginUrl":
|
||||
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||
case "attributeMappings":
|
||||
@@ -13214,7 +13177,7 @@ func (ec *executionContext) unmarshalInputUpdateSAMLConfigurationInput(ctx conte
|
||||
it.AutoSignupEnabled = data
|
||||
case "enforcementPolicy":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("enforcementPolicy"))
|
||||
data, err := ec.unmarshalOSAMLEnforcementPolicy2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy(ctx, v)
|
||||
data, err := ec.unmarshalNSAMLEnforcementPolicy2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
@@ -16030,17 +15993,17 @@ func (ec *executionContext) _SAMLConfiguration(ctx context.Context, sel ast.Sele
|
||||
case "id":
|
||||
out.Values[i] = ec._SAMLConfiguration_id(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "emailDomain":
|
||||
out.Values[i] = ec._SAMLConfiguration_emailDomain(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "enforcementPolicy":
|
||||
out.Values[i] = ec._SAMLConfiguration_enforcementPolicy(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "domainVerifiedAt":
|
||||
out.Values[i] = ec._SAMLConfiguration_domainVerifiedAt(ctx, field, obj)
|
||||
@@ -16049,47 +16012,73 @@ func (ec *executionContext) _SAMLConfiguration(ctx context.Context, sel ast.Sele
|
||||
case "idpEntityId":
|
||||
out.Values[i] = ec._SAMLConfiguration_idpEntityId(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "idpSsoUrl":
|
||||
out.Values[i] = ec._SAMLConfiguration_idpSsoUrl(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "idpCertificate":
|
||||
out.Values[i] = ec._SAMLConfiguration_idpCertificate(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "autoSignupEnabled":
|
||||
out.Values[i] = ec._SAMLConfiguration_autoSignupEnabled(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "createdAt":
|
||||
out.Values[i] = ec._SAMLConfiguration_createdAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "updatedAt":
|
||||
out.Values[i] = ec._SAMLConfiguration_updatedAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "spMetadataUrl":
|
||||
out.Values[i] = ec._SAMLConfiguration_spMetadataUrl(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "testLoginUrl":
|
||||
out.Values[i] = ec._SAMLConfiguration_testLoginUrl(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._SAMLConfiguration_testLoginUrl(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&fs.Invalids, 1)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
if field.Deferrable != nil {
|
||||
dfs, ok := deferred[field.Deferrable.Label]
|
||||
di := 0
|
||||
if ok {
|
||||
dfs.AddField(field)
|
||||
di = len(dfs.Values) - 1
|
||||
} else {
|
||||
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
|
||||
deferred[field.Deferrable.Label] = dfs
|
||||
}
|
||||
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
|
||||
return innerFunc(ctx, dfs)
|
||||
})
|
||||
|
||||
// don't run the out.Concurrently() call below
|
||||
out.Values[i] = graphql.Null
|
||||
continue
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
case "attributeMappings":
|
||||
out.Values[i] = ec._SAMLConfiguration_attributeMappings(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
@@ -18881,38 +18870,6 @@ func (ec *executionContext) marshalOSAMLConfigurationConnection2ᚖgoᚗproboᚗ
|
||||
return ec._SAMLConfigurationConnection(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOSAMLEnforcementPolicy2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy(ctx context.Context, v any) (*coredata.SAMLEnforcementPolicy, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
tmp, err := graphql.UnmarshalString(v)
|
||||
res := unmarshalOSAMLEnforcementPolicy2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy[tmp]
|
||||
return &res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOSAMLEnforcementPolicy2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy(ctx context.Context, sel ast.SelectionSet, v *coredata.SAMLEnforcementPolicy) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
_ = sel
|
||||
_ = ctx
|
||||
res := graphql.MarshalString(marshalOSAMLEnforcementPolicy2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy[*v])
|
||||
return res
|
||||
}
|
||||
|
||||
var (
|
||||
unmarshalOSAMLEnforcementPolicy2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy = map[string]coredata.SAMLEnforcementPolicy{
|
||||
"OFF": coredata.SAMLEnforcementPolicyOff,
|
||||
"OPTIONAL": coredata.SAMLEnforcementPolicyOptional,
|
||||
"REQUIRED": coredata.SAMLEnforcementPolicyRequired,
|
||||
}
|
||||
marshalOSAMLEnforcementPolicy2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSAMLEnforcementPolicy = map[coredata.SAMLEnforcementPolicy]string{
|
||||
coredata.SAMLEnforcementPolicyOff: "OFF",
|
||||
coredata.SAMLEnforcementPolicyOptional: "OPTIONAL",
|
||||
coredata.SAMLEnforcementPolicyRequired: "REQUIRED",
|
||||
}
|
||||
)
|
||||
|
||||
func (ec *executionContext) marshalOSession2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSession(ctx context.Context, sel ast.SelectionSet, v *types.Session) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
|
||||
@@ -371,7 +371,6 @@ type SAMLConfiguration struct {
|
||||
AutoSignupEnabled bool `json:"autoSignupEnabled"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
SpMetadataURL string `json:"spMetadataUrl"`
|
||||
TestLoginURL string `json:"testLoginUrl"`
|
||||
AttributeMappings *SAMLAttributeMappings `json:"attributeMappings"`
|
||||
}
|
||||
@@ -472,14 +471,14 @@ type UpdatePersonalAPIKeyPayload struct {
|
||||
}
|
||||
|
||||
type UpdateSAMLConfigurationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
SamlConfigurationID gid.GID `json:"samlConfigurationId"`
|
||||
IdpEntityID *string `json:"idpEntityId,omitempty"`
|
||||
IdpSsoURL *string `json:"idpSsoUrl,omitempty"`
|
||||
IdpCertificate *string `json:"idpCertificate,omitempty"`
|
||||
AutoSignupEnabled *bool `json:"autoSignupEnabled,omitempty"`
|
||||
EnforcementPolicy *coredata.SAMLEnforcementPolicy `json:"enforcementPolicy,omitempty"`
|
||||
AttributeMappings *SAMLAttributeMappingsInput `json:"attributeMappings,omitempty"`
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
SamlConfigurationID gid.GID `json:"samlConfigurationId"`
|
||||
IdpEntityID *string `json:"idpEntityId,omitempty"`
|
||||
IdpSsoURL *string `json:"idpSsoUrl,omitempty"`
|
||||
IdpCertificate *string `json:"idpCertificate,omitempty"`
|
||||
AutoSignupEnabled *bool `json:"autoSignupEnabled,omitempty"`
|
||||
EnforcementPolicy coredata.SAMLEnforcementPolicy `json:"enforcementPolicy"`
|
||||
AttributeMappings *SAMLAttributeMappingsInput `json:"attributeMappings,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateSAMLConfigurationPayload struct {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
@@ -933,6 +934,7 @@ func (r *mutationResolver) UpdateSAMLConfiguration(ctx context.Context, input ty
|
||||
IdPSsoURL: input.IdpSsoURL,
|
||||
IdPCertificate: input.IdpCertificate,
|
||||
AutoSignupEnabled: input.AutoSignupEnabled,
|
||||
EnforcementPolicy: &input.EnforcementPolicy,
|
||||
}
|
||||
|
||||
if input.AttributeMappings != nil {
|
||||
@@ -1163,6 +1165,16 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
||||
|
||||
return types.NewInvitation(invitation), nil
|
||||
}
|
||||
case coredata.SAMLConfigurationEntityType:
|
||||
action = iam.ActionIAMSAMLConfigurationGet
|
||||
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
|
||||
samlConfiguration, err := r.iam.GetSAMLconfiguration(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return types.NewSAMLConfiguration(samlConfiguration), nil
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entity type: %d", id.EntityType())
|
||||
}
|
||||
@@ -1231,6 +1243,17 @@ func (r *queryResolver) CheckSSOAvailability(ctx context.Context, email string)
|
||||
panic(fmt.Errorf("not implemented: CheckSSOAvailability - checkSSOAvailability"))
|
||||
}
|
||||
|
||||
// TestLoginURL is the resolver for the testLoginUrl field.
|
||||
func (r *sAMLConfigurationResolver) TestLoginURL(ctx context.Context, obj *types.SAMLConfiguration) (string, error) {
|
||||
entityID := r.iam.SAMLService.GetEntityID()
|
||||
parts := strings.Split(entityID, "/connect/saml/metadata")
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("invalid entity ID format")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/connect/saml/login/%s", parts[0], obj.ID), nil
|
||||
}
|
||||
|
||||
// TotalCount is the resolver for the totalCount field.
|
||||
func (r *sAMLConfigurationConnectionResolver) TotalCount(ctx context.Context, obj *types.SAMLConfigurationConnection) (*int, error) {
|
||||
switch obj.Resolver.(type) {
|
||||
@@ -1314,6 +1337,11 @@ func (r *Resolver) PersonalAPIKeyConnection() schema.PersonalAPIKeyConnectionRes
|
||||
// Query returns schema.QueryResolver implementation.
|
||||
func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
// SAMLConfiguration returns schema.SAMLConfigurationResolver implementation.
|
||||
func (r *Resolver) SAMLConfiguration() schema.SAMLConfigurationResolver {
|
||||
return &sAMLConfigurationResolver{r}
|
||||
}
|
||||
|
||||
// SAMLConfigurationConnection returns schema.SAMLConfigurationConnectionResolver implementation.
|
||||
func (r *Resolver) SAMLConfigurationConnection() schema.SAMLConfigurationConnectionResolver {
|
||||
return &sAMLConfigurationConnectionResolver{r}
|
||||
@@ -1336,6 +1364,7 @@ type mutationResolver struct{ *Resolver }
|
||||
type organizationResolver struct{ *Resolver }
|
||||
type personalAPIKeyConnectionResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type sAMLConfigurationResolver struct{ *Resolver }
|
||||
type sAMLConfigurationConnectionResolver struct{ *Resolver }
|
||||
type sessionResolver struct{ *Resolver }
|
||||
type sessionConnectionResolver struct{ *Resolver }
|
||||
|
||||
Reference in New Issue
Block a user