Skip to content

Commit 2c4f0aa

Browse files
authored
Merge pull request #2427 from JohnDuprey/jit-admin
JIT Admin frontend
2 parents d1ae131 + 41bc439 commit 2c4f0aa

File tree

4 files changed

+226
-1
lines changed

4 files changed

+226
-1
lines changed

src/_nav.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ const _nav = [
7575
name: 'Roles',
7676
to: '/identity/administration/roles',
7777
},
78+
{
79+
component: CNavItem,
80+
name: 'JIT Admin',
81+
to: '/identity/administration/users/jit-admin',
82+
},
7883
{
7984
component: CNavItem,
8085
name: 'Offboarding Wizard',

src/importsMap.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import React from 'react'
1212
"/identity/administration/users/edit": React.lazy(() => import('./views/identity/administration/EditUser')),
1313
"/identity/administration/users/view": React.lazy(() => import('./views/identity/administration/ViewUser')),
1414
"/identity/administration/users/InviteGuest": React.lazy(() => import('./views/identity/administration/InviteGuest')),
15+
"/identity/administration/users/jit-admin": React.lazy(() => import('./views/identity/administration/DeployJITAdmin')),
1516
"/identity/administration/ViewBec": React.lazy(() => import('./views/identity/administration/ViewBEC')),
1617
"/identity/administration/users": React.lazy(() => import('./views/identity/administration/Users')),
1718
"/identity/administration/devices": React.lazy(() => import('./views/identity/administration/Devices')),
@@ -142,4 +143,4 @@ import React from 'react'
142143
"/tenant/administration/tenant-offboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOffboardingWizard')),
143144
"/tenant/administration/tenant-onboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOnboardingWizard')),
144145
}
145-
export default importsMap
146+
export default importsMap

src/routes.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@
7676
"component": "views/identity/administration/InviteGuest",
7777
"allowedRoles": ["admin", "editor", "readonly"]
7878
},
79+
{
80+
"path": "/identity/administration/users/jit-admin",
81+
"name": "JIT Admin",
82+
"component": "views/identity/administration/DeployJITAdmin",
83+
"allowedRoles": ["admin", "editor", "readonly"]
84+
},
7985
{
8086
"path": "/identity/administration/ViewBec",
8187
"name": "View BEC",
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import React, { useState } from 'react'
2+
import { CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react'
3+
import { useSelector } from 'react-redux'
4+
import { Field, Form, FormSpy } from 'react-final-form'
5+
import { Condition, RFFCFormRadio, RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms'
6+
import {
7+
useGenericGetRequestQuery,
8+
useLazyGenericGetRequestQuery,
9+
useLazyGenericPostRequestQuery,
10+
} from 'src/store/api/app'
11+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
12+
import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons'
13+
import { CippContentCard, CippPage, CippPageList } from 'src/components/layout'
14+
import { CellTip } from 'src/components/tables/CellGenericFormat'
15+
import 'react-datepicker/dist/react-datepicker.css'
16+
import { CippActionsOffcanvas, ModalService, TenantSelector } from 'src/components/utilities'
17+
import arrayMutators from 'final-form-arrays'
18+
import DatePicker from 'react-datepicker'
19+
import 'react-datepicker/dist/react-datepicker.css'
20+
import { useListUsersQuery } from 'src/store/api/users'
21+
import { useListConditionalAccessPoliciesQuery } from 'src/store/api/tenants'
22+
import GDAPRoles from 'src/data/GDAPRoles'
23+
24+
const DeployJITAdmin = () => {
25+
const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery()
26+
const currentDate = new Date()
27+
const [startDate, setStartDate] = useState(currentDate)
28+
const [endDate, setEndDate] = useState(currentDate)
29+
30+
const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName)
31+
const [refreshState, setRefreshState] = useState(false)
32+
const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery()
33+
34+
const onSubmit = (values) => {
35+
const startTime = Math.floor(startDate.getTime() / 1000)
36+
const endTime = Math.floor(endDate.getTime() / 1000)
37+
const shippedValues = {
38+
tenantFilter: tenantDomain,
39+
UserId: values.UserId?.value,
40+
PolicyId: values.PolicyId?.value,
41+
StartDate: startTime,
42+
EndDate: endTime,
43+
ExpireAction: values?.expireAction ?? 'delete',
44+
}
45+
genericPostRequest({ path: '/api/ExecJITAdmin', values: shippedValues }).then((res) => {
46+
setRefreshState(res.requestId)
47+
})
48+
}
49+
50+
const {
51+
data: users = [],
52+
isFetching: usersIsFetching,
53+
error: usersError,
54+
} = useListUsersQuery({ tenantDomain })
55+
56+
return (
57+
<CippPage title={`Add JIT Admin`} tenantSelector={false}>
58+
<>
59+
<CRow>
60+
<CCol md={4}>
61+
<CippContentCard title="Add JIT Admin" icon={faEdit}>
62+
<Form
63+
onSubmit={onSubmit}
64+
mutators={{
65+
...arrayMutators,
66+
}}
67+
render={({ handleSubmit, submitting, values }) => {
68+
return (
69+
<CForm onSubmit={handleSubmit}>
70+
<p>
71+
JIT Admin creates an account that is usable for a specific period of time.
72+
Enter a username, select admin roles, date range and expiration action.
73+
</p>
74+
<CRow className="mb-3">
75+
<CCol>
76+
<label>Tenant</label>
77+
<Field name="tenantFilter">{(props) => <TenantSelector />}</Field>
78+
</CCol>
79+
</CRow>
80+
<CRow>
81+
<hr />
82+
</CRow>
83+
<CRow className="mb-3">
84+
<CCol>
85+
<RFFCFormRadio
86+
value="create"
87+
name="useraction"
88+
label="Create User"
89+
validate={false}
90+
/>
91+
<RFFCFormRadio
92+
value="select"
93+
name="useraction"
94+
label="Select User"
95+
validate={false}
96+
/>
97+
</CCol>
98+
</CRow>
99+
<CRow className="mb-3">
100+
<CCol>
101+
102+
<RFFSelectSearch
103+
label={'Users in ' + tenantDomain}
104+
values={users?.map((user) => ({
105+
value: user.id,
106+
name: `${user.displayName} <${user.userPrincipalName}>`,
107+
}))}
108+
placeholder={!usersIsFetching ? 'Select user' : 'Loading...'}
109+
name="UserId"
110+
isLoading={usersIsFetching}
111+
/>
112+
</CCol>
113+
</CRow>
114+
<CRow className="mb-3">
115+
<CCol>
116+
<RFFSelectSearch
117+
label="Administrative Roles"
118+
values={GDAPRoles?.map((role) => ({
119+
value: role.ObjectId,
120+
name: role.Name,
121+
}))}
122+
multi={true}
123+
placeholder="Select Roles"
124+
name="AdminRoles"
125+
/>
126+
</CCol>
127+
</CRow>
128+
<CRow className="mb-3">
129+
<CCol>
130+
<label>Scheduled Start Date</label>
131+
<DatePicker
132+
className="form-control mb-3"
133+
selected={startDate}
134+
showTimeSelect
135+
timeFormat="HH:mm"
136+
timeIntervals={15}
137+
dateFormat="Pp"
138+
onChange={(date) => setStartDate(date)}
139+
/>
140+
</CCol>
141+
<CCol>
142+
<label>Scheduled End Date</label>
143+
<DatePicker
144+
className="form-control mb-3"
145+
selected={endDate}
146+
showTimeSelect
147+
timeFormat="HH:mm"
148+
timeIntervals={15}
149+
dateFormat="Pp"
150+
onChange={(date) => setEndDate(date)}
151+
/>
152+
</CCol>
153+
</CRow>
154+
<CRow className="mb-3">
155+
<CCol>
156+
<RFFSelectSearch
157+
label="Expiration Action"
158+
values={[
159+
{ value: 'removeroles', name: 'Remove Admin Roles' },
160+
{ value: 'disable', name: 'Disable' },
161+
{ value: 'delete', name: 'Delete' },
162+
]}
163+
placeholder="Select action for when JIT expires"
164+
name="expireAction"
165+
/>
166+
</CCol>
167+
</CRow>
168+
<CRow>
169+
<CCol md={6}>
170+
<CButton type="submit" disabled={submitting}>
171+
Add JIT Admin
172+
{postResults.isFetching && (
173+
<FontAwesomeIcon
174+
icon={faCircleNotch}
175+
spin
176+
className="ms-2"
177+
size="1x"
178+
/>
179+
)}
180+
</CButton>
181+
</CCol>
182+
</CRow>
183+
{postResults.isSuccess && (
184+
<CCallout color="success">
185+
<li>{postResults.data.Results}</li>
186+
</CCallout>
187+
)}
188+
{getResults.isFetching && (
189+
<CCallout color="info">
190+
<CSpinner>Loading</CSpinner>
191+
</CCallout>
192+
)}
193+
{getResults.isSuccess && (
194+
<CCallout color="info">{getResults.data?.Results}</CCallout>
195+
)}
196+
{getResults.isError && (
197+
<CCallout color="danger">
198+
Could not connect to API: {getResults.error.message}
199+
</CCallout>
200+
)}
201+
</CForm>
202+
)
203+
}}
204+
/>
205+
</CippContentCard>
206+
</CCol>
207+
</CRow>
208+
</>
209+
</CippPage>
210+
)
211+
}
212+
213+
export default DeployJITAdmin

0 commit comments

Comments
 (0)