1
- import { Spinner , SpinnerSize , Stack } from '@fluentui/react' ;
1
+ import { FontIcon , Icon , Label , Spinner , SpinnerSize , Stack , getTheme , mergeStyles } from '@fluentui/react' ;
2
2
import React , { useContext , useEffect , useRef , useState } from 'react' ;
3
3
import { Route , Routes , useParams } from 'react-router-dom' ;
4
4
import { ApiEndpoint } from '../../models/apiEndpoints' ;
@@ -18,54 +18,80 @@ import { Airlock } from '../shared/airlock/Airlock';
18
18
import { APIError } from '../../models/exceptions' ;
19
19
import { LoadingState } from '../../models/loadingState' ;
20
20
import { ExceptionLayout } from '../shared/ExceptionLayout' ;
21
+ import { AppRolesContext } from '../../contexts/AppRolesContext' ;
22
+ import { RoleName } from '../../models/roleNames' ;
21
23
22
24
export const WorkspaceProvider : React . FunctionComponent = ( ) => {
23
25
const apiCall = useAuthApiCall ( ) ;
24
26
const [ selectedWorkspaceService , setSelectedWorkspaceService ] = useState ( { } as WorkspaceService ) ;
25
- const [ workspaceServices , setWorkspaceServices ] = useState ( [ ] as Array < WorkspaceService > )
26
- const [ sharedServices , setSharedServices ] = useState ( [ ] as Array < SharedService > )
27
+ const [ workspaceServices , setWorkspaceServices ] = useState ( [ ] as Array < WorkspaceService > ) ;
28
+ const [ sharedServices , setSharedServices ] = useState ( [ ] as Array < SharedService > ) ;
27
29
const workspaceCtx = useRef ( useContext ( WorkspaceContext ) ) ;
28
30
const [ loadingState , setLoadingState ] = useState ( LoadingState . Loading ) ;
29
- const [ apiError , setApiError ] = useState ( { } as APIError ) ;
31
+ const [ apiError , setApiError ] = useState ( { } as APIError ) ;
30
32
const { workspaceId } = useParams ( ) ;
31
33
34
+ const appRoles = useContext ( AppRolesContext ) ;
35
+ const refIsTREAdminUser = useRef ( false ) ;
32
36
33
37
// set workspace context from url
34
38
useEffect ( ( ) => {
35
39
const getWorkspace = async ( ) => {
36
40
try {
37
41
// get the workspace - first we get the scope_id so we can auth against the right aad app
38
42
let scopeId = ( await apiCall ( `${ ApiEndpoint . Workspaces } /${ workspaceId } /scopeid` , HttpMethod . Get ) ) . workspaceAuth . scopeId ;
39
- if ( scopeId === "" ) {
40
- console . error ( "Unable to get scope_id from workspace - authentication not set up." ) ;
41
- }
42
43
43
- const ws = ( await apiCall ( `${ ApiEndpoint . Workspaces } /${ workspaceId } ` , HttpMethod . Get , scopeId ) ) . workspace ;
44
- workspaceCtx . current . setWorkspace ( ws ) ;
45
- const ws_application_id_uri = ws . properties . scope_id ;
44
+ const authProvisioned = scopeId !== "" ;
46
45
47
- // use the client ID to get a token against the workspace (tokenOnly), and set the workspace roles in the context
48
46
let wsRoles : Array < string > = [ ] ;
49
- console . log ( 'Getting workspace' ) ;
50
- await apiCall ( `${ ApiEndpoint . Workspaces } /${ workspaceId } ` , HttpMethod . Get , ws_application_id_uri , undefined , ResultType . JSON , ( roles : Array < string > ) => {
51
- workspaceCtx . current . setRoles ( roles ) ;
52
- wsRoles = roles ;
53
- } , true ) ;
54
-
55
- // get workspace services to pass to nav + ws services page
56
- const workspaceServices = await apiCall ( `${ ApiEndpoint . Workspaces } /${ ws . id } /${ ApiEndpoint . WorkspaceServices } ` , HttpMethod . Get , ws_application_id_uri ) ;
57
- setWorkspaceServices ( workspaceServices . workspaceServices ) ;
58
- setLoadingState ( wsRoles && wsRoles . length > 0 ? LoadingState . Ok : LoadingState . AccessDenied ) ;
59
-
60
- // get shared services to pass to nav shared services pages
61
- const sharedServices = await apiCall ( ApiEndpoint . SharedServices , HttpMethod . Get ) ;
62
- setSharedServices ( sharedServices . sharedServices ) ;
63
-
64
- } catch ( e : any ) {
65
- e . userMessage = 'Error retrieving workspace' ;
66
- setApiError ( e ) ;
67
- setLoadingState ( LoadingState . Error ) ;
47
+ let ws : Workspace = { } as Workspace ;
48
+
49
+ if ( authProvisioned ) {
50
+ // use the client ID to get a token against the workspace (tokenOnly), and set the workspace roles in the context
51
+ await apiCall ( `${ ApiEndpoint . Workspaces } /${ workspaceId } ` , HttpMethod . Get , scopeId ,
52
+ undefined , ResultType . JSON , ( roles : Array < string > ) => {
53
+ wsRoles = roles ;
54
+ } , true ) ;
55
+ }
56
+
57
+ if ( wsRoles && wsRoles . length > 0 ) {
58
+ ws = ( await apiCall ( `${ ApiEndpoint . Workspaces } /${ workspaceId } ` , HttpMethod . Get , scopeId ) ) . workspace ;
59
+ workspaceCtx . current . setWorkspace ( ws ) ;
60
+ workspaceCtx . current . setRoles ( wsRoles ) ;
61
+
62
+ // get workspace services to pass to nav + ws services page
63
+ const workspaceServices = await apiCall ( `${ ApiEndpoint . Workspaces } /${ ws . id } /${ ApiEndpoint . WorkspaceServices } ` ,
64
+ HttpMethod . Get , ws . properties . scope_id ) ;
65
+ setWorkspaceServices ( workspaceServices . workspaceServices ) ;
66
+ setLoadingState ( LoadingState . Ok ) ;
67
+ // get shared services to pass to nav shared services pages
68
+ const sharedServices = await apiCall ( ApiEndpoint . SharedServices , HttpMethod . Get ) ;
69
+ setSharedServices ( sharedServices . sharedServices ) ;
70
+ } else if ( appRoles . roles . includes ( RoleName . TREAdmin ) ) {
71
+
72
+ ws = ( await apiCall ( `${ ApiEndpoint . Workspaces } /${ workspaceId } ` , HttpMethod . Get ) ) . workspace ;
73
+ workspaceCtx . current . setWorkspace ( ws ) ;
74
+ setLoadingState ( LoadingState . Ok ) ;
75
+ refIsTREAdminUser . current = true ;
76
+ } else {
77
+ let e = new APIError ( ) ;
78
+ e . status = 403 ;
79
+ e . userMessage = "User does not have a role assigned in the workspace or the TRE Admin role assigned" ;
80
+ e . endpoint = `${ ApiEndpoint . Workspaces } /${ workspaceId } ` ;
81
+ throw e ;
82
+ }
83
+
84
+ } catch ( e : any ) {
85
+ if ( e . status === 401 || e . status === 403 ) {
86
+ setApiError ( e ) ;
87
+ setLoadingState ( LoadingState . AccessDenied ) ;
88
+ } else {
89
+ e . userMessage = 'Error retrieving workspace' ;
90
+ setApiError ( e ) ;
91
+ setLoadingState ( LoadingState . Error ) ;
92
+ }
68
93
}
94
+
69
95
} ;
70
96
getWorkspace ( ) ;
71
97
@@ -76,76 +102,95 @@ export const WorkspaceProvider: React.FunctionComponent = () => {
76
102
ctx . setRoles ( [ ] ) ;
77
103
ctx . setWorkspace ( { } as Workspace ) ;
78
104
} ) ;
79
- } , [ apiCall , workspaceId ] ) ;
105
+ } , [ apiCall , workspaceId , appRoles . roles , loadingState ] ) ;
80
106
81
107
const addWorkspaceService = ( w : WorkspaceService ) => {
82
- let ws = [ ...workspaceServices ]
108
+ let ws = [ ...workspaceServices ] ;
83
109
ws . push ( w ) ;
84
110
setWorkspaceServices ( ws ) ;
85
- }
111
+ } ;
86
112
87
113
const updateWorkspaceService = ( w : WorkspaceService ) => {
88
114
let i = workspaceServices . findIndex ( ( f : WorkspaceService ) => f . id === w . id ) ;
89
- let ws = [ ...workspaceServices ]
115
+ let ws = [ ...workspaceServices ] ;
90
116
ws . splice ( i , 1 , w ) ;
91
117
setWorkspaceServices ( ws ) ;
92
- }
118
+ } ;
93
119
94
120
const removeWorkspaceService = ( w : WorkspaceService ) => {
95
121
let i = workspaceServices . findIndex ( ( f : WorkspaceService ) => f . id === w . id ) ;
96
122
let ws = [ ...workspaceServices ] ;
97
- console . log ( "removing WS..." , ws [ i ] ) ;
98
123
ws . splice ( i , 1 ) ;
99
124
setWorkspaceServices ( ws ) ;
100
- }
125
+ } ;
101
126
102
127
switch ( loadingState ) {
103
128
case LoadingState . Ok :
104
129
return (
105
130
< >
106
131
< WorkspaceHeader />
107
132
< Stack horizontal className = 'tre-body-inner' >
108
- < Stack . Item className = 'tre-left-nav' >
109
- < WorkspaceLeftNav
110
- workspaceServices = { workspaceServices }
111
- sharedServices = { sharedServices }
112
- setWorkspaceService = { ( ws : WorkspaceService ) => setSelectedWorkspaceService ( ws ) }
113
- addWorkspaceService = { ( ws : WorkspaceService ) => addWorkspaceService ( ws ) } />
114
- </ Stack . Item > < Stack . Item className = 'tre-body-content' >
133
+ { ! refIsTREAdminUser . current && (
134
+ < Stack . Item className = 'tre-left-nav' >
135
+ < WorkspaceLeftNav
136
+ workspaceServices = { workspaceServices }
137
+ sharedServices = { sharedServices }
138
+ setWorkspaceService = { ( ws : WorkspaceService ) => setSelectedWorkspaceService ( ws ) }
139
+ addWorkspaceService = { ( ws : WorkspaceService ) => addWorkspaceService ( ws ) } />
140
+ </ Stack . Item >
141
+ ) }
142
+ < Stack . Item className = 'tre-body-content' >
115
143
< Stack >
116
144
< Stack . Item grow = { 100 } >
117
145
< Routes >
118
146
< Route path = "/" element = { < >
119
147
< WorkspaceItem />
120
- < WorkspaceServices workspaceServices = { workspaceServices }
121
- setWorkspaceService = { ( ws : WorkspaceService ) => setSelectedWorkspaceService ( ws ) }
122
- addWorkspaceService = { ( ws : WorkspaceService ) => addWorkspaceService ( ws ) }
123
- updateWorkspaceService = { ( ws : WorkspaceService ) => updateWorkspaceService ( ws ) }
124
- removeWorkspaceService = { ( ws : WorkspaceService ) => removeWorkspaceService ( ws ) } />
125
- </ > } />
126
- < Route path = "workspace-services" element = {
127
- < WorkspaceServices workspaceServices = { workspaceServices }
128
- setWorkspaceService = { ( ws : WorkspaceService ) => setSelectedWorkspaceService ( ws ) }
129
- addWorkspaceService = { ( ws : WorkspaceService ) => addWorkspaceService ( ws ) }
130
- updateWorkspaceService = { ( ws : WorkspaceService ) => updateWorkspaceService ( ws ) }
131
- removeWorkspaceService = { ( ws : WorkspaceService ) => removeWorkspaceService ( ws ) }
132
- />
133
- } />
134
- < Route path = "workspace-services/:workspaceServiceId/*" element = {
135
- < WorkspaceServiceItem
136
- workspaceService = { selectedWorkspaceService }
137
- updateWorkspaceService = { ( ws : WorkspaceService ) => updateWorkspaceService ( ws ) }
138
- removeWorkspaceService = { ( ws : WorkspaceService ) => removeWorkspaceService ( ws ) } />
139
- } />
140
- < Route path = "shared-services" element = {
141
- < SharedServices readonly = { true } />
142
- } />
143
- < Route path = "shared-services/:sharedServiceId/*" element = {
144
- < SharedServiceItem readonly = { true } />
145
- } />
146
- < Route path = "requests/*" element = {
147
- < Airlock />
148
- } />
148
+ { ! refIsTREAdminUser . current ? (
149
+ < WorkspaceServices workspaceServices = { workspaceServices }
150
+ setWorkspaceService = { ( ws : WorkspaceService ) => setSelectedWorkspaceService ( ws ) }
151
+ addWorkspaceService = { ( ws : WorkspaceService ) => addWorkspaceService ( ws ) }
152
+ updateWorkspaceService = { ( ws : WorkspaceService ) => updateWorkspaceService ( ws ) }
153
+ removeWorkspaceService = { ( ws : WorkspaceService ) => removeWorkspaceService ( ws ) } />
154
+ ) : (
155
+ < Stack className = "tre-panel" >
156
+ < Stack . Item >
157
+ < FontIcon iconName = "WarningSolid"
158
+ className = { warningIcon }
159
+ />
160
+ You are currently accessing this workspace using a TRE Admin role. Additional funcitonality requires a workspace role, such as Workspace Owner.
161
+ </ Stack . Item >
162
+ </ Stack >
163
+ ) }
164
+ </ > }
165
+ />
166
+ { ! refIsTREAdminUser . current && (
167
+ < >
168
+ < Route path = "workspace-services" element = {
169
+ < WorkspaceServices workspaceServices = { workspaceServices }
170
+ setWorkspaceService = { ( ws : WorkspaceService ) => setSelectedWorkspaceService ( ws ) }
171
+ addWorkspaceService = { ( ws : WorkspaceService ) => addWorkspaceService ( ws ) }
172
+ updateWorkspaceService = { ( ws : WorkspaceService ) => updateWorkspaceService ( ws ) }
173
+ removeWorkspaceService = { ( ws : WorkspaceService ) => removeWorkspaceService ( ws ) }
174
+ />
175
+ } />
176
+ < Route path = "workspace-services/:workspaceServiceId/*" element = {
177
+ < WorkspaceServiceItem
178
+ workspaceService = { selectedWorkspaceService }
179
+ updateWorkspaceService = { ( ws : WorkspaceService ) => updateWorkspaceService ( ws ) }
180
+ removeWorkspaceService = { ( ws : WorkspaceService ) => removeWorkspaceService ( ws ) } />
181
+ } />
182
+
183
+ < Route path = "shared-services" element = {
184
+ < SharedServices readonly = { true } />
185
+ } />
186
+ < Route path = "shared-services/:sharedServiceId/*" element = {
187
+ < SharedServiceItem readonly = { true } />
188
+ } />
189
+ < Route path = "requests/*" element = {
190
+ < Airlock />
191
+ } />
192
+ </ >
193
+ ) }
149
194
</ Routes >
150
195
</ Stack . Item >
151
196
</ Stack >
@@ -154,14 +199,22 @@ export const WorkspaceProvider: React.FunctionComponent = () => {
154
199
</ >
155
200
) ;
156
201
case LoadingState . Error :
202
+ case LoadingState . AccessDenied :
157
203
return (
158
204
< ExceptionLayout e = { apiError } />
159
- )
205
+ ) ;
160
206
default :
161
207
return (
162
208
< div style = { { marginTop : '20px' } } >
163
209
< Spinner label = "Loading Workspace" ariaLive = "assertive" labelPosition = "top" size = { SpinnerSize . large } />
164
210
</ div >
165
- )
211
+ ) ;
166
212
}
167
213
} ;
214
+
215
+ const { palette } = getTheme ( ) ;
216
+ const warningIcon = mergeStyles ( {
217
+ color : palette . orangeLight ,
218
+ fontSize : 18 ,
219
+ marginRight : 8
220
+ } ) ;
0 commit comments