implement user converter

This commit is contained in:
Eduard Heimbuch
2020-10-15 15:18:53 +02:00
parent 7d2351e06d
commit 258e056646
5 changed files with 232 additions and 10 deletions

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.security;
import com.google.inject.Inject;
@@ -33,10 +33,14 @@ import sonia.scm.NotFoundException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.plugin.Extension;
import sonia.scm.user.ExternalUserConverter;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.security.AdministrationContext;
import java.util.Collections;
import java.util.Set;
/**
* Helper class for syncing realms. The class should simplify the creation of realms, which are syncing authenticated
* users with the local database.
@@ -50,28 +54,41 @@ public final class SyncingRealmHelper {
private final AdministrationContext ctx;
private final UserManager userManager;
private final GroupManager groupManager;
private final Set<ExternalUserConverter> externalUserConverters;
/**
* Constructs a new SyncingRealmHelper.
*
* @param ctx administration context
* @param userManager user manager
* @param groupManager group manager
* @param ctx administration context
* @param userManager user manager
* @param groupManager group manager
* @param externalUserConverters global scm configuration
*/
@Inject
public SyncingRealmHelper(AdministrationContext ctx, UserManager userManager, GroupManager groupManager) {
public SyncingRealmHelper(AdministrationContext ctx, UserManager userManager, GroupManager groupManager, Set<ExternalUserConverter> externalUserConverters) {
this.ctx = ctx;
this.userManager = userManager;
this.groupManager = groupManager;
this.externalUserConverters = externalUserConverters;
}
/**
* Constructs a new SyncingRealmHelper.
*
* @param ctx administration context
* @param userManager user manager
* @param groupManager group manager
*/
@Deprecated
public SyncingRealmHelper(AdministrationContext ctx, UserManager userManager, GroupManager groupManager) {
this(ctx, userManager, groupManager, Collections.emptySet());
}
/**
* Create {@link AuthenticationInfo} from user and groups.
*
*
* @param realm name of the realm
* @param user authenticated user
*
* @param user authenticated user
* @return authentication info
*/
public AuthenticationInfo createAuthenticationInfo(String realm, User user) {
@@ -114,10 +131,17 @@ public final class SyncingRealmHelper {
public void store(final User user) {
ctx.runAsAdmin(() -> {
if (userManager.contains(user.getName())) {
User clone = user.clone();
if (!externalUserConverters.isEmpty()) {
for (ExternalUserConverter converter : externalUserConverters) {
clone = converter.convert(clone);
}
}
try {
userManager.modify(user);
userManager.modify(clone);
} catch (NotFoundException e) {
throw new IllegalStateException("got NotFoundException though user " + user.getName() + " could be loaded", e);
throw new IllegalStateException("got NotFoundException though user " + clone.getName() + " could be loaded", e);
}
} else {
try {

View File

@@ -0,0 +1,39 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user;
import sonia.scm.plugin.ExtensionPoint;
@ExtensionPoint
public interface ExternalUserConverter {
/**
* Returns the converted user.
*
* @return converted user
* @since 2.8.0
*/
User convert(User user);
}

View File

@@ -27,6 +27,7 @@ package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import org.apache.shiro.authc.AuthenticationInfo;
import org.junit.Before;
import org.junit.Test;
@@ -36,6 +37,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.AlreadyExistsException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.user.ExternalUserConverter;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.security.AdministrationContext;
@@ -47,6 +49,7 @@ import static org.hamcrest.Matchers.hasItem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -68,8 +71,13 @@ public class SyncingRealmHelperTest {
@Mock
private UserManager userManager;
@Mock
private ExternalUserConverter converter;
private SyncingRealmHelper helper;
private SyncingRealmHelper helperWithConverters;
/**
* Mock {@link AdministrationContext} and create object under test.
*/
@@ -94,6 +102,7 @@ public class SyncingRealmHelperTest {
};
helper = new SyncingRealmHelper(ctx, userManager, groupManager);
helperWithConverters = new SyncingRealmHelper(ctx, userManager, groupManager, ImmutableSet.of(converter));
}
/**
@@ -170,6 +179,23 @@ public class SyncingRealmHelperTest {
verify(userManager, times(1)).modify(user);
}
/**
* Tests {@link SyncingRealmHelper#store(User)} with an existing user.
*/
@Test
public void testConvertUser(){
User zaphod = new User("zaphod");
when(converter.convert(any())).thenReturn(zaphod);
when(userManager.contains("tricia")).thenReturn(Boolean.TRUE);
User user = new User("tricia");
helperWithConverters.store(user);
verify(converter).convert(user);
verify(userManager, times(1)).modify(zaphod);
}
@Test
public void builderShouldSetValues() {

View File

@@ -0,0 +1,54 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.plugin.Extension;
import javax.inject.Inject;
@Extension
public class InternalToExternalUserConverter implements ExternalUserConverter{
private final ScmConfiguration scmConfiguration;
@Inject
public InternalToExternalUserConverter(ScmConfiguration scmConfiguration) {
this.scmConfiguration = scmConfiguration;
}
public User convert(User user) {
if (shouldNotConvertUser(user)) {
return user;
}
user.setExternal(true);
user.setPassword(null);
return user;
}
private boolean shouldNotConvertUser(User user) {
return user.isExternal() || !scmConfiguration.isEnabledUserConverter();
}
}

View File

@@ -0,0 +1,79 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.config.ScmConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class InternalToExternalUserConverterTest {
@Mock
ScmConfiguration scmConfiguration;
@InjectMocks
InternalToExternalUserConverter converter;
@Test
void shouldNotConvertExternalUser() {
User external = new User();
external.setExternal(true);
User user = converter.convert(external);
assertThat(user).isSameAs(external);
}
@Test
void shouldNotConvertIfConfigDisabled() {
when(scmConfiguration.isEnabledUserConverter()).thenReturn(false);
User external = new User();
external.setExternal(false);
User user = converter.convert(external);
assertThat(user).isSameAs(external);
}
@Test
void shouldReturnConvertedUser() {
when(scmConfiguration.isEnabledUserConverter()).thenReturn(true);
User internal = new User();
internal.setExternal(false);
User external = converter.convert(internal);
assertThat(external).isInstanceOf(User.class);
assertThat(external.isExternal()).isTrue();
assertThat(external.getPassword()).isNull();
}
}