diff --git a/src/_nav.jsx b/src/_nav.jsx index 81175e8886f0..04fa3f3866a1 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -75,6 +75,11 @@ const _nav = [ name: 'Roles', to: '/identity/administration/roles', }, + { + component: CNavItem, + name: 'JIT Admin', + to: '/identity/administration/users/jit-admin', + }, { component: CNavItem, name: 'Offboarding Wizard', diff --git a/src/importsMap.jsx b/src/importsMap.jsx index a9dd3c4843d9..d42693788afb 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -12,6 +12,7 @@ import React from 'react' "/identity/administration/users/edit": React.lazy(() => import('./views/identity/administration/EditUser')), "/identity/administration/users/view": React.lazy(() => import('./views/identity/administration/ViewUser')), "/identity/administration/users/InviteGuest": React.lazy(() => import('./views/identity/administration/InviteGuest')), + "/identity/administration/users/jit-admin": React.lazy(() => import('./views/identity/administration/DeployJITAdmin')), "/identity/administration/ViewBec": React.lazy(() => import('./views/identity/administration/ViewBEC')), "/identity/administration/users": React.lazy(() => import('./views/identity/administration/Users')), "/identity/administration/devices": React.lazy(() => import('./views/identity/administration/Devices')), diff --git a/src/routes.json b/src/routes.json index 26c0ed1b027f..61c736f9c23d 100644 --- a/src/routes.json +++ b/src/routes.json @@ -76,6 +76,12 @@ "component": "views/identity/administration/InviteGuest", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/identity/administration/users/jit-admin", + "name": "JIT Admin", + "component": "views/identity/administration/DeployJITAdmin", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/identity/administration/ViewBec", "name": "View BEC", diff --git a/src/views/cipp/app-settings/CIPPSettings.jsx b/src/views/cipp/app-settings/CIPPSettings.jsx index c7e7a227f75e..14e5c3e0cb9e 100644 --- a/src/views/cipp/app-settings/CIPPSettings.jsx +++ b/src/views/cipp/app-settings/CIPPSettings.jsx @@ -59,7 +59,7 @@ export default function CIPPSettings() { Maintenance {superAdmin && ( - setActive(10)} href="#"> + setActive(8)} href="#"> SuperAdmin Settings )} @@ -98,8 +98,8 @@ export default function CIPPSettings() { - - + + diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index d4b8d240a668..75c0a7504487 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -30,6 +30,8 @@ const SettingsCustomRoles = () => { const { data: tenants = [], tenantsFetching } = useListTenantsQuery({ showAllTenantSelector: true, }) + const [allTenantSelected, setAllTenantSelected] = useState(false) + const [cippApiRoleSelected, setCippApiRoleSelected] = useState(false) const { data: apiPermissions = [], @@ -48,6 +50,20 @@ const SettingsCustomRoles = () => { path: 'api/ExecCustomRole', }) + const handleTenantChange = (e) => { + var alltenant = false + e.map((tenant) => { + if (tenant.value === 'AllTenants') { + alltenant = true + } + }) + if (alltenant) { + setAllTenantSelected(true) + } else { + setAllTenantSelected(false) + } + setSelectedTenant(e) + } const handleSubmit = async (values) => { //filter on only objects that are 'true' genericPostRequest({ @@ -92,6 +108,12 @@ const SettingsCustomRoles = () => { let customRole = customRoleList.filter(function (obj) { return obj.RowKey === value.value }) + if (customRole[0]?.RowKey === 'CIPP-API') { + setCippApiRoleSelected(true) + } else { + setCippApiRoleSelected(false) + } + if (customRole === undefined || customRole === null || customRole.length === 0) { return false } else { @@ -255,6 +277,12 @@ const SettingsCustomRoles = () => { /> + {cippApiRoleSelected && ( + + This role will limit access for the CIPP-API integration. It is not + intended to be used for users. + + )}
Allowed Tenants
@@ -263,8 +291,13 @@ const SettingsCustomRoles = () => { values={selectedTenant} AllTenants={true} valueIsDomain={true} - onChange={(e) => setSelectedTenant(e)} + onChange={(e) => handleTenantChange(e)} /> + {allTenantSelected && ( + + All tenants selected, no tenant restrictions will be applied. + + )}
API Permissions
diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx new file mode 100644 index 000000000000..126150f95360 --- /dev/null +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -0,0 +1,226 @@ +import React, { useState } from 'react' +import { CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' +import { useSelector } from 'react-redux' +import { Field, Form, FormSpy } from 'react-final-form' +import { + Condition, + RFFCFormInput, + RFFCFormRadio, + RFFCFormRadioList, + RFFCFormSwitch, + RFFSelectSearch, +} from 'src/components/forms' +import { + useGenericGetRequestQuery, + useLazyGenericGetRequestQuery, + useLazyGenericPostRequestQuery, +} from 'src/store/api/app' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' +import { CippContentCard, CippPage, CippPageList } from 'src/components/layout' +import { CellTip } from 'src/components/tables/CellGenericFormat' +import 'react-datepicker/dist/react-datepicker.css' +import { CippActionsOffcanvas, ModalService, TenantSelector } from 'src/components/utilities' +import arrayMutators from 'final-form-arrays' +import DatePicker from 'react-datepicker' +import 'react-datepicker/dist/react-datepicker.css' +import { useListUsersQuery } from 'src/store/api/users' +import { useListConditionalAccessPoliciesQuery } from 'src/store/api/tenants' +import GDAPRoles from 'src/data/GDAPRoles' + +const DeployJITAdmin = () => { + const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery() + const currentDate = new Date() + const [startDate, setStartDate] = useState(currentDate) + const [endDate, setEndDate] = useState(currentDate) + + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const [refreshState, setRefreshState] = useState(false) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + + const onSubmit = (values) => { + const startTime = Math.floor(startDate.getTime() / 1000) + const endTime = Math.floor(endDate.getTime() / 1000) + const shippedValues = { + tenantFilter: tenantDomain, + UserId: values.UserId?.value, + PolicyId: values.PolicyId?.value, + StartDate: startTime, + EndDate: endTime, + ExpireAction: values?.expireAction ?? 'delete', + } + genericPostRequest({ path: '/api/ExecJITAdmin', values: shippedValues }).then((res) => { + setRefreshState(res.requestId) + }) + } + + const { + data: users = [], + isFetching: usersIsFetching, + error: usersError, + } = useListUsersQuery({ tenantDomain }) + + return ( + + <> + + + +
{ + return ( + +

+ JIT Admin creates an account that is usable for a specific period of time. + Enter a username, select admin roles, date range and expiration action. +

+ + + + {(props) => } + + + +
+
+ + + + + + + + + + + + + + + + ({ + value: user.id, + name: `${user.displayName} <${user.userPrincipalName}>`, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="UserId" + isLoading={usersIsFetching} + /> + + + + + + ({ + value: role.ObjectId, + name: role.Name, + }))} + multi={true} + placeholder="Select Roles" + name="AdminRoles" + /> + + + + + + setStartDate(date)} + /> + + + + setEndDate(date)} + /> + + + + + + + + + + + Add JIT Admin + {postResults.isFetching && ( + + )} + + + + {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} + {getResults.isFetching && ( + + Loading + + )} + {getResults.isSuccess && ( + {getResults.data?.Results} + )} + {getResults.isError && ( + + Could not connect to API: {getResults.error.message} + + )} +
    + ) + }} + /> + + + + + + ) +} + +export default DeployJITAdmin