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"),
|
() => import("./pages/organizations/settings/DomainSettingsTab"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// path: "saml-sso",
|
path: "saml-sso",
|
||||||
// Component: lazy(
|
Component: lazy(
|
||||||
// () => import("./pages/organizations/settings/SAMLSettingsTab")
|
() =>
|
||||||
// ),
|
import("./pages/iam/organizations/settings/SAMLSettingsPageLoader"),
|
||||||
// },
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
...riskRoutes,
|
...riskRoutes,
|
||||||
|
|||||||
@@ -155,7 +155,7 @@
|
|||||||
"value": "admin@example.com",
|
"value": "admin@example.com",
|
||||||
"temporary": false
|
"temporary": false
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"username": "viewer@example.com",
|
"username": "viewer@example.com",
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ export { useRefSync } from "./useRefSync";
|
|||||||
export { useList } from "./useList";
|
export { useList } from "./useList";
|
||||||
export { useStateWithRef } from "./useStateWithRef";
|
export { useStateWithRef } from "./useStateWithRef";
|
||||||
export { useCleanup } from "./useCleanup";
|
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,
|
ActionIAMInvitationGet,
|
||||||
ActionIAMInvitationDelete,
|
ActionIAMInvitationDelete,
|
||||||
).WithSID("invitation-admin-access"),
|
).WithSID("invitation-admin-access"),
|
||||||
// Can view SAML configurations
|
// Can view and update SAML configurations
|
||||||
policy.Allow(
|
policy.Allow(ActionIAMSAMLConfigurationGet).WithSID("saml-configuration-admin-access"),
|
||||||
ActionIAMSAMLConfigurationGet,
|
|
||||||
ActionIAMSAMLConfigurationList,
|
|
||||||
).WithSID("saml-viewer-access"),
|
|
||||||
// Cannot delete organization
|
// Cannot delete organization
|
||||||
policy.Deny(ActionIAMOrganizationDelete).WithSID("deny-org-delete"),
|
policy.Deny(ActionIAMOrganizationDelete).WithSID("deny-org-delete"),
|
||||||
// Cannot remove members (only owner can)
|
// 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) {
|
func (s *Service) GenerateSpMetadata() ([]byte, error) {
|
||||||
sp := s.baseServiceProvider()
|
sp := s.baseServiceProvider()
|
||||||
return xml.MarshalIndent(sp.Metadata(), "", " ")
|
return xml.MarshalIndent(sp.Metadata(), "", " ")
|
||||||
|
|||||||
@@ -219,3 +219,31 @@ func (s *Service) GetSession(ctx context.Context, sessionID gid.GID) (*coredata.
|
|||||||
|
|
||||||
return session, nil
|
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!
|
autoSignupEnabled: Boolean!
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
spMetadataUrl: String!
|
testLoginUrl: String! @goField(forceResolver: true)
|
||||||
testLoginUrl: String!
|
|
||||||
attributeMappings: SAMLAttributeMappings!
|
attributeMappings: SAMLAttributeMappings!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,7 +639,7 @@ input UpdateSAMLConfigurationInput {
|
|||||||
idpSsoUrl: String
|
idpSsoUrl: String
|
||||||
idpCertificate: String
|
idpCertificate: String
|
||||||
autoSignupEnabled: Boolean
|
autoSignupEnabled: Boolean
|
||||||
enforcementPolicy: SAMLEnforcementPolicy
|
enforcementPolicy: SAMLEnforcementPolicy!
|
||||||
attributeMappings: SAMLAttributeMappingsInput
|
attributeMappings: SAMLAttributeMappingsInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ type ResolverRoot interface {
|
|||||||
Organization() OrganizationResolver
|
Organization() OrganizationResolver
|
||||||
PersonalAPIKeyConnection() PersonalAPIKeyConnectionResolver
|
PersonalAPIKeyConnection() PersonalAPIKeyConnectionResolver
|
||||||
Query() QueryResolver
|
Query() QueryResolver
|
||||||
|
SAMLConfiguration() SAMLConfigurationResolver
|
||||||
SAMLConfigurationConnection() SAMLConfigurationConnectionResolver
|
SAMLConfigurationConnection() SAMLConfigurationConnectionResolver
|
||||||
Session() SessionResolver
|
Session() SessionResolver
|
||||||
SessionConnection() SessionConnectionResolver
|
SessionConnection() SessionConnectionResolver
|
||||||
@@ -339,7 +340,6 @@ type ComplexityRoot struct {
|
|||||||
IdpCertificate func(childComplexity int) int
|
IdpCertificate func(childComplexity int) int
|
||||||
IdpEntityID func(childComplexity int) int
|
IdpEntityID func(childComplexity int) int
|
||||||
IdpSsoURL func(childComplexity int) int
|
IdpSsoURL func(childComplexity int) int
|
||||||
SpMetadataURL func(childComplexity int) int
|
|
||||||
TestLoginURL func(childComplexity int) int
|
TestLoginURL func(childComplexity int) int
|
||||||
UpdatedAt func(childComplexity int) int
|
UpdatedAt func(childComplexity int) int
|
||||||
}
|
}
|
||||||
@@ -485,6 +485,9 @@ type QueryResolver interface {
|
|||||||
Viewer(ctx context.Context) (*types.Identity, error)
|
Viewer(ctx context.Context) (*types.Identity, error)
|
||||||
CheckSSOAvailability(ctx context.Context, email string) (*types.SSOAvailability, 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 {
|
type SAMLConfigurationConnectionResolver interface {
|
||||||
TotalCount(ctx context.Context, obj *types.SAMLConfigurationConnection) (*int, error)
|
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
|
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":
|
case "SAMLConfiguration.testLoginUrl":
|
||||||
if e.complexity.SAMLConfiguration.TestLoginURL == nil {
|
if e.complexity.SAMLConfiguration.TestLoginURL == nil {
|
||||||
break
|
break
|
||||||
@@ -2278,8 +2275,7 @@ type SAMLConfiguration implements Node {
|
|||||||
autoSignupEnabled: Boolean!
|
autoSignupEnabled: Boolean!
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
spMetadataUrl: String!
|
testLoginUrl: String! @goField(forceResolver: true)
|
||||||
testLoginUrl: String!
|
|
||||||
attributeMappings: SAMLAttributeMappings!
|
attributeMappings: SAMLAttributeMappings!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2618,7 +2614,7 @@ input UpdateSAMLConfigurationInput {
|
|||||||
idpSsoUrl: String
|
idpSsoUrl: String
|
||||||
idpCertificate: String
|
idpCertificate: String
|
||||||
autoSignupEnabled: Boolean
|
autoSignupEnabled: Boolean
|
||||||
enforcementPolicy: SAMLEnforcementPolicy
|
enforcementPolicy: SAMLEnforcementPolicy!
|
||||||
attributeMappings: SAMLAttributeMappingsInput
|
attributeMappings: SAMLAttributeMappingsInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9532,35 +9528,6 @@ func (ec *executionContext) fieldContext_SAMLConfiguration_updatedAt(_ context.C
|
|||||||
return fc, nil
|
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) {
|
func (ec *executionContext) _SAMLConfiguration_testLoginUrl(ctx context.Context, field graphql.CollectedField, obj *types.SAMLConfiguration) (ret graphql.Marshaler) {
|
||||||
return graphql.ResolveField(
|
return graphql.ResolveField(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -9568,7 +9535,7 @@ func (ec *executionContext) _SAMLConfiguration_testLoginUrl(ctx context.Context,
|
|||||||
field,
|
field,
|
||||||
ec.fieldContext_SAMLConfiguration_testLoginUrl,
|
ec.fieldContext_SAMLConfiguration_testLoginUrl,
|
||||||
func(ctx context.Context) (any, error) {
|
func(ctx context.Context) (any, error) {
|
||||||
return obj.TestLoginURL, nil
|
return ec.resolvers.SAMLConfiguration().TestLoginURL(ctx, obj)
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
ec.marshalNString2string,
|
ec.marshalNString2string,
|
||||||
@@ -9581,8 +9548,8 @@ func (ec *executionContext) fieldContext_SAMLConfiguration_testLoginUrl(_ contex
|
|||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "SAMLConfiguration",
|
Object: "SAMLConfiguration",
|
||||||
Field: field,
|
Field: field,
|
||||||
IsMethod: false,
|
IsMethod: true,
|
||||||
IsResolver: false,
|
IsResolver: true,
|
||||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
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 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)
|
return ec.fieldContext_SAMLConfiguration_createdAt(ctx, field)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
return ec.fieldContext_SAMLConfiguration_updatedAt(ctx, field)
|
return ec.fieldContext_SAMLConfiguration_updatedAt(ctx, field)
|
||||||
case "spMetadataUrl":
|
|
||||||
return ec.fieldContext_SAMLConfiguration_spMetadataUrl(ctx, field)
|
|
||||||
case "testLoginUrl":
|
case "testLoginUrl":
|
||||||
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||||
case "attributeMappings":
|
case "attributeMappings":
|
||||||
@@ -10707,8 +10672,6 @@ func (ec *executionContext) fieldContext_UpdateSAMLConfigurationPayload_samlConf
|
|||||||
return ec.fieldContext_SAMLConfiguration_createdAt(ctx, field)
|
return ec.fieldContext_SAMLConfiguration_createdAt(ctx, field)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
return ec.fieldContext_SAMLConfiguration_updatedAt(ctx, field)
|
return ec.fieldContext_SAMLConfiguration_updatedAt(ctx, field)
|
||||||
case "spMetadataUrl":
|
|
||||||
return ec.fieldContext_SAMLConfiguration_spMetadataUrl(ctx, field)
|
|
||||||
case "testLoginUrl":
|
case "testLoginUrl":
|
||||||
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||||
case "attributeMappings":
|
case "attributeMappings":
|
||||||
@@ -13214,7 +13177,7 @@ func (ec *executionContext) unmarshalInputUpdateSAMLConfigurationInput(ctx conte
|
|||||||
it.AutoSignupEnabled = data
|
it.AutoSignupEnabled = data
|
||||||
case "enforcementPolicy":
|
case "enforcementPolicy":
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("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 {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
@@ -16030,17 +15993,17 @@ func (ec *executionContext) _SAMLConfiguration(ctx context.Context, sel ast.Sele
|
|||||||
case "id":
|
case "id":
|
||||||
out.Values[i] = ec._SAMLConfiguration_id(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_id(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "emailDomain":
|
case "emailDomain":
|
||||||
out.Values[i] = ec._SAMLConfiguration_emailDomain(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_emailDomain(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "enforcementPolicy":
|
case "enforcementPolicy":
|
||||||
out.Values[i] = ec._SAMLConfiguration_enforcementPolicy(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_enforcementPolicy(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "domainVerifiedAt":
|
case "domainVerifiedAt":
|
||||||
out.Values[i] = ec._SAMLConfiguration_domainVerifiedAt(ctx, field, obj)
|
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":
|
case "idpEntityId":
|
||||||
out.Values[i] = ec._SAMLConfiguration_idpEntityId(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_idpEntityId(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "idpSsoUrl":
|
case "idpSsoUrl":
|
||||||
out.Values[i] = ec._SAMLConfiguration_idpSsoUrl(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_idpSsoUrl(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "idpCertificate":
|
case "idpCertificate":
|
||||||
out.Values[i] = ec._SAMLConfiguration_idpCertificate(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_idpCertificate(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "autoSignupEnabled":
|
case "autoSignupEnabled":
|
||||||
out.Values[i] = ec._SAMLConfiguration_autoSignupEnabled(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_autoSignupEnabled(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
out.Values[i] = ec._SAMLConfiguration_createdAt(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_createdAt(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
out.Values[i] = ec._SAMLConfiguration_updatedAt(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_updatedAt(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
|
||||||
case "spMetadataUrl":
|
|
||||||
out.Values[i] = ec._SAMLConfiguration_spMetadataUrl(ctx, field, obj)
|
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
out.Invalids++
|
|
||||||
}
|
}
|
||||||
case "testLoginUrl":
|
case "testLoginUrl":
|
||||||
out.Values[i] = ec._SAMLConfiguration_testLoginUrl(ctx, field, obj)
|
field := field
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
out.Invalids++
|
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":
|
case "attributeMappings":
|
||||||
out.Values[i] = ec._SAMLConfiguration_attributeMappings(ctx, field, obj)
|
out.Values[i] = ec._SAMLConfiguration_attributeMappings(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
@@ -18881,38 +18870,6 @@ func (ec *executionContext) marshalOSAMLConfigurationConnection2ᚖgoᚗproboᚗ
|
|||||||
return ec._SAMLConfigurationConnection(ctx, sel, v)
|
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 {
|
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 {
|
if v == nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
|||||||
@@ -371,7 +371,6 @@ type SAMLConfiguration struct {
|
|||||||
AutoSignupEnabled bool `json:"autoSignupEnabled"`
|
AutoSignupEnabled bool `json:"autoSignupEnabled"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
SpMetadataURL string `json:"spMetadataUrl"`
|
|
||||||
TestLoginURL string `json:"testLoginUrl"`
|
TestLoginURL string `json:"testLoginUrl"`
|
||||||
AttributeMappings *SAMLAttributeMappings `json:"attributeMappings"`
|
AttributeMappings *SAMLAttributeMappings `json:"attributeMappings"`
|
||||||
}
|
}
|
||||||
@@ -472,14 +471,14 @@ type UpdatePersonalAPIKeyPayload struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdateSAMLConfigurationInput struct {
|
type UpdateSAMLConfigurationInput struct {
|
||||||
OrganizationID gid.GID `json:"organizationId"`
|
OrganizationID gid.GID `json:"organizationId"`
|
||||||
SamlConfigurationID gid.GID `json:"samlConfigurationId"`
|
SamlConfigurationID gid.GID `json:"samlConfigurationId"`
|
||||||
IdpEntityID *string `json:"idpEntityId,omitempty"`
|
IdpEntityID *string `json:"idpEntityId,omitempty"`
|
||||||
IdpSsoURL *string `json:"idpSsoUrl,omitempty"`
|
IdpSsoURL *string `json:"idpSsoUrl,omitempty"`
|
||||||
IdpCertificate *string `json:"idpCertificate,omitempty"`
|
IdpCertificate *string `json:"idpCertificate,omitempty"`
|
||||||
AutoSignupEnabled *bool `json:"autoSignupEnabled,omitempty"`
|
AutoSignupEnabled *bool `json:"autoSignupEnabled,omitempty"`
|
||||||
EnforcementPolicy *coredata.SAMLEnforcementPolicy `json:"enforcementPolicy,omitempty"`
|
EnforcementPolicy coredata.SAMLEnforcementPolicy `json:"enforcementPolicy"`
|
||||||
AttributeMappings *SAMLAttributeMappingsInput `json:"attributeMappings,omitempty"`
|
AttributeMappings *SAMLAttributeMappingsInput `json:"attributeMappings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateSAMLConfigurationPayload struct {
|
type UpdateSAMLConfigurationPayload struct {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql"
|
"github.com/99designs/gqlgen/graphql"
|
||||||
@@ -933,6 +934,7 @@ func (r *mutationResolver) UpdateSAMLConfiguration(ctx context.Context, input ty
|
|||||||
IdPSsoURL: input.IdpSsoURL,
|
IdPSsoURL: input.IdpSsoURL,
|
||||||
IdPCertificate: input.IdpCertificate,
|
IdPCertificate: input.IdpCertificate,
|
||||||
AutoSignupEnabled: input.AutoSignupEnabled,
|
AutoSignupEnabled: input.AutoSignupEnabled,
|
||||||
|
EnforcementPolicy: &input.EnforcementPolicy,
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.AttributeMappings != nil {
|
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
|
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:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported entity type: %d", id.EntityType())
|
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"))
|
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.
|
// TotalCount is the resolver for the totalCount field.
|
||||||
func (r *sAMLConfigurationConnectionResolver) TotalCount(ctx context.Context, obj *types.SAMLConfigurationConnection) (*int, error) {
|
func (r *sAMLConfigurationConnectionResolver) TotalCount(ctx context.Context, obj *types.SAMLConfigurationConnection) (*int, error) {
|
||||||
switch obj.Resolver.(type) {
|
switch obj.Resolver.(type) {
|
||||||
@@ -1314,6 +1337,11 @@ func (r *Resolver) PersonalAPIKeyConnection() schema.PersonalAPIKeyConnectionRes
|
|||||||
// Query returns schema.QueryResolver implementation.
|
// Query returns schema.QueryResolver implementation.
|
||||||
func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
|
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.
|
// SAMLConfigurationConnection returns schema.SAMLConfigurationConnectionResolver implementation.
|
||||||
func (r *Resolver) SAMLConfigurationConnection() schema.SAMLConfigurationConnectionResolver {
|
func (r *Resolver) SAMLConfigurationConnection() schema.SAMLConfigurationConnectionResolver {
|
||||||
return &sAMLConfigurationConnectionResolver{r}
|
return &sAMLConfigurationConnectionResolver{r}
|
||||||
@@ -1336,6 +1364,7 @@ type mutationResolver struct{ *Resolver }
|
|||||||
type organizationResolver struct{ *Resolver }
|
type organizationResolver struct{ *Resolver }
|
||||||
type personalAPIKeyConnectionResolver struct{ *Resolver }
|
type personalAPIKeyConnectionResolver struct{ *Resolver }
|
||||||
type queryResolver struct{ *Resolver }
|
type queryResolver struct{ *Resolver }
|
||||||
|
type sAMLConfigurationResolver struct{ *Resolver }
|
||||||
type sAMLConfigurationConnectionResolver struct{ *Resolver }
|
type sAMLConfigurationConnectionResolver struct{ *Resolver }
|
||||||
type sessionResolver struct{ *Resolver }
|
type sessionResolver struct{ *Resolver }
|
||||||
type sessionConnectionResolver struct{ *Resolver }
|
type sessionConnectionResolver struct{ *Resolver }
|
||||||
|
|||||||
Reference in New Issue
Block a user