@@ -184,7 +184,7 @@ public List<VaultDto> getAllVaults() {
184184 @ APIResponse (responseCode = "200" )
185185 @ APIResponse (responseCode = "403" , description = "not a vault owner" )
186186 public List <MemberDto > getDirectMembers (@ PathParam ("vaultId" ) UUID vaultId ) {
187- return vaultAccessRepo .forVault (vaultId ).map (access -> switch (access .getAuthority ()) {
187+ return vaultAccessRepo .forVault (vaultId ).filter ( access -> !( access . getAuthority () instanceof User u ) || u . isEnabled ()). map (access -> switch (access .getAuthority ()) {
188188 case User u -> MemberDto .fromEntity (u , access .getRole ());
189189 case Group g -> MemberDto .fromEntity (g , access .getRole ());
190190 default -> throw new IllegalStateException ();
@@ -501,7 +501,7 @@ public Response grantAccess(@PathParam("vaultId") UUID vaultId, @NotEmpty Map<St
501501 @ GET
502502 @ Path ("/{vaultId}" )
503503 @ RolesAllowed ("user" )
504- @ VaultRole (value = {VaultAccess .Role .MEMBER , VaultAccess .Role .OWNER }, bypassForRealmRole = true , realmRole = RealmRole .ADMIN , onMissingVault = VaultRole .OnMissingVault . NOT_FOUND )
504+ @ VaultRole (value = {VaultAccess .Role .MEMBER , VaultAccess .Role .OWNER }, bypassForRealmRole = { RealmRole .ADMIN } , onMissingVault = @ VaultRole .OnMissingVault ( VaultRole . OnMissingVault . Action . NOT_FOUND ) )
505505 @ Produces (MediaType .APPLICATION_JSON )
506506 @ Transactional
507507 @ Operation (summary = "gets a vault" )
@@ -512,15 +512,49 @@ public VaultDto get(@PathParam("vaultId") UUID vaultId) {
512512 return VaultDto .fromEntity (vault );
513513 }
514514
515+ @ PUT
516+ @ Path ("/{vaultId}/archived" )
517+ @ RolesAllowed ("user" )
518+ @ VaultRole (value = VaultAccess .Role .OWNER , bypassForRealmRole = { RealmRole .ADMIN }, onMissingVault = @ VaultRole .OnMissingVault (VaultRole .OnMissingVault .Action .NOT_FOUND ))
519+ @ Consumes (MediaType .TEXT_PLAIN )
520+ @ Produces (MediaType .APPLICATION_JSON )
521+ @ Transactional
522+ @ Operation (summary = "sets the archived flag of a vault" )
523+ @ APIResponse (responseCode = "200" , description = "archived flag updated" )
524+ @ APIResponse (responseCode = "402" , description = "number of licensed seats would be exceeded after unarchiving" )
525+ @ APIResponse (responseCode = "403" , description = "requesting user is neither a vault owner nor has the admin role" )
526+ @ APIResponse (responseCode = "404" , description = "vault not found" )
527+ public VaultDto setArchived (@ PathParam ("vaultId" ) UUID vaultId , @ NotNull Boolean archived ) {
528+ Vault vault = vaultRepo .findByIdOptional (vaultId ).orElseThrow (NotFoundException ::new );
529+ if (vault .isArchived () && !archived ) {
530+ // resolve group members and simulate new seat count:
531+ var authorityIds = vaultAccessRepo .forVault (vaultId )
532+ .map (va -> va .getId ().authorityId ())
533+ .collect (Collectors .toSet ());
534+ var effectiveUsers = new HashSet <User >();
535+ effectiveUsers .addAll (userRepo .getEffectiveGroupUsers (authorityIds ));
536+ effectiveUsers .addAll (userRepo .findByIds (authorityIds ).toList ());
537+ var projectedSeatUsers = new HashSet <>(effectiveVaultAccessRepo .usersSeatedOnOtherVaults (vaultId ).toList ());
538+ projectedSeatUsers .addAll (effectiveUsers .stream ().map (User ::getId ).toList ());
539+ if (projectedSeatUsers .size () > license .getEntitlements ().seats ()) {
540+ throw new PaymentRequiredException ("Number of effective vault users exceeds available license seats" );
541+ }
542+ }
543+ vault .setArchived (archived );
544+ vaultRepo .persistAndFlush (vault );
545+ eventLogger .logVaultUpdated (jwt .getSubject (), vault .getId (), vault .getName (), vault .getDescription (), vault .isArchived ());
546+ return VaultDto .fromEntity (vault );
547+ }
548+
515549 @ PUT
516550 @ Path ("/{vaultId}" )
517551 @ RolesAllowed ("user" ) // general authentication. VaultRole filter will check for specific access rights
518- @ VaultRole (value = VaultAccess .Role .OWNER , onMissingVault = VaultRole .OnMissingVault . REQUIRE_REALM_ROLE , realmRole = RealmRole .CREATE_VAULTS , bypassForEmergencyAccess = true )
552+ @ VaultRole (value = VaultAccess .Role .OWNER , onMissingVault = @ VaultRole .OnMissingVault ( value = VaultRole . OnMissingVault . Action . REQUIRE_REALM_ROLE , realmRole = RealmRole .CREATE_VAULTS ) , bypassForEmergencyAccess = true )
519553 @ Consumes (MediaType .APPLICATION_JSON )
520554 @ Produces (MediaType .APPLICATION_JSON )
521555 @ Transactional
522556 @ Operation (summary = "creates or updates a vault" ,
523- description = "Creates or updates a vault with the given vault id. The creationTime in the vaultDto is always ignored. On creation, the current server time is used and the archived field is ignored . On update, only the name, description, and archived fields are considered." )
557+ description = "Creates or updates a vault with the given vault id. The creationTime in the vaultDto is always ignored. The archived field is always ignored (use the dedicated endpoint). On creation, the current server time is used. On update, only the name and description fields are considered." )
524558 @ APIResponse (responseCode = "200" , description = "existing vault updated" )
525559 @ APIResponse (responseCode = "201" , description = "new vault created" )
526560 @ APIResponse (responseCode = "402" , description = "number of licensed seats is exceeded" )
@@ -545,7 +579,6 @@ public Response createOrUpdate(@PathParam("vaultId") UUID vaultId, @Valid @NotNu
545579 // set regardless of whether vault is new or existing:
546580 vault .setName (vaultDto .name );
547581 vault .setDescription (vaultDto .description );
548- vault .setArchived (existingVault .isEmpty () ? false : vaultDto .archived );
549582 var oldEmergencyKeyShares = vault .getEmergencyKeyShares ().values ();
550583 vault .setRequiredEmergencyKeyShares (vaultDto .requiredEmergencyKeyShares );
551584 vault .setEmergencyKeyShares (vaultDto .emergencyKeyShares );
0 commit comments