11package invite .provision ;
22
3+ import com .fasterxml .jackson .databind .ObjectMapper ;
4+ import crypto .KeyStore ;
35import invite .eduid .EduID ;
46import invite .eduid .EduIDProvision ;
57import invite .exception .InvalidInputException ;
68import invite .exception .RemoteException ;
79import invite .manage .Manage ;
810import invite .manage .ManageIdentifier ;
9- import invite .model .*;
11+ import invite .model .Application ;
12+ import invite .model .Authority ;
13+ import invite .model .Provisionable ;
14+ import invite .model .RemoteProvisionedGroup ;
15+ import invite .model .RemoteProvisionedUser ;
16+ import invite .model .Role ;
17+ import invite .model .User ;
18+ import invite .model .UserRole ;
1019import invite .provision .eva .EvaClient ;
1120import invite .provision .graph .GraphClient ;
1221import invite .provision .graph .GraphResponse ;
13- import invite .provision .scim .*;
22+ import invite .provision .scim .GroupPatchRequest ;
23+ import invite .provision .scim .GroupRequest ;
24+ import invite .provision .scim .GroupURN ;
25+ import invite .provision .scim .Member ;
26+ import invite .provision .scim .Operation ;
27+ import invite .provision .scim .OperationType ;
28+ import invite .provision .scim .UserRequest ;
1429import invite .repository .RemoteProvisionedGroupRepository ;
1530import invite .repository .RemoteProvisionedUserRepository ;
31+ import invite .repository .RoleRepository ;
1632import invite .repository .UserRoleRepository ;
17- import com .fasterxml .jackson .databind .ObjectMapper ;
18- import crypto .KeyStore ;
1933import lombok .Getter ;
2034import lombok .SneakyThrows ;
21- import okhttp3 .OkHttpClient ;
2235import org .apache .commons .logging .Log ;
2336import org .apache .commons .logging .LogFactory ;
2437import org .springframework .beans .factory .annotation .Autowired ;
2538import org .springframework .beans .factory .annotation .Value ;
2639import org .springframework .core .ParameterizedTypeReference ;
27- import org .springframework .http .*;
28- import org .springframework .http .client .OkHttp3ClientHttpRequestFactory ;
40+ import org .springframework .http .HttpHeaders ;
41+ import org .springframework .http .HttpMethod ;
42+ import org .springframework .http .HttpStatus ;
43+ import org .springframework .http .HttpStatusCode ;
44+ import org .springframework .http .MediaType ;
45+ import org .springframework .http .RequestEntity ;
46+ import org .springframework .http .ResponseEntity ;
47+ import org .springframework .http .client .ClientHttpRequestInterceptor ;
48+ import org .springframework .http .client .JdkClientHttpRequestFactory ;
49+ import org .springframework .retry .support .RetryTemplate ;
2950import org .springframework .stereotype .Service ;
3051import org .springframework .util .StringUtils ;
3152import org .springframework .web .client .RestClientException ;
3253import org .springframework .web .client .RestTemplate ;
3354
3455import java .net .URI ;
35- import java .util .*;
36- import java .util .concurrent .TimeUnit ;
56+ import java .time .Duration ;
57+ import java .util .ArrayList ;
58+ import java .util .Collection ;
59+ import java .util .Collections ;
60+ import java .util .HashSet ;
61+ import java .util .List ;
62+ import java .util .Map ;
63+ import java .util .Optional ;
64+ import java .util .Set ;
3765import java .util .concurrent .atomic .AtomicReference ;
3866import java .util .stream .Collectors ;
67+ import java .util .stream .Stream ;
3968
4069@ Service
4170@ SuppressWarnings ("unchecked" )
4271public class ProvisioningServiceDefault implements ProvisioningService {
4372
73+ private final RoleRepository roleRepository ;
74+
4475 private enum APIType {
45- USER_API ("Users" ), GROUP_API ("Groups" );
76+ USER_API ("Users" ), GROUP_API ("Groups" );
4677
4778 @ Getter
4879 private final String display ;
@@ -82,7 +113,7 @@ public ProvisioningServiceDefault(UserRoleRepository userRoleRepository,
82113 EduID eduID ,
83114 @ Value ("${voot.group_urn_domain}" ) String groupUrnPrefix ,
84115 @ Value ("${config.eduid-idp-schac-home-organization}" ) String eduidIdpSchacHomeOrganization ,
85- @ Value ("${config.server-url}" ) String serverBaseURL ) {
116+ @ Value ("${config.server-url}" ) String serverBaseURL , RoleRepository roleRepository ) {
86117 this .userRoleRepository = userRoleRepository ;
87118 this .remoteProvisionedUserRepository = remoteProvisionedUserRepository ;
88119 this .remoteProvisionedGroupRepository = remoteProvisionedGroupRepository ;
@@ -93,11 +124,11 @@ public ProvisioningServiceDefault(UserRoleRepository userRoleRepository,
93124 this .eduID = eduID ;
94125 this .graphClient = new GraphClient (serverBaseURL , eduidIdpSchacHomeOrganization , keyStore , objectMapper );
95126 this .evaClient = new EvaClient (keyStore , remoteProvisionedUserRepository );
96- // Otherwise, we can't use method PATCH
97- OkHttpClient . Builder builder = new OkHttpClient . Builder ();
98- builder . connectTimeout ( 1 , TimeUnit . MINUTES );
99- builder . retryOnConnectionFailure ( true );
100- restTemplate . setRequestFactory ( new OkHttp3ClientHttpRequestFactory ( builder . build ())) ;
127+ // Using JdkClientHttpRequestFactory (available in Spring 6.1+)
128+ JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory ();
129+ requestFactory . setReadTimeout ( Duration . ofMinutes ( 1 ) );
130+ restTemplate . setRequestFactory ( requestFactory );
131+ this . roleRepository = roleRepository ;
101132 }
102133
103134 @ Override
@@ -201,6 +232,63 @@ public void deleteUserRequest(User user) {
201232
202233 List <Provisioning > provisionings = getProvisionings (user );
203234 //Delete the user to all provisionings in Manage where the user is known
235+ deprovisionUser (user , provisionings );
236+ }
237+
238+ @ Override
239+ public void deleteUserRequest (User user , UserRole userRole ) {
240+ //First send update role request
241+ this .updateGroupRequest (userRole , OperationType .Remove );
242+ /*
243+ * We first need a List all provisionings for the user#userRole, and then we need to remove the provisiongs
244+ * from that List that are in use by other user#userRoles, and those are the provisionings which we need to delete
245+ */
246+ List <Provisioning > userRoleProvisionings = getProvisioningsUserRole (user , userRole );
247+ List <String > otherProvisioningIdentifiers = user .getUserRoles ().stream ()
248+ .filter (otherUserRole -> !otherUserRole .getId ().equals (userRole .getId ()))
249+ .map (otherUserRole -> getProvisioningsUserRole (user , userRole ))
250+ .flatMap (Collection ::stream )
251+ .map (Provisioning ::getId )
252+ .toList ();
253+ List <Provisioning > provisionings = userRoleProvisionings .stream ()
254+ .filter (provisioning -> !otherProvisioningIdentifiers .contains (provisioning .getId ()))
255+ .toList ();
256+ //Delete the user to the not used anymore provisionings in Manage
257+ deprovisionUser (user , provisionings );
258+ }
259+
260+ @ Override
261+ public void deleteUserRequest (Role role ) {
262+ List <String > manageIdentifiers = getManageIdentifiers (role );
263+ List <Provisioning > allRoleProvisionings = manage .provisioning (manageIdentifiers ).stream ()
264+ .map (Provisioning ::new )
265+ .toList ();
266+ /*
267+ * We can't deprovision all users of the Role in each provisioning, as they might be in use in other provisioned
268+ * roles. We need all provisionings of the Role, but we need to check for each user which provisionings needs to
269+ * be excluded from the deprovision.
270+ */
271+ List <UserRole > userRoles = userRoleRepository .findByRole (role );
272+ userRoles .forEach (userRole -> {
273+ User user = userRole .getUser ();
274+ List <ManageIdentifier > otherManageIdentifiers = user .getUserRoles ().stream ()
275+ .filter (otherUserRole -> !otherUserRole .getId ().equals (userRole .getId ()))
276+ .map (otherUserRole -> user .manageIdentifierSet (userRole ))
277+ .flatMap (Collection ::stream )
278+ .toList ();
279+ // Provisionings that are used by any other userRoles are filtered out
280+ List <Provisioning > provisionings = allRoleProvisionings .stream ()
281+ .filter (provisioning -> provisioning .getRemoteApplications ().stream ()
282+ .noneMatch (otherManageIdentifiers ::contains ))
283+ .toList ();
284+ //Delete the user to the not used anymore provisionings in Manage
285+ deprovisionUser (user , provisionings );
286+ });
287+
288+
289+ }
290+
291+ private void deprovisionUser (User user , List <Provisioning > provisionings ) {
204292 provisionings .forEach (provisioning -> {
205293 Optional <RemoteProvisionedUser > provisionedUserOptional = this .remoteProvisionedUserRepository
206294 .findByManageProvisioningIdAndUser (provisioning .getId (), user );
@@ -465,6 +553,12 @@ private List<Provisioning> getProvisionings(User user) {
465553 return manage .provisioning (identifiers ).stream ().map (Provisioning ::new ).toList ();
466554 }
467555
556+ private List <Provisioning > getProvisioningsUserRole (User user , UserRole userRole ) {
557+ Set <ManageIdentifier > manageIdentifiers = user .manageIdentifierSet (userRole );
558+ List <String > identifiers = manageIdentifiers .stream ().map (ManageIdentifier ::manageId ).toList ();
559+ return manage .provisioning (identifiers ).stream ().map (Provisioning ::new ).toList ();
560+ }
561+
468562 private List <Provisioning > getProvisionings (Role role ) {
469563 List <String > manageIdentifiers = getManageIdentifiers (role );
470564 return manage .provisioning (manageIdentifiers ).stream ().map (Provisioning ::new ).toList ();
0 commit comments