Skip to content

Commit

Permalink
[UserSubstitution] Handle user removal jmix-projects/jmix-ui#696
Browse files Browse the repository at this point in the history
  • Loading branch information
glebfox committed Oct 17, 2021
1 parent 2b57f06 commit 6ceabd2
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import io.jmix.core.security.event.UserRemovedEvent;
import io.jmix.core.usersubstitution.event.UserSubstitutionsChangedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Date;
import java.util.*;
import java.util.stream.Collectors;

/**
Expand All @@ -33,6 +37,9 @@ public class InMemoryUserSubstitutionProvider implements UserSubstitutionProvide
//the key of the map is UserSubstitution.username
protected Multimap<String, UserSubstitution> userSubstitutions = HashMultimap.create();

@Autowired
protected ApplicationEventPublisher eventPublisher;

@Override
public Collection<UserSubstitution> getUserSubstitutions(String username, Date date) {
return userSubstitutions.get(username).stream()
Expand All @@ -48,4 +55,34 @@ public void addUserSubstitution(UserSubstitution userSubstitution) {
public void clear() {
this.userSubstitutions.clear();
}

@EventListener
protected void onUserRemove(UserRemovedEvent event) {
String username = event.getUsername();
Set<String> usernames = new HashSet<>();

if (userSubstitutions.containsKey(username)) {
userSubstitutions.removeAll(username);
usernames.add(username);
}

List<UserSubstitution> substitutions = userSubstitutions.values().stream()
.filter(userSubstitution ->
userSubstitution.getSubstitutedUsername().equals(username))
.collect(Collectors.toList());

for (UserSubstitution substitution : substitutions) {
userSubstitutions.remove(substitution.getUsername(), substitution);
usernames.add(substitution.getUsername());
}

for (String name : usernames) {
fireUserSubstitutionsChanged(name);
}
}

protected void fireUserSubstitutionsChanged(String username) {
UserSubstitutionsChangedEvent changedEvent = new UserSubstitutionsChangedEvent(username);
eventPublisher.publishEvent(changedEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@
import org.springframework.security.core.userdetails.UserDetails;

/**
* Event that is fired for UI when UserDetails substitutions for currently logged in user are changed.
* Event that is fired for UI when substitutions for a user are changed.
*/
public class UiUserSubstitutionsChangedEvent extends ApplicationEvent {

public UiUserSubstitutionsChangedEvent(UserDetails source) {
public UiUserSubstitutionsChangedEvent(String source) {
super(source);
}

@Override
public UserDetails getSource() {
return (UserDetails) super.getSource();
public String getSource() {
return (String) super.getSource();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2021 Haulmont.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.jmix.core.usersubstitution.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.security.core.userdetails.UserDetails;

/**
* Event that is fired when substitutions for a user are changed.
*/
public class UserSubstitutionsChangedEvent extends ApplicationEvent {

public UserSubstitutionsChangedEvent(String source) {
super(source);
}

@Override
public String getSource() {
return (String) super.getSource();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
import io.jmix.core.security.CurrentAuthentication;
import io.jmix.core.security.SecurityContextHelper;
import io.jmix.core.security.UserRepository;
import io.jmix.core.usersubstitution.UserSubstitutionManager;
import io.jmix.core.security.event.UserSubstitutedEvent;
import io.jmix.core.security.impl.SubstitutedUserAuthenticationToken;
import io.jmix.core.usersubstitution.UserSubstitution;
import io.jmix.core.usersubstitution.UserSubstitutionManager;
import io.jmix.core.usersubstitution.UserSubstitutionProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
Expand All @@ -44,7 +44,7 @@ public class UserSubstitutionManagerImpl implements UserSubstitutionManager {
protected UserRepository userRepository;

@Autowired
private CurrentAuthentication currentAuthentication;
protected CurrentAuthentication currentAuthentication;

@Autowired(required = false)
protected AuthenticationManager authenticationManager;
Expand All @@ -56,7 +56,7 @@ public class UserSubstitutionManagerImpl implements UserSubstitutionManager {
protected Collection<UserSubstitutionProvider> userSubstitutionProviders;

@Autowired
private TimeSource timeSource;
protected TimeSource timeSource;

@Override
public List<UserDetails> getCurrentSubstitutedUsers() {
Expand All @@ -73,7 +73,7 @@ public List<UserDetails> getSubstitutedUsers(String username, Date date) {
.collect(Collectors.toList());
}

private Collection<UserSubstitution> getUserSubstitutions(String username, Date date) {
protected Collection<UserSubstitution> getUserSubstitutions(String username, Date date) {
return userSubstitutionProviders.stream()
.flatMap(provider -> provider.getUserSubstitutions(username, date).stream())
.collect(Collectors.toList());
Expand Down Expand Up @@ -106,7 +106,7 @@ public void substituteUser(String substitutedUserName) {
}

protected boolean canSubstitute(String userName, String substitutedUserName) {
return userName.equals(substitutedUserName)
return userName.equals(substitutedUserName)
|| getUserSubstitutions(userName, timeSource.currentTimestamp()).stream()
.anyMatch(userSubstitution -> userSubstitution.getSubstitutedUsername().equals(substitutedUserName));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
package user_substitution_provider

import io.jmix.core.CoreConfiguration
import io.jmix.core.security.event.UserRemovedEvent
import io.jmix.core.usersubstitution.InMemoryUserSubstitutionProvider
import io.jmix.core.usersubstitution.UserSubstitution
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationEventPublisher
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
import test_support.base.TestBaseConfiguration
Expand All @@ -32,6 +34,9 @@ class InMemoryUserSubstitutionProviderTest extends Specification {
@Autowired
InMemoryUserSubstitutionProvider userSubstitutionProvider

@Autowired
ApplicationEventPublisher eventPublisher

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm")

def setup() {
Expand Down Expand Up @@ -102,4 +107,31 @@ class InMemoryUserSubstitutionProviderTest extends Specification {
substitutions.size() == 1
substitutions[0].username == 'orig1'
}

def "provider should remove substitutions for removed users"() {
userSubstitutionProvider.addUserSubstitution(new UserSubstitution("orig", "userToRemove"))
userSubstitutionProvider.addUserSubstitution(new UserSubstitution("orig", "subst1"))
userSubstitutionProvider.addUserSubstitution(new UserSubstitution("orig", "subst2"))

userSubstitutionProvider.addUserSubstitution(new UserSubstitution("userToRemove", "subst1"))

when:

eventPublisher.publishEvent(new UserRemovedEvent("userToRemove"))

def origSubstitutions =
userSubstitutionProvider.getUserSubstitutions("orig", sdf.parse("2021-01-15 09:00"))
def userToRemoveSubstitutions =
userSubstitutionProvider.getUserSubstitutions("userToRemove", sdf.parse("2021-01-15 09:00"))

then: "there is no substitution of 'userToRemove'"

origSubstitutions.size() == 2
origSubstitutions[0].substitutedUsername != "userToRemove"
origSubstitutions[1].substitutedUsername != "userToRemove"

and: "there are no userToRemove's substitutions"

userToRemoveSubstitutions.size() == 0
}
}

0 comments on commit 6ceabd2

Please sign in to comment.