diff --git a/src/components/CippComponents/CippApiDialog.jsx b/src/components/CippComponents/CippApiDialog.jsx
index bd6348868c8b..99c5bcc455c9 100644
--- a/src/components/CippComponents/CippApiDialog.jsx
+++ b/src/components/CippComponents/CippApiDialog.jsx
@@ -207,6 +207,40 @@ export const CippApiDialog = (props) => {
const onSubmit = (data) => handleActionClick(row, api, data);
const selectedType = api.type === "POST" ? actionPostRequest : actionGetRequest;
+ if (api?.setDefaultValues) {
+ fields.map((field) => {
+ if (
+ ((typeof row[field.name] === "string" && field.type === "textField") ||
+ (typeof row[field.name] === "boolean" && field.type === "switch")) &&
+ row[field.name] !== undefined &&
+ row[field.name] !== null &&
+ row[field.name] !== ""
+ ) {
+ formHook.setValue(field.name, row[field.name]);
+ } else if (Array.isArray(row[field.name]) && field.type === "autoComplete") {
+ var values = [];
+ row[field.name].map((element) => {
+ values.push({
+ label: element,
+ value: element,
+ });
+ });
+ formHook.setValue(field.name, values);
+ } else if (
+ field.type === "autoComplete" &&
+ row[field.name] !== undefined &&
+ row[field.name] !== null &&
+ row[field.name] !== "" &&
+ typeof row[field.name] === "string"
+ ) {
+ formHook.setValue(field.name, {
+ label: row[field.name],
+ value: row[field.name],
+ });
+ }
+ });
+ }
+
// Handling link navigation
if (api.link) {
const getNestedValue = (obj, path) => {
diff --git a/src/components/CippComponents/CippTranslations.jsx b/src/components/CippComponents/CippTranslations.jsx
index 0876bded5bd2..7761362d09ed 100644
--- a/src/components/CippComponents/CippTranslations.jsx
+++ b/src/components/CippComponents/CippTranslations.jsx
@@ -44,4 +44,5 @@ export const CippTranslations = {
"commitmentTerm.renewalConfiguration.renewalDate": "Renewal Date",
storageUsedInBytes: "Storage Used",
prohibitSendReceiveQuotaInBytes: "Quota",
+ ClientId: "Client ID",
};
diff --git a/src/components/CippIntegrations/CippApiClientManagement.jsx b/src/components/CippIntegrations/CippApiClientManagement.jsx
new file mode 100644
index 000000000000..01e174c64e46
--- /dev/null
+++ b/src/components/CippIntegrations/CippApiClientManagement.jsx
@@ -0,0 +1,354 @@
+import { Button, Stack, SvgIcon, Menu, MenuItem, ListItemText } from "@mui/material";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall";
+import { CippDataTable } from "../CippTable/CippDataTable";
+import {
+ ChevronDownIcon,
+ ClipboardDocumentIcon,
+ PencilIcon,
+ PlusSmallIcon,
+ TrashIcon,
+} from "@heroicons/react/24/outline";
+import { CippApiResults } from "../CippComponents/CippApiResults";
+import { CippApiDialog } from "../CippComponents/CippApiDialog";
+import { Create, Key, Save, Sync } from "@mui/icons-material";
+import { CippPropertyListCard } from "../CippCards/CippPropertyListCard";
+import { CippCopyToClipBoard } from "../CippComponents/CippCopyToClipboard";
+
+const CippApiClientManagement = () => {
+ const [openAddClientDialog, setOpenAddClientDialog] = useState(false);
+ const [openAddExistingAppDialog, setOpenAddExistingAppDialog] = useState(false);
+ const [menuAnchorEl, setMenuAnchorEl] = useState(null);
+
+ const formControl = useForm({
+ mode: "onChange",
+ });
+
+ const postCall = ApiPostCall({
+ datafromUrl: true,
+ relatedQueryKeys: ["ApiClients", "AzureConfiguration"],
+ });
+
+ const azureConfig = ApiGetCall({
+ url: "/api/ExecApiClient",
+ data: { Action: "GetAzureConfiguration" },
+ queryKey: "AzureConfiguration",
+ });
+
+ const handleMenuOpen = (event) => {
+ setMenuAnchorEl(event.currentTarget);
+ };
+
+ const handleMenuClose = () => {
+ setMenuAnchorEl(null);
+ };
+
+ const handleSaveToAzure = () => {
+ postCall.mutate({
+ url: `/api/ExecApiClient?action=SaveToAzure`,
+ data: {},
+ });
+ handleMenuClose();
+ };
+
+ const actions = [
+ {
+ label: "Edit",
+ icon: (
+
+
+
+ ),
+ confirmText: "Update the API client settings:",
+ hideBulk: true,
+ setDefaultValues: true,
+ fields: [
+ {
+ type: "autoComplete",
+ name: "Role",
+ multiple: false,
+ creatable: false,
+ placeholder: "Select Role",
+ api: {
+ url: "/api/ListCustomRole",
+ queryKey: "CustomRoleList",
+ labelField: "RowKey",
+ valueField: "RowKey",
+ },
+ },
+ {
+ type: "autoComplete",
+ name: "IpRange",
+ multiple: true,
+ freeSolo: true,
+ creatable: true,
+ options: [],
+ placeholder: "Enter IP Range (Single hosts or CIDR notation)",
+ },
+ {
+ type: "switch",
+ name: "Enabled",
+ label: "Enable this client",
+ },
+ ],
+ type: "POST",
+ url: "/api/ExecApiClient",
+ data: {
+ Action: "AddUpdate",
+ ClientId: "ClientId",
+ },
+ relatedQueryKeys: ["ApiClients"],
+ },
+ {
+ label: "Reset Application Secret",
+ icon: ,
+ confirmText: "Are you sure you want to reset the application secret?",
+ type: "POST",
+ url: "/api/ExecApiClient",
+ data: {
+ Action: "ResetSecret",
+ ClientId: "ClientId",
+ },
+ },
+ {
+ label: "Copy API Scope",
+ icon: ,
+ noConfirm: true,
+ customFunction: (row, action, formData) => {
+ var scope = `api://${row.ClientId}/.default`;
+ navigator.clipboard.writeText(scope);
+ },
+ },
+ {
+ label: "Delete Client",
+ icon: ,
+ confirmText: "Are you sure you want to delete this client?",
+ type: "POST",
+ url: "/api/ExecApiClient",
+ data: {
+ Action: "Delete",
+ ClientId: "ClientId",
+ },
+ fields: [
+ {
+ type: "switch",
+ name: "RemoveAppReg",
+ label: "Remove App Registration",
+ },
+ ],
+ relatedQueryKeys: ["ApiClients"],
+ },
+ ];
+
+ return (
+ <>
+
+
+
+
+ >
+ }
+ propertyItems={[
+ { label: "API Auth Enabled", value: azureConfig.data?.Results?.Enabled },
+ {
+ label: "API Url",
+ value: azureConfig.data?.Results?.ApiUrl ? (
+
+ ) : (
+ "Not Available"
+ ),
+ },
+ {
+ label: "Token URL",
+ value: azureConfig.data?.Results?.TenantID ? (
+
+ ) : (
+ "Not Available"
+ ),
+ },
+ ]}
+ layout="dual"
+ showDivider={false}
+ isFetching={azureConfig.isFetching}
+ />
+
+
+
+
+
+ setOpenAddClientDialog(false),
+ }}
+ title="Add Client"
+ fields={[
+ {
+ type: "textField",
+ name: "AppName",
+ placeholder: "Enter App Name",
+ },
+ {
+ type: "autoComplete",
+ name: "Role",
+ multiple: false,
+ creatable: false,
+ placeholder: "Select Role",
+ api: {
+ url: "/api/ListCustomRole",
+ queryKey: "CustomRoleList",
+ labelField: "RowKey",
+ valueField: "RowKey",
+ },
+ },
+ {
+ type: "autoComplete",
+ name: "IpRange",
+ multiple: true,
+ freeSolo: true,
+ creatable: true,
+ options: [],
+ placeholder: "Enter IP Range (Single hosts or CIDR notation)",
+ },
+ {
+ type: "switch",
+ name: "Enabled",
+ label: "Enable this client",
+ },
+ ]}
+ api={{
+ type: "POST",
+ url: "/api/ExecApiClient",
+ data: { Action: "AddUpdate" },
+ relatedQueryKeys: [`ApiClients`],
+ }}
+ />
+ setOpenAddExistingAppDialog(false),
+ }}
+ title="Add Existing App"
+ fields={[
+ {
+ type: "autoComplete",
+ name: "ClientId",
+ placeholder: "Select Existing App",
+ api: {
+ type: "GET",
+ url: "/api/ExecApiClient",
+ data: { Action: "ListAvailable" },
+ queryKey: `AvailableApiApps`,
+ dataKey: "Results",
+ labelField: (app) => `${app.displayName} (${app.appId})`,
+ valueField: "appId",
+ addedField: {
+ displayName: "displayName",
+ createdDateTime: "createdDateTime",
+ },
+ },
+ creatable: false,
+ multiple: false,
+ },
+ {
+ type: "autoComplete",
+ name: "Role",
+ multiple: false,
+ creatable: false,
+ placeholder: "Select Role",
+ api: {
+ url: "/api/ListCustomRole",
+ queryKey: "CustomRoleList",
+ labelField: "RowKey",
+ valueField: "RowKey",
+ },
+ },
+ {
+ type: "autoComplete",
+ name: "IpRange",
+ multiple: true,
+ freeSolo: true,
+ creatable: true,
+ options: [],
+ placeholder: "Enter IP Range(s)",
+ },
+ {
+ type: "switch",
+ name: "Enabled",
+ label: "Enable this client",
+ },
+ ]}
+ api={{
+ type: "POST",
+ url: "/api/ExecApiClient",
+ data: { Action: "!AddUpdate" },
+ relatedQueryKeys: [`ApiClients`],
+ }}
+ />
+ >
+ );
+};
+
+export default CippApiClientManagement;
diff --git a/src/data/Extensions.json b/src/data/Extensions.json
index 0110e7d63729..e81d25cf3468 100644
--- a/src/data/Extensions.json
+++ b/src/data/Extensions.json
@@ -7,19 +7,12 @@
"logo": "/assets/integrations/cipp-api.png",
"forceSyncButton": false,
"hideTestButton": true,
- "disableWhenhosted": true,
- "description": "Enable the CIPP API to access CIPP data outside of CIPP using a RESTful API.",
- "helpText": "This integration allows you to enable CIPP-API access outside of CIPP. Requires Global Administrator permissions inside your tenant for activation of the API. The API credentials will only be shown once.",
- "SettingOptions": [
- {
- "type": "switch",
- "name": "cippapi.ResetPassword",
- "label": "Reset application secret - this will invalidate all existing API tokens"
- },
+ "description": "The CIPP API allows you to access CIPP data outside of CIPP using a RESTful API.",
+ "helpText": "This integration allows you to enable CIPP-API access outside of CIPP. This is useful for integrations with other systems or custom scripts.",
+ "links": [
{
- "type": "switch",
- "name": "cippapi.Enabled",
- "label": "Enable Integration"
+ "name": "CIPP API Documentation",
+ "url": "https://docs.cipp.app/api-documentation/setup-and-authentication"
}
],
"mappingRequired": false
@@ -76,7 +69,7 @@
"name": "Sherweb.AllowedCustomRoles",
"label": "Select custom roles that are allowed to purchase licenses",
"api": {
- "url": "/api/ExecCustomRole",
+ "url": "/api/ListCustomRole",
"queryKey": "CustomRoles",
"labelField": "RowKey",
"valueField": "RowKey"
@@ -463,5 +456,36 @@
}
],
"mappingRequired": false
+ },
+ {
+ "name": "GitHub",
+ "id": "GitHub",
+ "type": "DevOps",
+ "cat": "Source Control",
+ "logo": "/assets/integrations/github.png",
+ "logoDark": "/assets/integrations/github_dark.png",
+ "description": "Enable the GitHub integration to manage your repositories from CIPP.",
+ "helpText": "This integration allows you to manage your GitHub repositories from CIPP. Requires a GitHub Fine Grained Personal Access Token (PAT).",
+ "links": [
+ {
+ "name": "GitHub Token",
+ "url": "https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token"
+ }
+ ],
+ "SettingOptions": [
+ {
+ "type": "password",
+ "name": "GitHub.APIKey",
+ "label": "GitHub Fine Grained Personal Access Token",
+ "placeholder": "Enter your GitHub PAT",
+ "required": true
+ },
+ {
+ "type": "switch",
+ "name": "GitHub.Enabled",
+ "label": "Enable Integration"
+ }
+ ],
+ "mappingRequired": false
}
]
diff --git a/src/pages/cipp/integrations/configure.js b/src/pages/cipp/integrations/configure.js
index e45bf0fd4489..1995566ddd38 100644
--- a/src/pages/cipp/integrations/configure.js
+++ b/src/pages/cipp/integrations/configure.js
@@ -15,6 +15,7 @@ import CippPageCard from "../../../components/CippCards/CippPageCard";
import CippIntegrationTenantMapping from "../../../components/CippIntegrations/CippIntegrationTenantMapping";
import CippIntegrationFieldMapping from "../../../components/CippIntegrations/CippIntegrationFieldMapping";
import { CippCardTabPanel } from "../../../components/CippComponents/CippCardTabPanel";
+import CippApiClientManagement from "../../../components/CippIntegrations/CippApiClientManagement";
function tabProps(index) {
return {
@@ -77,7 +78,7 @@ const Page = () => {
defaultValues: integrations?.data,
});
- const extension = extensions.find((extension) => extension.id === router.query.id);
+ const extension = extensions.find((extension) => extension.id === router.query.id) || {};
var logo = extension?.logo;
if (preferredTheme === "dark" && extension?.logoDark) {
@@ -185,12 +186,16 @@ const Page = () => {
{extension?.mappingRequired && }
{extension?.fieldMapping && }
- {extension?.id === "cippapi" && }
-
+ {extension?.id === "cippapi" ? (
+
+ ) : (
+
+ )}
+
{extension?.mappingRequired && (
@@ -201,11 +206,6 @@ const Page = () => {
)}
- {extension?.id === "cippapi" && (
-
- API Client component to go here
-
- )}
)}
diff --git a/src/pages/cipp/integrations/index.js b/src/pages/cipp/integrations/index.js
index 241ced5c4792..4bf582e23432 100644
--- a/src/pages/cipp/integrations/index.js
+++ b/src/pages/cipp/integrations/index.js
@@ -56,11 +56,11 @@ const Page = () => {
}
var integrationConfig = integrations?.data?.[extension.id];
- var isEnabled = integrationConfig?.Enabled;
+ var isEnabled = integrationConfig?.Enabled || extension.id === "cippapi";
var status = "Unconfigured";
if (integrationConfig && !isEnabled) {
status = "Disabled";
- } else if (integrationConfig && isEnabled) {
+ } else if ((integrationConfig && isEnabled) || extension.id === "cippapi") {
status = "Enabled";
}
diff --git a/src/utils/get-cipp-formatting.js b/src/utils/get-cipp-formatting.js
index 73c65b8dbdad..2feaacacf94d 100644
--- a/src/utils/get-cipp-formatting.js
+++ b/src/utils/get-cipp-formatting.js
@@ -219,6 +219,10 @@ export const getCippFormatting = (data, cellName, type, canReceive) => {
}
}
+ if (cellName === "ClientId") {
+ return isText ? data : ;
+ }
+
if (cellName === "excludedTenants") {
//check if data is an array.
if (Array.isArray(data)) {