Exclude users from google workspace bridge
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<3c7c629a3509891b6edc98178a376c2d>>
|
||||
* @generated SignedSource<<b449429f81b74e6da6c7c6ed523ca0ab>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -16,6 +16,8 @@ export type GoogleWorkspaceConnectorFragment$data = {
|
||||
readonly createdAt: string;
|
||||
readonly id: string;
|
||||
} | null | undefined;
|
||||
readonly excludedUserNames: ReadonlyArray<string>;
|
||||
readonly id: string;
|
||||
} | null | undefined;
|
||||
readonly id: string;
|
||||
readonly " $fragmentType": "GoogleWorkspaceConnectorFragment";
|
||||
@@ -48,6 +50,14 @@ return {
|
||||
"name": "bridge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "excludedUserNames",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -76,6 +86,6 @@ return {
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "6ce8b7f55ff2a3d0fad04ad6b452a28a";
|
||||
(node as any).hash = "4f3898fca8d93369a46b50227f3df63d";
|
||||
|
||||
export default node;
|
||||
|
||||
115
apps/console/src/__generated__/iam/GoogleWorkspaceConnectorUpdateSCIMBridgeMutation.graphql.ts
generated
Normal file
115
apps/console/src/__generated__/iam/GoogleWorkspaceConnectorUpdateSCIMBridgeMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @generated SignedSource<<41b1b8127b7840bf52b967e212c286ec>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type UpdateSCIMBridgeInput = {
|
||||
excludedUserNames: ReadonlyArray<string>;
|
||||
organizationId: string;
|
||||
scimBridgeId: string;
|
||||
};
|
||||
export type GoogleWorkspaceConnectorUpdateSCIMBridgeMutation$variables = {
|
||||
input: UpdateSCIMBridgeInput;
|
||||
};
|
||||
export type GoogleWorkspaceConnectorUpdateSCIMBridgeMutation$data = {
|
||||
readonly updateSCIMBridge: {
|
||||
readonly scimBridge: {
|
||||
readonly excludedUserNames: ReadonlyArray<string>;
|
||||
readonly id: string;
|
||||
};
|
||||
} | null | undefined;
|
||||
};
|
||||
export type GoogleWorkspaceConnectorUpdateSCIMBridgeMutation = {
|
||||
response: GoogleWorkspaceConnectorUpdateSCIMBridgeMutation$data;
|
||||
variables: GoogleWorkspaceConnectorUpdateSCIMBridgeMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "UpdateSCIMBridgePayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateSCIMBridge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "SCIMBridge",
|
||||
"kind": "LinkedField",
|
||||
"name": "scimBridge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "excludedUserNames",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "GoogleWorkspaceConnectorUpdateSCIMBridgeMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "GoogleWorkspaceConnectorUpdateSCIMBridgeMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "5b2fdb1e51ff728727c10809a89ba0ea",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "GoogleWorkspaceConnectorUpdateSCIMBridgeMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation GoogleWorkspaceConnectorUpdateSCIMBridgeMutation(\n $input: UpdateSCIMBridgeInput!\n) {\n updateSCIMBridge(input: $input) {\n scimBridge {\n id\n excludedUserNames\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "5b4046c71e69bbcc16e3fc533a5467ee";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<68578d960865d35a2d46ee7e2c50a054>>
|
||||
* @generated SignedSource<<e59dc83c1f91e928764947766661f044>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -190,6 +190,13 @@ return {
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "excludedUserNames",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -409,12 +416,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "e57ee79b79046f9f07c159c7079f766a",
|
||||
"cacheID": "1074234a9a3734270b5da7420dea2ec6",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "SCIMSettingsPageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query SCIMSettingsPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n scimConfiguration {\n id\n bridge {\n id\n }\n ...SCIMEventListFragment\n }\n ...SCIMConfigurationFragment\n ...ConnectorListFragment\n }\n id\n }\n}\n\nfragment ConnectorListFragment on Organization {\n scimConfiguration {\n ...GoogleWorkspaceConnectorFragment\n id\n }\n}\n\nfragment GoogleWorkspaceConnectorFragment on SCIMConfiguration {\n id\n bridge {\n connector {\n id\n createdAt\n }\n id\n }\n}\n\nfragment SCIMConfigurationFragment on Organization {\n canCreateSCIMConfiguration: permission(action: \"iam:scim-configuration:create\")\n canDeleteSCIMConfiguration: permission(action: \"iam:scim-configuration:delete\")\n scimConfiguration {\n id\n endpointUrl\n bridge {\n id\n }\n }\n}\n\nfragment SCIMEventListFragment on SCIMConfiguration {\n events(first: 20) {\n edges {\n node {\n id\n ...SCIMEventListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment SCIMEventListItemFragment on SCIMEvent {\n id\n method\n path\n statusCode\n errorMessage\n ipAddress\n createdAt\n membership {\n id\n profile {\n fullName\n id\n }\n }\n}\n"
|
||||
"text": "query SCIMSettingsPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n scimConfiguration {\n id\n bridge {\n id\n }\n ...SCIMEventListFragment\n }\n ...SCIMConfigurationFragment\n ...ConnectorListFragment\n }\n id\n }\n}\n\nfragment ConnectorListFragment on Organization {\n scimConfiguration {\n ...GoogleWorkspaceConnectorFragment\n id\n }\n}\n\nfragment GoogleWorkspaceConnectorFragment on SCIMConfiguration {\n id\n bridge {\n id\n excludedUserNames\n connector {\n id\n createdAt\n }\n }\n}\n\nfragment SCIMConfigurationFragment on Organization {\n canCreateSCIMConfiguration: permission(action: \"iam:scim-configuration:create\")\n canDeleteSCIMConfiguration: permission(action: \"iam:scim-configuration:delete\")\n scimConfiguration {\n id\n endpointUrl\n bridge {\n id\n }\n }\n}\n\nfragment SCIMEventListFragment on SCIMConfiguration {\n events(first: 20) {\n edges {\n node {\n id\n ...SCIMEventListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment SCIMEventListItemFragment on SCIMEvent {\n id\n method\n path\n statusCode\n errorMessage\n ipAddress\n createdAt\n membership {\n id\n profile {\n fullName\n id\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -8,12 +8,16 @@ import {
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Google,
|
||||
IconSettingsGear2,
|
||||
Input,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { useState } from "react";
|
||||
import { graphql, useFragment } from "react-relay";
|
||||
|
||||
import type { GoogleWorkspaceConnectorDeleteMutation } from "#/__generated__/iam/GoogleWorkspaceConnectorDeleteMutation.graphql";
|
||||
import type { GoogleWorkspaceConnectorFragment$key } from "#/__generated__/iam/GoogleWorkspaceConnectorFragment.graphql";
|
||||
import type { GoogleWorkspaceConnectorUpdateSCIMBridgeMutation } from "#/__generated__/iam/GoogleWorkspaceConnectorUpdateSCIMBridgeMutation.graphql";
|
||||
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
|
||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||
|
||||
@@ -21,6 +25,8 @@ const googleWorkspaceConnectorFragment = graphql`
|
||||
fragment GoogleWorkspaceConnectorFragment on SCIMConfiguration {
|
||||
id
|
||||
bridge {
|
||||
id
|
||||
excludedUserNames
|
||||
connector {
|
||||
id
|
||||
createdAt
|
||||
@@ -39,17 +45,35 @@ const deleteSCIMConfigurationMutation = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const updateSCIMBridgeMutation = graphql`
|
||||
mutation GoogleWorkspaceConnectorUpdateSCIMBridgeMutation(
|
||||
$input: UpdateSCIMBridgeInput!
|
||||
) {
|
||||
updateSCIMBridge(input: $input) {
|
||||
scimBridge {
|
||||
id
|
||||
excludedUserNames
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function GoogleWorkspaceConnector(props: {
|
||||
fKey: GoogleWorkspaceConnectorFragment$key | null;
|
||||
}) {
|
||||
const { fKey } = props;
|
||||
const data = useFragment<GoogleWorkspaceConnectorFragment$key>(googleWorkspaceConnectorFragment, fKey);
|
||||
const connector = data?.bridge?.connector;
|
||||
const bridge = data?.bridge;
|
||||
const connector = bridge?.connector;
|
||||
const scimConfigurationId = data?.id;
|
||||
const bridgeId = bridge?.id;
|
||||
|
||||
const organizationId = useOrganizationId();
|
||||
const { __, dateTimeFormat } = useTranslate();
|
||||
const dialogRef = useDialogRef();
|
||||
const excludedUserNamesDialogRef = useDialogRef();
|
||||
|
||||
const [newUser, setNewUser] = useState("");
|
||||
|
||||
const [deleteSCIMConfiguration, isDeleting]
|
||||
= useMutationWithToasts<GoogleWorkspaceConnectorDeleteMutation>(
|
||||
@@ -60,6 +84,15 @@ export function GoogleWorkspaceConnector(props: {
|
||||
},
|
||||
);
|
||||
|
||||
const [updateSCIMBridge, isUpdating]
|
||||
= useMutationWithToasts<GoogleWorkspaceConnectorUpdateSCIMBridgeMutation>(
|
||||
updateSCIMBridgeMutation,
|
||||
{
|
||||
successMessage: __("Excluded user names updated successfully"),
|
||||
errorMessage: __("Failed to update excluded user names"),
|
||||
},
|
||||
);
|
||||
|
||||
const handleConnect = () => {
|
||||
const baseUrl = import.meta.env.VITE_API_URL || window.location.origin;
|
||||
const url = new URL("/api/console/v1/connectors/initiate", baseUrl);
|
||||
@@ -92,6 +125,34 @@ export function GoogleWorkspaceConnector(props: {
|
||||
});
|
||||
};
|
||||
|
||||
const currentExcludedUserNames = bridge?.excludedUserNames ? [...bridge.excludedUserNames] : [];
|
||||
|
||||
const saveExcludedUserNames = (newList: string[]) => {
|
||||
if (!bridgeId) return;
|
||||
|
||||
void updateSCIMBridge({
|
||||
variables: {
|
||||
input: {
|
||||
organizationId: organizationId,
|
||||
scimBridgeId: bridgeId,
|
||||
excludedUserNames: newList,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddUser = () => {
|
||||
const user = newUser.trim().toLowerCase();
|
||||
if (user && !currentExcludedUserNames.includes(user)) {
|
||||
saveExcludedUserNames([...currentExcludedUserNames, user]);
|
||||
setNewUser("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveUser = (user: string) => {
|
||||
saveExcludedUserNames(currentExcludedUserNames.filter(e => e !== user));
|
||||
};
|
||||
|
||||
// Not connected state
|
||||
if (!connector) {
|
||||
return (
|
||||
@@ -130,9 +191,75 @@ export function GoogleWorkspaceConnector(props: {
|
||||
{__("Connected")}
|
||||
</Badge>
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
ref={excludedUserNamesDialogRef}
|
||||
trigger={(
|
||||
<Button variant="secondary">
|
||||
<IconSettingsGear2 size={16} />
|
||||
{__("Settings")}
|
||||
</Button>
|
||||
)}
|
||||
title={__("Google Workspace Settings")}
|
||||
className="max-w-lg"
|
||||
>
|
||||
<DialogContent padded className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium">{__("Excluded user names")}</h4>
|
||||
<p className="text-sm text-txt-secondary mt-1">
|
||||
{__("Users with these user names will not be synced from Google Workspace.")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={newUser}
|
||||
onChange={e => setNewUser(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
handleAddUser();
|
||||
}
|
||||
}}
|
||||
placeholder="user@example.com"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button onClick={handleAddUser} variant="secondary" disabled={isUpdating}>
|
||||
{__("Add")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{currentExcludedUserNames.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
{currentExcludedUserNames.map((user: string) => (
|
||||
<div
|
||||
key={user}
|
||||
className="flex items-center justify-between p-2 bg-subtle rounded"
|
||||
>
|
||||
<span className="text-sm">{user}</span>
|
||||
<Button
|
||||
variant="quaternary"
|
||||
onClick={() => handleRemoveUser(user)}
|
||||
disabled={isUpdating}
|
||||
>
|
||||
{__("Remove")}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentExcludedUserNames.length === 0 && (
|
||||
<p className="text-sm text-txt-secondary text-center py-4">
|
||||
{__("No excluded user names. All Google Workspace users will be synced.")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
trigger={(
|
||||
<Button variant="danger">
|
||||
{__("Disconnect")}
|
||||
</Button>
|
||||
)}
|
||||
@@ -155,7 +282,9 @@ export function GoogleWorkspaceConnector(props: {
|
||||
onClick={handleDisconnect}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{isDeleting ? __("Disconnecting...") : __("Disconnect")}
|
||||
{isDeleting
|
||||
? __("Disconnecting...")
|
||||
: __("Disconnect")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
|
||||
Reference in New Issue
Block a user