Skip to content

Commit

Permalink
Merge pull request KelvinTegelaar#2495 from JohnDuprey/dev
Browse files Browse the repository at this point in the history
JIT Admin
  • Loading branch information
JohnDuprey committed May 30, 2024
2 parents 40d54d6 + 04a4111 commit 6499786
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/_nav.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions src/importsMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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')),
Expand Down
6 changes: 6 additions & 0 deletions src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions src/views/cipp/app-settings/CIPPSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function CIPPSettings() {
Maintenance
</CNavItem>
{superAdmin && (
<CNavItem active={active === 10} onClick={() => setActive(10)} href="#">
<CNavItem active={active === 8} onClick={() => setActive(8)} href="#">
SuperAdmin Settings
</CNavItem>
)}
Expand Down Expand Up @@ -98,8 +98,8 @@ export default function CIPPSettings() {
<SettingsMaintenance />
</CippLazy>
</CTabPane>
<CTabPane visible={active === 10} className="mt-3">
<CippLazy visible={active === 10}>
<CTabPane visible={active === 8} className="mt-3">
<CippLazy visible={active === 8}>
<SettingsSuperAdmin />
</CippLazy>
</CTabPane>
Expand Down
35 changes: 34 additions & 1 deletion src/views/cipp/app-settings/components/SettingsCustomRoles.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [],
Expand All @@ -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({
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -255,6 +277,12 @@ const SettingsCustomRoles = () => {
/>
<WhenFieldChanges field="RoleName" set="Permissions" />
<WhenFieldChanges field="RoleName" set="AllowedTenants" />
{cippApiRoleSelected && (
<CCallout color="info">
This role will limit access for the CIPP-API integration. It is not
intended to be used for users.
</CCallout>
)}
</div>
<div className="mb-3">
<h5>Allowed Tenants</h5>
Expand All @@ -263,8 +291,13 @@ const SettingsCustomRoles = () => {
values={selectedTenant}
AllTenants={true}
valueIsDomain={true}
onChange={(e) => setSelectedTenant(e)}
onChange={(e) => handleTenantChange(e)}
/>
{allTenantSelected && (
<CCallout color="warning">
All tenants selected, no tenant restrictions will be applied.
</CCallout>
)}
</div>
<h5>API Permissions</h5>
<CRow className="mt-4 px-2">
Expand Down
226 changes: 226 additions & 0 deletions src/views/identity/administration/DeployJITAdmin.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<CippPage title={`Add JIT Admin`} tenantSelector={false}>
<>
<CRow>
<CCol md={4}>
<CippContentCard title="Add JIT Admin" icon={faEdit}>
<Form
onSubmit={onSubmit}
mutators={{
...arrayMutators,
}}
render={({ handleSubmit, submitting, values }) => {
return (
<CForm onSubmit={handleSubmit}>
<p>
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.
</p>
<CRow className="mb-3">
<CCol>
<label className="mb-2">Tenant</label>
<Field name="tenantFilter">{(props) => <TenantSelector />}</Field>
</CCol>
</CRow>
<CRow>
<hr />
</CRow>
<CRow className="mb-3">
<CCol>
<RFFCFormRadioList
name="useraction"
options={[
{ value: 'create', label: 'New User' },
{ value: 'select', label: 'Existing User' },
]}
validate={false}
inline={true}
className=""
/>
</CCol>
</CRow>
<Condition when="useraction" is="create">
<CRow className="mb-3">
<CCol>
<RFFCFormInput label="User Principal Name" name="UserPrincipalName" />
</CCol>
</CRow>
</Condition>
<Condition when="useraction" is="select">
<CRow className="mb-3">
<CCol>
<RFFSelectSearch
label={'Users in ' + tenantDomain}
values={users?.map((user) => ({
value: user.id,
name: `${user.displayName} <${user.userPrincipalName}>`,
}))}
placeholder={!usersIsFetching ? 'Select user' : 'Loading...'}
name="UserId"
isLoading={usersIsFetching}
/>
</CCol>
</CRow>
</Condition>
<CRow className="mb-3">
<CCol>
<RFFSelectSearch
label="Administrative Roles"
values={GDAPRoles?.map((role) => ({
value: role.ObjectId,
name: role.Name,
}))}
multi={true}
placeholder="Select Roles"
name="AdminRoles"
/>
</CCol>
</CRow>
<CRow className="mb-3">
<CCol>
<label>Scheduled Start Date</label>
<DatePicker
className="form-control"
selected={startDate}
showTimeSelect
timeFormat="HH:mm"
timeIntervals={15}
dateFormat="Pp"
onChange={(date) => setStartDate(date)}
/>
</CCol>
<CCol>
<label>Scheduled End Date</label>
<DatePicker
className="form-control"
selected={endDate}
showTimeSelect
timeFormat="HH:mm"
timeIntervals={15}
dateFormat="Pp"
onChange={(date) => setEndDate(date)}
/>
</CCol>
</CRow>
<CRow className="mb-3">
<CCol>
<RFFSelectSearch
label="Expiration Action"
values={[
{ value: 'removeroles', name: 'Remove Admin Roles' },
{ value: 'disable', name: 'Disable' },
{ value: 'delete', name: 'Delete' },
]}
placeholder="Select action for when JIT expires"
name="expireAction"
/>
</CCol>
</CRow>
<CRow className="mb-3">
<CCol md={6}>
<CButton type="submit" disabled={submitting}>
Add JIT Admin
{postResults.isFetching && (
<FontAwesomeIcon
icon={faCircleNotch}
spin
className="ms-2"
size="1x"
/>
)}
</CButton>
</CCol>
</CRow>
{postResults.isSuccess && (
<CCallout color="success">
<li>{postResults.data.Results}</li>
</CCallout>
)}
{getResults.isFetching && (
<CCallout color="info">
<CSpinner>Loading</CSpinner>
</CCallout>
)}
{getResults.isSuccess && (
<CCallout color="info">{getResults.data?.Results}</CCallout>
)}
{getResults.isError && (
<CCallout color="danger">
Could not connect to API: {getResults.error.message}
</CCallout>
)}
</CForm>
)
}}
/>
</CippContentCard>
</CCol>
</CRow>
</>
</CippPage>
)
}

export default DeployJITAdmin

0 comments on commit 6499786

Please sign in to comment.