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
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +16,8 @@ export type GoogleWorkspaceConnectorFragment$data = {
|
|||||||
readonly createdAt: string;
|
readonly createdAt: string;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
} | null | undefined;
|
} | null | undefined;
|
||||||
|
readonly excludedUserNames: ReadonlyArray<string>;
|
||||||
|
readonly id: string;
|
||||||
} | null | undefined;
|
} | null | undefined;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly " $fragmentType": "GoogleWorkspaceConnectorFragment";
|
readonly " $fragmentType": "GoogleWorkspaceConnectorFragment";
|
||||||
@@ -48,6 +50,14 @@ return {
|
|||||||
"name": "bridge",
|
"name": "bridge",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "excludedUserNames",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -76,6 +86,6 @@ return {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "6ce8b7f55ff2a3d0fad04ad6b452a28a";
|
(node as any).hash = "4f3898fca8d93369a46b50227f3df63d";
|
||||||
|
|
||||||
export default node;
|
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
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -190,6 +190,13 @@ return {
|
|||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v3/*: any*/),
|
(v3/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "excludedUserNames",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -409,12 +416,12 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "e57ee79b79046f9f07c159c7079f766a",
|
"cacheID": "1074234a9a3734270b5da7420dea2ec6",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "SCIMSettingsPageQuery",
|
"name": "SCIMSettingsPageQuery",
|
||||||
"operationKind": "query",
|
"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,
|
DialogContent,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
Google,
|
Google,
|
||||||
|
IconSettingsGear2,
|
||||||
|
Input,
|
||||||
useDialogRef,
|
useDialogRef,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
|
import { useState } from "react";
|
||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
|
|
||||||
import type { GoogleWorkspaceConnectorDeleteMutation } from "#/__generated__/iam/GoogleWorkspaceConnectorDeleteMutation.graphql";
|
import type { GoogleWorkspaceConnectorDeleteMutation } from "#/__generated__/iam/GoogleWorkspaceConnectorDeleteMutation.graphql";
|
||||||
import type { GoogleWorkspaceConnectorFragment$key } from "#/__generated__/iam/GoogleWorkspaceConnectorFragment.graphql";
|
import type { GoogleWorkspaceConnectorFragment$key } from "#/__generated__/iam/GoogleWorkspaceConnectorFragment.graphql";
|
||||||
|
import type { GoogleWorkspaceConnectorUpdateSCIMBridgeMutation } from "#/__generated__/iam/GoogleWorkspaceConnectorUpdateSCIMBridgeMutation.graphql";
|
||||||
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
|
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
|
||||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||||
|
|
||||||
@@ -21,6 +25,8 @@ const googleWorkspaceConnectorFragment = graphql`
|
|||||||
fragment GoogleWorkspaceConnectorFragment on SCIMConfiguration {
|
fragment GoogleWorkspaceConnectorFragment on SCIMConfiguration {
|
||||||
id
|
id
|
||||||
bridge {
|
bridge {
|
||||||
|
id
|
||||||
|
excludedUserNames
|
||||||
connector {
|
connector {
|
||||||
id
|
id
|
||||||
createdAt
|
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: {
|
export function GoogleWorkspaceConnector(props: {
|
||||||
fKey: GoogleWorkspaceConnectorFragment$key | null;
|
fKey: GoogleWorkspaceConnectorFragment$key | null;
|
||||||
}) {
|
}) {
|
||||||
const { fKey } = props;
|
const { fKey } = props;
|
||||||
const data = useFragment<GoogleWorkspaceConnectorFragment$key>(googleWorkspaceConnectorFragment, fKey);
|
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 scimConfigurationId = data?.id;
|
||||||
|
const bridgeId = bridge?.id;
|
||||||
|
|
||||||
const organizationId = useOrganizationId();
|
const organizationId = useOrganizationId();
|
||||||
const { __, dateTimeFormat } = useTranslate();
|
const { __, dateTimeFormat } = useTranslate();
|
||||||
const dialogRef = useDialogRef();
|
const dialogRef = useDialogRef();
|
||||||
|
const excludedUserNamesDialogRef = useDialogRef();
|
||||||
|
|
||||||
|
const [newUser, setNewUser] = useState("");
|
||||||
|
|
||||||
const [deleteSCIMConfiguration, isDeleting]
|
const [deleteSCIMConfiguration, isDeleting]
|
||||||
= useMutationWithToasts<GoogleWorkspaceConnectorDeleteMutation>(
|
= 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 handleConnect = () => {
|
||||||
const baseUrl = import.meta.env.VITE_API_URL || window.location.origin;
|
const baseUrl = import.meta.env.VITE_API_URL || window.location.origin;
|
||||||
const url = new URL("/api/console/v1/connectors/initiate", baseUrl);
|
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
|
// Not connected state
|
||||||
if (!connector) {
|
if (!connector) {
|
||||||
return (
|
return (
|
||||||
@@ -130,9 +191,75 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
{__("Connected")}
|
{__("Connected")}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Dialog
|
<Dialog
|
||||||
ref={dialogRef}
|
ref={excludedUserNamesDialogRef}
|
||||||
trigger={(
|
trigger={(
|
||||||
<Button variant="secondary">
|
<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")}
|
{__("Disconnect")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -155,7 +282,9 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
onClick={handleDisconnect}
|
onClick={handleDisconnect}
|
||||||
disabled={isDeleting}
|
disabled={isDeleting}
|
||||||
>
|
>
|
||||||
{isDeleting ? __("Disconnecting...") : __("Disconnect")}
|
{isDeleting
|
||||||
|
? __("Disconnecting...")
|
||||||
|
: __("Disconnect")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
3
pkg/coredata/migrations/20260209T160000Z.sql
Normal file
3
pkg/coredata/migrations/20260209T160000Z.sql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE iam_scim_bridges ADD COLUMN excluded_user_names TEXT[] NOT NULL DEFAULT '{}';
|
||||||
|
|
||||||
|
ALTER TABLE iam_scim_bridges ALTER COLUMN excluded_user_names DROP DEFAULT;
|
||||||
@@ -29,20 +29,21 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
SCIMBridge struct {
|
SCIMBridge struct {
|
||||||
ID gid.GID `db:"id"`
|
ID gid.GID `db:"id"`
|
||||||
OrganizationID gid.GID `db:"organization_id"`
|
OrganizationID gid.GID `db:"organization_id"`
|
||||||
ScimConfigurationID gid.GID `db:"scim_configuration_id"`
|
ScimConfigurationID gid.GID `db:"scim_configuration_id"`
|
||||||
ConnectorID *gid.GID `db:"connector_id"`
|
ConnectorID *gid.GID `db:"connector_id"`
|
||||||
Type SCIMBridgeType `db:"type"`
|
Type SCIMBridgeType `db:"type"`
|
||||||
State SCIMBridgeState `db:"state"`
|
State SCIMBridgeState `db:"state"`
|
||||||
LastSyncedAt *time.Time `db:"last_synced_at"`
|
ExcludedUserNames []string `db:"excluded_user_names"`
|
||||||
NextSyncAt *time.Time `db:"next_sync_at"`
|
LastSyncedAt *time.Time `db:"last_synced_at"`
|
||||||
SyncError *string `db:"sync_error"`
|
NextSyncAt *time.Time `db:"next_sync_at"`
|
||||||
ConsecutiveFailures int `db:"consecutive_failures"`
|
SyncError *string `db:"sync_error"`
|
||||||
TotalSyncCount int `db:"total_sync_count"`
|
ConsecutiveFailures int `db:"consecutive_failures"`
|
||||||
TotalFailureCount int `db:"total_failure_count"`
|
TotalSyncCount int `db:"total_sync_count"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
TotalFailureCount int `db:"total_failure_count"`
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
SCIMBridges []*SCIMBridge
|
SCIMBridges []*SCIMBridge
|
||||||
@@ -87,6 +88,7 @@ SELECT
|
|||||||
connector_id,
|
connector_id,
|
||||||
type,
|
type,
|
||||||
state,
|
state,
|
||||||
|
excluded_user_names,
|
||||||
last_synced_at,
|
last_synced_at,
|
||||||
next_sync_at,
|
next_sync_at,
|
||||||
sync_error,
|
sync_error,
|
||||||
@@ -141,6 +143,7 @@ SELECT
|
|||||||
connector_id,
|
connector_id,
|
||||||
type,
|
type,
|
||||||
state,
|
state,
|
||||||
|
excluded_user_names,
|
||||||
last_synced_at,
|
last_synced_at,
|
||||||
next_sync_at,
|
next_sync_at,
|
||||||
sync_error,
|
sync_error,
|
||||||
@@ -195,6 +198,7 @@ SELECT
|
|||||||
connector_id,
|
connector_id,
|
||||||
type,
|
type,
|
||||||
state,
|
state,
|
||||||
|
excluded_user_names,
|
||||||
last_synced_at,
|
last_synced_at,
|
||||||
next_sync_at,
|
next_sync_at,
|
||||||
sync_error,
|
sync_error,
|
||||||
@@ -249,6 +253,7 @@ INSERT INTO iam_scim_bridges (
|
|||||||
connector_id,
|
connector_id,
|
||||||
type,
|
type,
|
||||||
state,
|
state,
|
||||||
|
excluded_user_names,
|
||||||
last_synced_at,
|
last_synced_at,
|
||||||
next_sync_at,
|
next_sync_at,
|
||||||
sync_error,
|
sync_error,
|
||||||
@@ -265,6 +270,7 @@ INSERT INTO iam_scim_bridges (
|
|||||||
@connector_id,
|
@connector_id,
|
||||||
@type,
|
@type,
|
||||||
@state,
|
@state,
|
||||||
|
@excluded_user_names,
|
||||||
@last_synced_at,
|
@last_synced_at,
|
||||||
@next_sync_at,
|
@next_sync_at,
|
||||||
@sync_error,
|
@sync_error,
|
||||||
@@ -284,6 +290,7 @@ INSERT INTO iam_scim_bridges (
|
|||||||
"connector_id": s.ConnectorID,
|
"connector_id": s.ConnectorID,
|
||||||
"type": s.Type,
|
"type": s.Type,
|
||||||
"state": s.State,
|
"state": s.State,
|
||||||
|
"excluded_user_names": s.ExcludedUserNames,
|
||||||
"last_synced_at": s.LastSyncedAt,
|
"last_synced_at": s.LastSyncedAt,
|
||||||
"next_sync_at": s.NextSyncAt,
|
"next_sync_at": s.NextSyncAt,
|
||||||
"sync_error": s.SyncError,
|
"sync_error": s.SyncError,
|
||||||
@@ -312,6 +319,7 @@ UPDATE iam_scim_bridges
|
|||||||
SET
|
SET
|
||||||
connector_id = @connector_id,
|
connector_id = @connector_id,
|
||||||
state = @state,
|
state = @state,
|
||||||
|
excluded_user_names = @excluded_user_names,
|
||||||
last_synced_at = @last_synced_at,
|
last_synced_at = @last_synced_at,
|
||||||
next_sync_at = @next_sync_at,
|
next_sync_at = @next_sync_at,
|
||||||
sync_error = @sync_error,
|
sync_error = @sync_error,
|
||||||
@@ -330,6 +338,7 @@ WHERE
|
|||||||
"id": s.ID,
|
"id": s.ID,
|
||||||
"connector_id": s.ConnectorID,
|
"connector_id": s.ConnectorID,
|
||||||
"state": s.State,
|
"state": s.State,
|
||||||
|
"excluded_user_names": s.ExcludedUserNames,
|
||||||
"last_synced_at": s.LastSyncedAt,
|
"last_synced_at": s.LastSyncedAt,
|
||||||
"next_sync_at": s.NextSyncAt,
|
"next_sync_at": s.NextSyncAt,
|
||||||
"sync_error": s.SyncError,
|
"sync_error": s.SyncError,
|
||||||
@@ -364,6 +373,7 @@ SELECT
|
|||||||
connector_id,
|
connector_id,
|
||||||
type,
|
type,
|
||||||
state,
|
state,
|
||||||
|
excluded_user_names,
|
||||||
last_synced_at,
|
last_synced_at,
|
||||||
next_sync_at,
|
next_sync_at,
|
||||||
sync_error,
|
sync_error,
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ const (
|
|||||||
// SCIM Bridge actions
|
// SCIM Bridge actions
|
||||||
ActionSCIMBridgeGet = "iam:scim-bridge:get"
|
ActionSCIMBridgeGet = "iam:scim-bridge:get"
|
||||||
ActionSCIMBridgeCreate = "iam:scim-bridge:create"
|
ActionSCIMBridgeCreate = "iam:scim-bridge:create"
|
||||||
|
ActionSCIMBridgeUpdate = "iam:scim-bridge:update"
|
||||||
ActionSCIMBridgeDelete = "iam:scim-bridge:delete"
|
ActionSCIMBridgeDelete = "iam:scim-bridge:delete"
|
||||||
|
|
||||||
// Connector actions
|
// Connector actions
|
||||||
|
|||||||
@@ -180,6 +180,11 @@ var IAMOwnerPolicy = policy.NewPolicy(
|
|||||||
policy.Allow("iam:scim-event:*").
|
policy.Allow("iam:scim-event:*").
|
||||||
WithSID("full-scim-event-access").
|
WithSID("full-scim-event-access").
|
||||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||||
|
|
||||||
|
// Allow updating SCIM bridge settings (scoped to own organization)
|
||||||
|
policy.Allow(ActionSCIMBridgeUpdate).
|
||||||
|
WithSID("scim-bridge-update-access").
|
||||||
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||||
).
|
).
|
||||||
WithDescription("Full IAM access for organization owners")
|
WithDescription("Full IAM access for organization owners")
|
||||||
|
|
||||||
|
|||||||
@@ -1434,6 +1434,50 @@ func (s OrganizationService) RegenerateSCIMToken(
|
|||||||
return config, token, nil
|
return config, token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s OrganizationService) UpdateSCIMBridge(
|
||||||
|
ctx context.Context,
|
||||||
|
organizationID gid.GID,
|
||||||
|
bridgeID gid.GID,
|
||||||
|
excludedUserNames []string,
|
||||||
|
) (*coredata.SCIMBridge, error) {
|
||||||
|
bridge := &coredata.SCIMBridge{}
|
||||||
|
scope := coredata.NewScopeFromObjectID(bridgeID)
|
||||||
|
|
||||||
|
err := s.pg.WithTx(
|
||||||
|
ctx,
|
||||||
|
func(tx pg.Conn) error {
|
||||||
|
err := bridge.LoadByID(ctx, tx, scope, bridgeID)
|
||||||
|
if err != nil {
|
||||||
|
if err == coredata.ErrResourceNotFound {
|
||||||
|
return fmt.Errorf("SCIM bridge not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("cannot load SCIM bridge: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bridge.OrganizationID != organizationID {
|
||||||
|
return fmt.Errorf("SCIM bridge not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge.ExcludedUserNames = excludedUserNames
|
||||||
|
bridge.UpdatedAt = time.Now()
|
||||||
|
|
||||||
|
err = bridge.Update(ctx, tx, scope)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot update SCIM bridge: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s OrganizationService) ListSCIMEventsByConfigID(
|
func (s OrganizationService) ListSCIMEventsByConfigID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scimConfigurationID gid.GID,
|
scimConfigurationID gid.GID,
|
||||||
@@ -1863,6 +1907,7 @@ func (s OrganizationService) CreateSCIMBridge(
|
|||||||
ConnectorID: &connectorID,
|
ConnectorID: &connectorID,
|
||||||
Type: bridgeType,
|
Type: bridgeType,
|
||||||
State: coredata.SCIMBridgeStateActive, // Active immediately since connector already exists
|
State: coredata.SCIMBridgeStateActive, // Active immediately since connector already exists
|
||||||
|
ExcludedUserNames: []string{},
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
Bridge struct {
|
Bridge struct {
|
||||||
provider provider.Provider
|
provider provider.Provider
|
||||||
scimClient *scimclient.Client
|
scimClient *scimclient.Client
|
||||||
forceUpdate bool
|
excludedUserNames []string
|
||||||
dryRun bool
|
forceUpdate bool
|
||||||
|
dryRun bool
|
||||||
}
|
}
|
||||||
|
|
||||||
Option func(*Bridge)
|
Option func(*Bridge)
|
||||||
@@ -49,6 +50,12 @@ func WithForceUpdate(forceUpdate bool) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithExcludedUserNames(excludedUserNames []string) Option {
|
||||||
|
return func(s *Bridge) {
|
||||||
|
s.excludedUserNames = excludedUserNames
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewBridge(provider provider.Provider, scimClient *scimclient.Client, opts ...Option) *Bridge {
|
func NewBridge(provider provider.Provider, scimClient *scimclient.Client, opts ...Option) *Bridge {
|
||||||
s := &Bridge{
|
s := &Bridge{
|
||||||
provider: provider,
|
provider: provider,
|
||||||
@@ -62,15 +69,15 @@ func NewBridge(provider provider.Provider, scimClient *scimclient.Client, opts .
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Bridge) Run(ctx context.Context) (created, updated, deactivated, skipped int, err error) {
|
func (s *Bridge) Run(ctx context.Context) (created, updated, deleted, deactivated, skipped int, err error) {
|
||||||
providerUsers, err := s.provider.ListUsers(ctx)
|
providerUsers, err := s.provider.ListUsers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, 0, 0, fmt.Errorf("cannot list provider users: %w", err)
|
return 0, 0, 0, 0, 0, fmt.Errorf("cannot list provider users: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scimUsers, err := s.scimClient.ListUsers(ctx)
|
scimUsers, err := s.scimClient.ListUsers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, 0, 0, fmt.Errorf("cannot list scim users: %w", err)
|
return 0, 0, 0, 0, 0, fmt.Errorf("cannot list scim users: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scimUsersByEmail := make(map[string]*scimclient.User)
|
scimUsersByEmail := make(map[string]*scimclient.User)
|
||||||
@@ -125,6 +132,17 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deactivated, skippe
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.isExcluded(email) {
|
||||||
|
if !s.dryRun {
|
||||||
|
if err := s.scimClient.DeleteUser(ctx, scimUser.ID); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("cannot delete user %q: %w", email, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleted++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if !scimUser.Active {
|
if !scimUser.Active {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -138,5 +156,14 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deactivated, skippe
|
|||||||
deactivated++
|
deactivated++
|
||||||
}
|
}
|
||||||
|
|
||||||
return created, updated, deactivated, skipped, errors.Join(errs...)
|
return created, updated, deleted, deactivated, skipped, errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Bridge) isExcluded(email string) bool {
|
||||||
|
for _, excluded := range s.excludedUserNames {
|
||||||
|
if strings.EqualFold(excluded, email) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,6 +248,29 @@ func (c *Client) DeactivateUser(ctx context.Context, userID string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) DeleteUser(ctx context.Context, userID string) error {
|
||||||
|
reqURL := fmt.Sprintf("%s/Users/%s", c.endpoint, url.PathEscape(userID))
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, reqURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.setHeaders(req)
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot delete user: %w", err)
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusNotFound {
|
||||||
|
respBody, _ := io.ReadAll(resp.Body)
|
||||||
|
return fmt.Errorf("SCIM API error: status %d, body: %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) setHeaders(req *http.Request) {
|
func (c *Client) setHeaders(req *http.Request) {
|
||||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||||
req.Header.Set("Accept", "application/scim+json")
|
req.Header.Set("Accept", "application/scim+json")
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
admin "google.golang.org/api/admin/directory/v1"
|
admin "google.golang.org/api/admin/directory/v1"
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
@@ -31,12 +32,14 @@ import (
|
|||||||
var _ provider.Provider = (*Provider)(nil)
|
var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
excludedUserNames []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(httpClient *http.Client) *Provider {
|
func New(httpClient *http.Client, excludedUserNames []string) *Provider {
|
||||||
return &Provider{
|
return &Provider{
|
||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
|
excludedUserNames: excludedUserNames,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +47,16 @@ func (p *Provider) Name() string {
|
|||||||
return "google-workspace"
|
return "google-workspace"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) isExcluded(email string) bool {
|
||||||
|
emailLower := strings.ToLower(email)
|
||||||
|
for _, excluded := range p.excludedUserNames {
|
||||||
|
if strings.ToLower(excluded) == emailLower {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
|
func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
|
||||||
adminService, err := admin.NewService(ctx, option.WithHTTPClient(p.httpClient))
|
adminService, err := admin.NewService(ctx, option.WithHTTPClient(p.httpClient))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -65,6 +78,10 @@ func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range resp.Users {
|
for _, u := range resp.Users {
|
||||||
|
if p.isExcluded(u.PrimaryEmail) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
allUsers = append(
|
allUsers = append(
|
||||||
allUsers,
|
allUsers,
|
||||||
scimclient.User{
|
scimclient.User{
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
type SyncStats struct {
|
type SyncStats struct {
|
||||||
Created int
|
Created int
|
||||||
Updated int
|
Updated int
|
||||||
|
Deleted int
|
||||||
Deactivated int
|
Deactivated int
|
||||||
Skipped int
|
Skipped int
|
||||||
}
|
}
|
||||||
@@ -104,6 +105,7 @@ func (r *BridgeRunner) transitionToSuccess(
|
|||||||
log.Duration("sync_duration", duration),
|
log.Duration("sync_duration", duration),
|
||||||
log.Int("users_created", stats.Created),
|
log.Int("users_created", stats.Created),
|
||||||
log.Int("users_updated", stats.Updated),
|
log.Int("users_updated", stats.Updated),
|
||||||
|
log.Int("users_deleted", stats.Deleted),
|
||||||
log.Int("users_deactivated", stats.Deactivated),
|
log.Int("users_deactivated", stats.Deactivated),
|
||||||
log.Int("users_skipped", stats.Skipped),
|
log.Int("users_skipped", stats.Skipped),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func (r *BridgeRunner) doSync(
|
|||||||
return SyncStats{}, nil, fmt.Errorf("cannot load connector: %w", err)
|
return SyncStats{}, nil, fmt.Errorf("cannot load connector: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
idp, err := r.createProvider(ctx, logger, scimBridge.Type, dbConnector)
|
idp, err := r.createProvider(ctx, logger, scimBridge.Type, dbConnector, scimBridge.ExcludedUserNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SyncStats{}, nil, fmt.Errorf("cannot create provider: %w", err)
|
return SyncStats{}, nil, fmt.Errorf("cannot create provider: %w", err)
|
||||||
}
|
}
|
||||||
@@ -89,8 +89,8 @@ func (r *BridgeRunner) doSync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
scimClient := r.createSCIMClient(logger, token)
|
scimClient := r.createSCIMClient(logger, token)
|
||||||
syncer := bridge.NewBridge(idp, scimClient)
|
syncer := bridge.NewBridge(idp, scimClient, bridge.WithExcludedUserNames(scimBridge.ExcludedUserNames))
|
||||||
created, updated, deactivated, skipped, err := syncer.Run(ctx)
|
created, updated, deleted, deactivated, skipped, err := syncer.Run(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SyncStats{}, nil, fmt.Errorf("sync failed: %w", err)
|
return SyncStats{}, nil, fmt.Errorf("sync failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -98,6 +98,7 @@ func (r *BridgeRunner) doSync(
|
|||||||
stats := SyncStats{
|
stats := SyncStats{
|
||||||
Created: created,
|
Created: created,
|
||||||
Updated: updated,
|
Updated: updated,
|
||||||
|
Deleted: deleted,
|
||||||
Deactivated: deactivated,
|
Deactivated: deactivated,
|
||||||
Skipped: skipped,
|
Skipped: skipped,
|
||||||
}
|
}
|
||||||
@@ -121,10 +122,11 @@ func (r *BridgeRunner) createProvider(
|
|||||||
logger *log.Logger,
|
logger *log.Logger,
|
||||||
bridgeType coredata.SCIMBridgeType,
|
bridgeType coredata.SCIMBridgeType,
|
||||||
dbConnector *coredata.Connector,
|
dbConnector *coredata.Connector,
|
||||||
|
excludedUserNames []string,
|
||||||
) (provider.Provider, error) {
|
) (provider.Provider, error) {
|
||||||
switch bridgeType {
|
switch bridgeType {
|
||||||
case coredata.SCIMBridgeTypeGoogleWorkspace:
|
case coredata.SCIMBridgeTypeGoogleWorkspace:
|
||||||
return r.createGoogleWorkspaceProvider(ctx, logger, dbConnector)
|
return r.createGoogleWorkspaceProvider(ctx, logger, dbConnector, excludedUserNames)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported bridge type: %s", bridgeType)
|
return nil, fmt.Errorf("unsupported bridge type: %s", bridgeType)
|
||||||
}
|
}
|
||||||
@@ -134,6 +136,7 @@ func (r *BridgeRunner) createGoogleWorkspaceProvider(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *log.Logger,
|
logger *log.Logger,
|
||||||
dbConnector *coredata.Connector,
|
dbConnector *coredata.Connector,
|
||||||
|
excludedUserNames []string,
|
||||||
) (provider.Provider, error) {
|
) (provider.Provider, error) {
|
||||||
if dbConnector.Connection == nil {
|
if dbConnector.Connection == nil {
|
||||||
return nil, fmt.Errorf("connector has no connection configured")
|
return nil, fmt.Errorf("connector has no connection configured")
|
||||||
@@ -161,7 +164,7 @@ func (r *BridgeRunner) createGoogleWorkspaceProvider(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create HTTP client: %w", err)
|
return nil, fmt.Errorf("cannot create HTTP client: %w", err)
|
||||||
}
|
}
|
||||||
return googleworkspace.New(httpClient), nil
|
return googleworkspace.New(httpClient, excludedUserNames), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient, err := oauth2Conn.RefreshableClient(ctx, *refreshCfg, httpClientOpts...)
|
httpClient, err := oauth2Conn.RefreshableClient(ctx, *refreshCfg, httpClientOpts...)
|
||||||
@@ -169,5 +172,5 @@ func (r *BridgeRunner) createGoogleWorkspaceProvider(
|
|||||||
return nil, fmt.Errorf("cannot create refreshable HTTP client: %w", err)
|
return nil, fmt.Errorf("cannot create refreshable HTTP client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return googleworkspace.New(httpClient), nil
|
return googleworkspace.New(httpClient, excludedUserNames), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ type Mutation {
|
|||||||
regenerateSCIMToken(
|
regenerateSCIMToken(
|
||||||
input: RegenerateSCIMTokenInput!
|
input: RegenerateSCIMTokenInput!
|
||||||
): RegenerateSCIMTokenPayload @session(required: PRESENT)
|
): RegenerateSCIMTokenPayload @session(required: PRESENT)
|
||||||
|
updateSCIMBridge(
|
||||||
|
input: UpdateSCIMBridgeInput!
|
||||||
|
): UpdateSCIMBridgePayload @session(required: PRESENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Identity implements Node {
|
type Identity implements Node {
|
||||||
@@ -368,6 +371,7 @@ type SCIMBridge implements Node {
|
|||||||
scimConfiguration: SCIMConfiguration @goField(forceResolver: true)
|
scimConfiguration: SCIMConfiguration @goField(forceResolver: true)
|
||||||
connector: Connector @goField(forceResolver: true)
|
connector: Connector @goField(forceResolver: true)
|
||||||
type: SCIMBridgeType!
|
type: SCIMBridgeType!
|
||||||
|
excludedUserNames: [String!]!
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
|
|
||||||
@@ -905,6 +909,12 @@ input RegenerateSCIMTokenInput {
|
|||||||
scimConfigurationId: ID!
|
scimConfigurationId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input UpdateSCIMBridgeInput {
|
||||||
|
organizationId: ID!
|
||||||
|
scimBridgeId: ID!
|
||||||
|
excludedUserNames: [String!]!
|
||||||
|
}
|
||||||
|
|
||||||
type CreateSCIMConfigurationPayload {
|
type CreateSCIMConfigurationPayload {
|
||||||
scimConfiguration: SCIMConfiguration!
|
scimConfiguration: SCIMConfiguration!
|
||||||
scimBridge: SCIMBridge
|
scimBridge: SCIMBridge
|
||||||
@@ -919,3 +929,7 @@ type RegenerateSCIMTokenPayload {
|
|||||||
scimConfiguration: SCIMConfiguration!
|
scimConfiguration: SCIMConfiguration!
|
||||||
token: String!
|
token: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateSCIMBridgePayload {
|
||||||
|
scimBridge: SCIMBridge!
|
||||||
|
}
|
||||||
|
|||||||
@@ -246,6 +246,7 @@ type ComplexityRoot struct {
|
|||||||
UpdateMembership func(childComplexity int, input types.UpdateMembershipInput) int
|
UpdateMembership func(childComplexity int, input types.UpdateMembershipInput) int
|
||||||
UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int
|
UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int
|
||||||
UpdateSAMLConfiguration func(childComplexity int, input types.UpdateSAMLConfigurationInput) int
|
UpdateSAMLConfiguration func(childComplexity int, input types.UpdateSAMLConfigurationInput) int
|
||||||
|
UpdateSCIMBridge func(childComplexity int, input types.UpdateSCIMBridgeInput) int
|
||||||
VerifyEmail func(childComplexity int, input types.VerifyEmailInput) int
|
VerifyEmail func(childComplexity int, input types.VerifyEmailInput) int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,6 +380,7 @@ type ComplexityRoot struct {
|
|||||||
SCIMBridge struct {
|
SCIMBridge struct {
|
||||||
Connector func(childComplexity int) int
|
Connector func(childComplexity int) int
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
|
ExcludedUserNames func(childComplexity int) int
|
||||||
ID func(childComplexity int) int
|
ID func(childComplexity int) int
|
||||||
Permission func(childComplexity int, action string) int
|
Permission func(childComplexity int, action string) int
|
||||||
ScimConfiguration func(childComplexity int) int
|
ScimConfiguration func(childComplexity int) int
|
||||||
@@ -474,6 +476,10 @@ type ComplexityRoot struct {
|
|||||||
SamlConfiguration func(childComplexity int) int
|
SamlConfiguration func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateSCIMBridgePayload struct {
|
||||||
|
ScimBridge func(childComplexity int) int
|
||||||
|
}
|
||||||
|
|
||||||
VerifyEmailPayload struct {
|
VerifyEmailPayload struct {
|
||||||
Success func(childComplexity int) int
|
Success func(childComplexity int) int
|
||||||
}
|
}
|
||||||
@@ -540,6 +546,7 @@ type MutationResolver interface {
|
|||||||
CreateSCIMConfiguration(ctx context.Context, input types.CreateSCIMConfigurationInput) (*types.CreateSCIMConfigurationPayload, error)
|
CreateSCIMConfiguration(ctx context.Context, input types.CreateSCIMConfigurationInput) (*types.CreateSCIMConfigurationPayload, error)
|
||||||
DeleteSCIMConfiguration(ctx context.Context, input types.DeleteSCIMConfigurationInput) (*types.DeleteSCIMConfigurationPayload, error)
|
DeleteSCIMConfiguration(ctx context.Context, input types.DeleteSCIMConfigurationInput) (*types.DeleteSCIMConfigurationPayload, error)
|
||||||
RegenerateSCIMToken(ctx context.Context, input types.RegenerateSCIMTokenInput) (*types.RegenerateSCIMTokenPayload, error)
|
RegenerateSCIMToken(ctx context.Context, input types.RegenerateSCIMTokenInput) (*types.RegenerateSCIMTokenPayload, error)
|
||||||
|
UpdateSCIMBridge(ctx context.Context, input types.UpdateSCIMBridgeInput) (*types.UpdateSCIMBridgePayload, error)
|
||||||
}
|
}
|
||||||
type OrganizationResolver interface {
|
type OrganizationResolver interface {
|
||||||
LogoURL(ctx context.Context, obj *types.Organization) (*string, error)
|
LogoURL(ctx context.Context, obj *types.Organization) (*string, error)
|
||||||
@@ -1415,6 +1422,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Mutation.UpdateSAMLConfiguration(childComplexity, args["input"].(types.UpdateSAMLConfigurationInput)), true
|
return e.complexity.Mutation.UpdateSAMLConfiguration(childComplexity, args["input"].(types.UpdateSAMLConfigurationInput)), true
|
||||||
|
case "Mutation.updateSCIMBridge":
|
||||||
|
if e.complexity.Mutation.UpdateSCIMBridge == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_updateSCIMBridge_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.UpdateSCIMBridge(childComplexity, args["input"].(types.UpdateSCIMBridgeInput)), true
|
||||||
case "Mutation.verifyEmail":
|
case "Mutation.verifyEmail":
|
||||||
if e.complexity.Mutation.VerifyEmail == nil {
|
if e.complexity.Mutation.VerifyEmail == nil {
|
||||||
break
|
break
|
||||||
@@ -1918,6 +1936,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.SCIMBridge.CreatedAt(childComplexity), true
|
return e.complexity.SCIMBridge.CreatedAt(childComplexity), true
|
||||||
|
case "SCIMBridge.excludedUserNames":
|
||||||
|
if e.complexity.SCIMBridge.ExcludedUserNames == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.SCIMBridge.ExcludedUserNames(childComplexity), true
|
||||||
case "SCIMBridge.id":
|
case "SCIMBridge.id":
|
||||||
if e.complexity.SCIMBridge.ID == nil {
|
if e.complexity.SCIMBridge.ID == nil {
|
||||||
break
|
break
|
||||||
@@ -2264,6 +2288,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
|||||||
|
|
||||||
return e.complexity.UpdateSAMLConfigurationPayload.SamlConfiguration(childComplexity), true
|
return e.complexity.UpdateSAMLConfigurationPayload.SamlConfiguration(childComplexity), true
|
||||||
|
|
||||||
|
case "UpdateSCIMBridgePayload.scimBridge":
|
||||||
|
if e.complexity.UpdateSCIMBridgePayload.ScimBridge == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.UpdateSCIMBridgePayload.ScimBridge(childComplexity), true
|
||||||
|
|
||||||
case "VerifyEmailPayload.success":
|
case "VerifyEmailPayload.success":
|
||||||
if e.complexity.VerifyEmailPayload.Success == nil {
|
if e.complexity.VerifyEmailPayload.Success == nil {
|
||||||
break
|
break
|
||||||
@@ -2310,6 +2341,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
|||||||
ec.unmarshalInputUpdateMembershipInput,
|
ec.unmarshalInputUpdateMembershipInput,
|
||||||
ec.unmarshalInputUpdateOrganizationInput,
|
ec.unmarshalInputUpdateOrganizationInput,
|
||||||
ec.unmarshalInputUpdateSAMLConfigurationInput,
|
ec.unmarshalInputUpdateSAMLConfigurationInput,
|
||||||
|
ec.unmarshalInputUpdateSCIMBridgeInput,
|
||||||
ec.unmarshalInputVerifyEmailInput,
|
ec.unmarshalInputVerifyEmailInput,
|
||||||
)
|
)
|
||||||
first := true
|
first := true
|
||||||
@@ -2534,6 +2566,9 @@ type Mutation {
|
|||||||
regenerateSCIMToken(
|
regenerateSCIMToken(
|
||||||
input: RegenerateSCIMTokenInput!
|
input: RegenerateSCIMTokenInput!
|
||||||
): RegenerateSCIMTokenPayload @session(required: PRESENT)
|
): RegenerateSCIMTokenPayload @session(required: PRESENT)
|
||||||
|
updateSCIMBridge(
|
||||||
|
input: UpdateSCIMBridgeInput!
|
||||||
|
): UpdateSCIMBridgePayload @session(required: PRESENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Identity implements Node {
|
type Identity implements Node {
|
||||||
@@ -2778,6 +2813,7 @@ type SCIMBridge implements Node {
|
|||||||
scimConfiguration: SCIMConfiguration @goField(forceResolver: true)
|
scimConfiguration: SCIMConfiguration @goField(forceResolver: true)
|
||||||
connector: Connector @goField(forceResolver: true)
|
connector: Connector @goField(forceResolver: true)
|
||||||
type: SCIMBridgeType!
|
type: SCIMBridgeType!
|
||||||
|
excludedUserNames: [String!]!
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
|
|
||||||
@@ -3315,6 +3351,12 @@ input RegenerateSCIMTokenInput {
|
|||||||
scimConfigurationId: ID!
|
scimConfigurationId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input UpdateSCIMBridgeInput {
|
||||||
|
organizationId: ID!
|
||||||
|
scimBridgeId: ID!
|
||||||
|
excludedUserNames: [String!]!
|
||||||
|
}
|
||||||
|
|
||||||
type CreateSCIMConfigurationPayload {
|
type CreateSCIMConfigurationPayload {
|
||||||
scimConfiguration: SCIMConfiguration!
|
scimConfiguration: SCIMConfiguration!
|
||||||
scimBridge: SCIMBridge
|
scimBridge: SCIMBridge
|
||||||
@@ -3329,6 +3371,10 @@ type RegenerateSCIMTokenPayload {
|
|||||||
scimConfiguration: SCIMConfiguration!
|
scimConfiguration: SCIMConfiguration!
|
||||||
token: String!
|
token: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateSCIMBridgePayload {
|
||||||
|
scimBridge: SCIMBridge!
|
||||||
|
}
|
||||||
`, BuiltIn: false},
|
`, BuiltIn: false},
|
||||||
{Name: "../../../../gqlutils/directives/session/schema.graphql", Input: `# Session directive for GraphQL APIs
|
{Name: "../../../../gqlutils/directives/session/schema.graphql", Input: `# Session directive for GraphQL APIs
|
||||||
# Include this schema in your gqlgen configuration to enable session-based access control.
|
# Include this schema in your gqlgen configuration to enable session-based access control.
|
||||||
@@ -3847,6 +3893,17 @@ func (ec *executionContext) field_Mutation_updateSAMLConfiguration_args(ctx cont
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_updateSCIMBridge_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]any{}
|
||||||
|
arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNUpdateSCIMBridgeInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐUpdateSCIMBridgeInput)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args["input"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_verifyEmail_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
func (ec *executionContext) field_Mutation_verifyEmail_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]any{}
|
args := map[string]any{}
|
||||||
@@ -4770,6 +4827,8 @@ func (ec *executionContext) fieldContext_CreateSCIMConfigurationPayload_scimBrid
|
|||||||
return ec.fieldContext_SCIMBridge_connector(ctx, field)
|
return ec.fieldContext_SCIMBridge_connector(ctx, field)
|
||||||
case "type":
|
case "type":
|
||||||
return ec.fieldContext_SCIMBridge_type(ctx, field)
|
return ec.fieldContext_SCIMBridge_type(ctx, field)
|
||||||
|
case "excludedUserNames":
|
||||||
|
return ec.fieldContext_SCIMBridge_excludedUserNames(ctx, field)
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
return ec.fieldContext_SCIMBridge_createdAt(ctx, field)
|
return ec.fieldContext_SCIMBridge_createdAt(ctx, field)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
@@ -8586,6 +8645,69 @@ func (ec *executionContext) fieldContext_Mutation_regenerateSCIMToken(ctx contex
|
|||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_updateSCIMBridge(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
return graphql.ResolveField(
|
||||||
|
ctx,
|
||||||
|
ec.OperationContext,
|
||||||
|
field,
|
||||||
|
ec.fieldContext_Mutation_updateSCIMBridge,
|
||||||
|
func(ctx context.Context) (any, error) {
|
||||||
|
fc := graphql.GetFieldContext(ctx)
|
||||||
|
return ec.resolvers.Mutation().UpdateSCIMBridge(ctx, fc.Args["input"].(types.UpdateSCIMBridgeInput))
|
||||||
|
},
|
||||||
|
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||||
|
directive0 := next
|
||||||
|
|
||||||
|
directive1 := func(ctx context.Context) (any, error) {
|
||||||
|
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT")
|
||||||
|
if err != nil {
|
||||||
|
var zeroVal *types.UpdateSCIMBridgePayload
|
||||||
|
return zeroVal, err
|
||||||
|
}
|
||||||
|
if ec.directives.Session == nil {
|
||||||
|
var zeroVal *types.UpdateSCIMBridgePayload
|
||||||
|
return zeroVal, errors.New("directive session is not implemented")
|
||||||
|
}
|
||||||
|
return ec.directives.Session(ctx, nil, directive0, required)
|
||||||
|
}
|
||||||
|
|
||||||
|
next = directive1
|
||||||
|
return next
|
||||||
|
},
|
||||||
|
ec.marshalOUpdateSCIMBridgePayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐUpdateSCIMBridgePayload,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_Mutation_updateSCIMBridge(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
IsMethod: true,
|
||||||
|
IsResolver: true,
|
||||||
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
|
switch field.Name {
|
||||||
|
case "scimBridge":
|
||||||
|
return ec.fieldContext_UpdateSCIMBridgePayload_scimBridge(ctx, field)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no field named %q was found under type UpdateSCIMBridgePayload", field.Name)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = ec.Recover(ctx, r)
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
if fc.Args, err = ec.field_Mutation_updateSCIMBridge_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return fc, err
|
||||||
|
}
|
||||||
|
return fc, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Organization_id(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Organization_id(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) {
|
||||||
return graphql.ResolveField(
|
return graphql.ResolveField(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -11337,6 +11459,35 @@ func (ec *executionContext) fieldContext_SCIMBridge_type(_ context.Context, fiel
|
|||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _SCIMBridge_excludedUserNames(ctx context.Context, field graphql.CollectedField, obj *types.SCIMBridge) (ret graphql.Marshaler) {
|
||||||
|
return graphql.ResolveField(
|
||||||
|
ctx,
|
||||||
|
ec.OperationContext,
|
||||||
|
field,
|
||||||
|
ec.fieldContext_SCIMBridge_excludedUserNames,
|
||||||
|
func(ctx context.Context) (any, error) {
|
||||||
|
return obj.ExcludedUserNames, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
ec.marshalNString2ᚕstringᚄ,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_SCIMBridge_excludedUserNames(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "SCIMBridge",
|
||||||
|
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) _SCIMBridge_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.SCIMBridge) (ret graphql.Marshaler) {
|
func (ec *executionContext) _SCIMBridge_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.SCIMBridge) (ret graphql.Marshaler) {
|
||||||
return graphql.ResolveField(
|
return graphql.ResolveField(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -11667,6 +11818,8 @@ func (ec *executionContext) fieldContext_SCIMConfiguration_bridge(_ context.Cont
|
|||||||
return ec.fieldContext_SCIMBridge_connector(ctx, field)
|
return ec.fieldContext_SCIMBridge_connector(ctx, field)
|
||||||
case "type":
|
case "type":
|
||||||
return ec.fieldContext_SCIMBridge_type(ctx, field)
|
return ec.fieldContext_SCIMBridge_type(ctx, field)
|
||||||
|
case "excludedUserNames":
|
||||||
|
return ec.fieldContext_SCIMBridge_excludedUserNames(ctx, field)
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
return ec.fieldContext_SCIMBridge_createdAt(ctx, field)
|
return ec.fieldContext_SCIMBridge_createdAt(ctx, field)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
@@ -13217,6 +13370,55 @@ func (ec *executionContext) fieldContext_UpdateSAMLConfigurationPayload_samlConf
|
|||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _UpdateSCIMBridgePayload_scimBridge(ctx context.Context, field graphql.CollectedField, obj *types.UpdateSCIMBridgePayload) (ret graphql.Marshaler) {
|
||||||
|
return graphql.ResolveField(
|
||||||
|
ctx,
|
||||||
|
ec.OperationContext,
|
||||||
|
field,
|
||||||
|
ec.fieldContext_UpdateSCIMBridgePayload_scimBridge,
|
||||||
|
func(ctx context.Context) (any, error) {
|
||||||
|
return obj.ScimBridge, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
ec.marshalNSCIMBridge2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSCIMBridge,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_UpdateSCIMBridgePayload_scimBridge(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "UpdateSCIMBridgePayload",
|
||||||
|
Field: field,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
|
switch field.Name {
|
||||||
|
case "id":
|
||||||
|
return ec.fieldContext_SCIMBridge_id(ctx, field)
|
||||||
|
case "state":
|
||||||
|
return ec.fieldContext_SCIMBridge_state(ctx, field)
|
||||||
|
case "scimConfiguration":
|
||||||
|
return ec.fieldContext_SCIMBridge_scimConfiguration(ctx, field)
|
||||||
|
case "connector":
|
||||||
|
return ec.fieldContext_SCIMBridge_connector(ctx, field)
|
||||||
|
case "type":
|
||||||
|
return ec.fieldContext_SCIMBridge_type(ctx, field)
|
||||||
|
case "excludedUserNames":
|
||||||
|
return ec.fieldContext_SCIMBridge_excludedUserNames(ctx, field)
|
||||||
|
case "createdAt":
|
||||||
|
return ec.fieldContext_SCIMBridge_createdAt(ctx, field)
|
||||||
|
case "updatedAt":
|
||||||
|
return ec.fieldContext_SCIMBridge_updatedAt(ctx, field)
|
||||||
|
case "permission":
|
||||||
|
return ec.fieldContext_SCIMBridge_permission(ctx, field)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no field named %q was found under type SCIMBridge", field.Name)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return fc, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _VerifyEmailPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.VerifyEmailPayload) (ret graphql.Marshaler) {
|
func (ec *executionContext) _VerifyEmailPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.VerifyEmailPayload) (ret graphql.Marshaler) {
|
||||||
return graphql.ResolveField(
|
return graphql.ResolveField(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -15872,6 +16074,47 @@ func (ec *executionContext) unmarshalInputUpdateSAMLConfigurationInput(ctx conte
|
|||||||
return it, nil
|
return it, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputUpdateSCIMBridgeInput(ctx context.Context, obj any) (types.UpdateSCIMBridgeInput, error) {
|
||||||
|
var it types.UpdateSCIMBridgeInput
|
||||||
|
asMap := map[string]any{}
|
||||||
|
for k, v := range obj.(map[string]any) {
|
||||||
|
asMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsInOrder := [...]string{"organizationId", "scimBridgeId", "excludedUserNames"}
|
||||||
|
for _, k := range fieldsInOrder {
|
||||||
|
v, ok := asMap[k]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch k {
|
||||||
|
case "organizationId":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId"))
|
||||||
|
data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.OrganizationID = data
|
||||||
|
case "scimBridgeId":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scimBridgeId"))
|
||||||
|
data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.ScimBridgeID = data
|
||||||
|
case "excludedUserNames":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("excludedUserNames"))
|
||||||
|
data, err := ec.unmarshalNString2ᚕstringᚄ(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.ExcludedUserNames = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputVerifyEmailInput(ctx context.Context, obj any) (types.VerifyEmailInput, error) {
|
func (ec *executionContext) unmarshalInputVerifyEmailInput(ctx context.Context, obj any) (types.VerifyEmailInput, error) {
|
||||||
var it types.VerifyEmailInput
|
var it types.VerifyEmailInput
|
||||||
asMap := map[string]any{}
|
asMap := map[string]any{}
|
||||||
@@ -17804,6 +18047,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||||
return ec._Mutation_regenerateSCIMToken(ctx, field)
|
return ec._Mutation_regenerateSCIMToken(ctx, field)
|
||||||
})
|
})
|
||||||
|
case "updateSCIMBridge":
|
||||||
|
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||||
|
return ec._Mutation_updateSCIMBridge(ctx, field)
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@@ -19350,6 +19597,11 @@ func (ec *executionContext) _SCIMBridge(ctx context.Context, sel ast.SelectionSe
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
atomic.AddUint32(&out.Invalids, 1)
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
|
case "excludedUserNames":
|
||||||
|
out.Values[i] = ec._SCIMBridge_excludedUserNames(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
|
}
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
out.Values[i] = ec._SCIMBridge_createdAt(ctx, field, obj)
|
out.Values[i] = ec._SCIMBridge_createdAt(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
@@ -20413,6 +20665,45 @@ func (ec *executionContext) _UpdateSAMLConfigurationPayload(ctx context.Context,
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var updateSCIMBridgePayloadImplementors = []string{"UpdateSCIMBridgePayload"}
|
||||||
|
|
||||||
|
func (ec *executionContext) _UpdateSCIMBridgePayload(ctx context.Context, sel ast.SelectionSet, obj *types.UpdateSCIMBridgePayload) graphql.Marshaler {
|
||||||
|
fields := graphql.CollectFields(ec.OperationContext, sel, updateSCIMBridgePayloadImplementors)
|
||||||
|
|
||||||
|
out := graphql.NewFieldSet(fields)
|
||||||
|
deferred := make(map[string]*graphql.FieldSet)
|
||||||
|
for i, field := range fields {
|
||||||
|
switch field.Name {
|
||||||
|
case "__typename":
|
||||||
|
out.Values[i] = graphql.MarshalString("UpdateSCIMBridgePayload")
|
||||||
|
case "scimBridge":
|
||||||
|
out.Values[i] = ec._UpdateSCIMBridgePayload_scimBridge(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
out.Invalids++
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.Dispatch(ctx)
|
||||||
|
if out.Invalids > 0 {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
|
||||||
|
|
||||||
|
for label, dfs := range deferred {
|
||||||
|
ec.processDeferredGroup(graphql.DeferredGroup{
|
||||||
|
Label: label,
|
||||||
|
Path: graphql.GetPath(ctx),
|
||||||
|
FieldSet: dfs,
|
||||||
|
Context: ctx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
var verifyEmailPayloadImplementors = []string{"VerifyEmailPayload"}
|
var verifyEmailPayloadImplementors = []string{"VerifyEmailPayload"}
|
||||||
|
|
||||||
func (ec *executionContext) _VerifyEmailPayload(ctx context.Context, sel ast.SelectionSet, obj *types.VerifyEmailPayload) graphql.Marshaler {
|
func (ec *executionContext) _VerifyEmailPayload(ctx context.Context, sel ast.SelectionSet, obj *types.VerifyEmailPayload) graphql.Marshaler {
|
||||||
@@ -21575,6 +21866,16 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNSCIMBridge2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSCIMBridge(ctx context.Context, sel ast.SelectionSet, v *types.SCIMBridge) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||||
|
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return ec._SCIMBridge(ctx, sel, v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNSCIMBridgeState2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSCIMBridgeState(ctx context.Context, v any) (coredata.SCIMBridgeState, error) {
|
func (ec *executionContext) unmarshalNSCIMBridgeState2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSCIMBridgeState(ctx context.Context, v any) (coredata.SCIMBridgeState, error) {
|
||||||
tmp, err := graphql.UnmarshalString(v)
|
tmp, err := graphql.UnmarshalString(v)
|
||||||
res := unmarshalNSCIMBridgeState2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSCIMBridgeState[tmp]
|
res := unmarshalNSCIMBridgeState2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐSCIMBridgeState[tmp]
|
||||||
@@ -21886,6 +22187,36 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNString2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) {
|
||||||
|
var vSlice []any
|
||||||
|
vSlice = graphql.CoerceList(v)
|
||||||
|
var err error
|
||||||
|
res := make([]string, len(vSlice))
|
||||||
|
for i := range vSlice {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||||
|
res[i], err = ec.unmarshalNString2string(ctx, vSlice[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler {
|
||||||
|
ret := make(graphql.Array, len(v))
|
||||||
|
for i := range v {
|
||||||
|
ret[i] = ec.marshalNString2string(ctx, sel, v[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range ret {
|
||||||
|
if e == graphql.Null {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNUpdateMembershipInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐUpdateMembershipInput(ctx context.Context, v any) (types.UpdateMembershipInput, error) {
|
func (ec *executionContext) unmarshalNUpdateMembershipInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐUpdateMembershipInput(ctx context.Context, v any) (types.UpdateMembershipInput, error) {
|
||||||
res, err := ec.unmarshalInputUpdateMembershipInput(ctx, v)
|
res, err := ec.unmarshalInputUpdateMembershipInput(ctx, v)
|
||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
@@ -21915,6 +22246,11 @@ func (ec *executionContext) unmarshalNUpdateSAMLConfigurationInput2goᚗproboᚗ
|
|||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNUpdateSCIMBridgeInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐUpdateSCIMBridgeInput(ctx context.Context, v any) (types.UpdateSCIMBridgeInput, error) {
|
||||||
|
res, err := ec.unmarshalInputUpdateSCIMBridgeInput(ctx, v)
|
||||||
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNVerifyEmailInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐVerifyEmailInput(ctx context.Context, v any) (types.VerifyEmailInput, error) {
|
func (ec *executionContext) unmarshalNVerifyEmailInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐVerifyEmailInput(ctx context.Context, v any) (types.VerifyEmailInput, error) {
|
||||||
res, err := ec.unmarshalInputVerifyEmailInput(ctx, v)
|
res, err := ec.unmarshalInputVerifyEmailInput(ctx, v)
|
||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
@@ -22695,6 +23031,13 @@ func (ec *executionContext) marshalOUpdateSAMLConfigurationPayload2ᚖgoᚗprobo
|
|||||||
return ec._UpdateSAMLConfigurationPayload(ctx, sel, v)
|
return ec._UpdateSAMLConfigurationPayload(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalOUpdateSCIMBridgePayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐUpdateSCIMBridgePayload(ctx context.Context, sel ast.SelectionSet, v *types.UpdateSCIMBridgePayload) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return ec._UpdateSCIMBridgePayload(ctx, sel, v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalOUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, v any) (*graphql.Upload, error) {
|
func (ec *executionContext) unmarshalOUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, v any) (*graphql.Upload, error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ func NewSCIMBridge(bridge *coredata.SCIMBridge) *SCIMBridge {
|
|||||||
ScimConfiguration: &SCIMConfiguration{
|
ScimConfiguration: &SCIMConfiguration{
|
||||||
ID: bridge.ScimConfigurationID,
|
ID: bridge.ScimConfigurationID,
|
||||||
},
|
},
|
||||||
Connector: connector,
|
Connector: connector,
|
||||||
Type: bridge.Type,
|
Type: bridge.Type,
|
||||||
CreatedAt: bridge.CreatedAt,
|
ExcludedUserNames: bridge.ExcludedUserNames,
|
||||||
UpdatedAt: bridge.UpdatedAt,
|
CreatedAt: bridge.CreatedAt,
|
||||||
|
UpdatedAt: bridge.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -415,6 +415,7 @@ type SCIMBridge struct {
|
|||||||
ScimConfiguration *SCIMConfiguration `json:"scimConfiguration,omitempty"`
|
ScimConfiguration *SCIMConfiguration `json:"scimConfiguration,omitempty"`
|
||||||
Connector *Connector `json:"connector,omitempty"`
|
Connector *Connector `json:"connector,omitempty"`
|
||||||
Type coredata.SCIMBridgeType `json:"type"`
|
Type coredata.SCIMBridgeType `json:"type"`
|
||||||
|
ExcludedUserNames []string `json:"excludedUserNames"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
Permission bool `json:"permission"`
|
Permission bool `json:"permission"`
|
||||||
@@ -557,6 +558,16 @@ type UpdateSAMLConfigurationPayload struct {
|
|||||||
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration,omitempty"`
|
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateSCIMBridgeInput struct {
|
||||||
|
OrganizationID gid.GID `json:"organizationId"`
|
||||||
|
ScimBridgeID gid.GID `json:"scimBridgeId"`
|
||||||
|
ExcludedUserNames []string `json:"excludedUserNames"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateSCIMBridgePayload struct {
|
||||||
|
ScimBridge *SCIMBridge `json:"scimBridge"`
|
||||||
|
}
|
||||||
|
|
||||||
type VerifyEmailInput struct {
|
type VerifyEmailInput struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1177,6 +1177,23 @@ func (r *mutationResolver) RegenerateSCIMToken(ctx context.Context, input types.
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateSCIMBridge is the resolver for the updateSCIMBridge field.
|
||||||
|
func (r *mutationResolver) UpdateSCIMBridge(ctx context.Context, input types.UpdateSCIMBridgeInput) (*types.UpdateSCIMBridgePayload, error) {
|
||||||
|
if err := r.authorize(ctx, input.ScimBridgeID, iam.ActionSCIMBridgeUpdate); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge, err := r.iam.OrganizationService.UpdateSCIMBridge(ctx, input.OrganizationID, input.ScimBridgeID, input.ExcludedUserNames)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot update scim bridge excluded user names", log.Error(err))
|
||||||
|
return nil, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.UpdateSCIMBridgePayload{
|
||||||
|
ScimBridge: types.NewSCIMBridge(bridge),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// LogoURL is the resolver for the logoUrl field.
|
// LogoURL is the resolver for the logoUrl field.
|
||||||
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
|
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
|
||||||
if err := r.authorize(ctx, obj.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
|
if err := r.authorize(ctx, obj.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user