Merge branch 'develop' into bugfix/api-key-to-access-token

This commit is contained in:
Konstantin Schaper
2020-11-03 14:48:39 +01:00
committed by GitHub
117 changed files with 2003 additions and 833 deletions

View File

@@ -46,8 +46,6 @@ public class ManagerDaoAdapter<T extends ModelObject> {
if (notModified != null) {
permissionCheck.apply(notModified).check();
doThrow().violation("type must not be changed").when(!notModified.getType().equals(object.getType()));
AssertUtil.assertIsValid(object);
beforeUpdate.handle(notModified);

View File

@@ -35,16 +35,14 @@ import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import static sonia.scm.repository.Branch.VALID_BRANCH_NAMES;
@Getter
@Setter
@NoArgsConstructor
@SuppressWarnings("java:S2160") // we do not need this for dto
public class BranchDto extends HalRepresentation {
private static final String VALID_CHARACTERS_AT_START_AND_END = "\\w-,;\\]{}@&+=$#`|<>";
private static final String VALID_CHARACTERS = VALID_CHARACTERS_AT_START_AND_END + "/.";
static final String VALID_BRANCH_NAMES = "[" + VALID_CHARACTERS_AT_START_AND_END + "]([" + VALID_CHARACTERS + "]*[" + VALID_CHARACTERS_AT_START_AND_END + "])?";
@NotEmpty
@Length(min = 1, max = 100)
@Pattern(regexp = VALID_BRANCH_NAMES)

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.api.v2.resources;
import lombok.Getter;
@@ -31,7 +31,7 @@ import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import static sonia.scm.api.v2.resources.BranchDto.VALID_BRANCH_NAMES;
import static sonia.scm.repository.Branch.VALID_BRANCH_NAMES;
@Getter
@Setter

View File

@@ -56,6 +56,7 @@ public class ConfigDto extends HalRepresentation {
private String pluginUrl;
private long loginAttemptLimitTimeout;
private boolean enabledXsrfProtection;
private boolean enabledUserConverter;
private String namespaceStrategy;
private String loginInfoUrl;
private String releaseFeedUrl;

View File

@@ -102,7 +102,7 @@ public class MeDtoFactory extends HalAppenderMapper {
if (UserPermissions.changePublicKeys(user).isPermitted()) {
linksBuilder.single(link("publicKeys", resourceLinks.user().publicKeys(user.getName())));
}
if (userManager.isTypeDefault(user) && UserPermissions.changePassword(user).isPermitted()) {
if (!user.isExternal() && UserPermissions.changePassword(user).isPermitted()) {
linksBuilder.single(link("password", resourceLinks.me().passwordChange()));
}
if (UserPermissions.changeApiKeys(user).isPermitted()) {

View File

@@ -123,6 +123,14 @@ class ResourceLinks {
return userLinkBuilder.method("getUserResource").parameters(name).method("overwritePassword").parameters().href();
}
public String toExternal(String name) {
return userLinkBuilder.method("getUserResource").parameters(name).method("toExternal").parameters().href();
}
public String toInternal(String name) {
return userLinkBuilder.method("getUserResource").parameters(name).method("toInternal").parameters().href();
}
public String publicKeys(String name) {
return publicKeyLinkBuilder.method("findAll").parameters(name).href();
}

View File

@@ -41,6 +41,7 @@ import java.time.Instant;
@NoArgsConstructor @Getter @Setter
public class UserDto extends HalRepresentation {
private boolean active;
private boolean external;
private Instant creationDate;
@NotEmpty
private String displayName;

View File

@@ -186,6 +186,71 @@ public class UserResource {
return Response.noContent().build();
}
/**
* This Endpoint is for Admin user to convert external user to internal.
* The oldPassword property of the DTO is not needed here. it will be ignored.
* The oldPassword property is needed in the MeResources when the actual user change the own password.
*
* <strong>Note:</strong> This method requires "user:modify" privilege to modify the password of other users.
*
* @param name name of the user to be modified
* @param passwordOverwrite change password object to modify password. the old password is here not required
*/
@PUT
@Path("convert-to-internal")
@Consumes(VndMediaType.USER)
@Operation(summary = "Converts an external user to internal", description = "Converts an external user to an internal one and set the new password.", tags = "User")
@ApiResponse(responseCode = "204", description = "update success")
@ApiResponse(responseCode = "400", description = "invalid body, e.g. the new password is missing")
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"user\" privilege")
@ApiResponse(
responseCode = "404",
description = "not found, no user with the specified id/name available",
content = @Content(
mediaType = VndMediaType.ERROR_TYPE,
schema = @Schema(implementation = ErrorDto.class)
))
@ApiResponse(responseCode = "500", description = "internal server error")
public Response toInternal(@PathParam("id") String name, @Valid PasswordOverwriteDto passwordOverwrite) {
UserDto dto = userToDtoMapper.map(userManager.get(name));
dto.setExternal(false);
adapter.update(name, existing -> dtoToUserMapper.map(dto, existing.getPassword()));
userManager.overwritePassword(name, passwordService.encryptPassword(passwordOverwrite.getNewPassword()));
return Response.noContent().build();
}
/**
* This Endpoint is for Admin user to convert internal user to external.
*
* <strong>Note:</strong> This method requires "user:modify" privilege to modify the password of other users.
*
* @param name name of the user to be modified
*/
@PUT
@Path("convert-to-external")
@Consumes(VndMediaType.USER)
@Operation(summary = "Converts an internal user to external", description = "Converts an internal user to an external one and removes the local password.", tags = "User")
@ApiResponse(responseCode = "204", description = "update success")
@ApiResponse(responseCode = "400", description = "invalid body, e.g. the new password is missing")
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"user\" privilege")
@ApiResponse(
responseCode = "404",
description = "not found, no user with the specified id/name available",
content = @Content(
mediaType = VndMediaType.ERROR_TYPE,
schema = @Schema(implementation = ErrorDto.class)
))
@ApiResponse(responseCode = "500", description = "internal server error")
public Response toExternal(@PathParam("id") String name) {
userManager.overwritePassword(name, null);
UserDto dto = userToDtoMapper.map(userManager.get(name));
dto.setExternal(true);
adapter.update(name, existing -> dtoToUserMapper.map(dto, existing.getPassword()));
return Response.noContent().build();
}
@Path("permissions")
public UserPermissionResource permissions() {
return userPermissionResource;

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.api.v2.resources;
import de.otto.edison.hal.Embedded;
@@ -66,8 +66,11 @@ public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> {
if (UserPermissions.modify(user).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.user().update(user.getName())));
linksBuilder.single(link("publicKeys", resourceLinks.user().publicKeys(user.getName())));
if (userManager.isTypeDefault(user)) {
if (user.isExternal()) {
linksBuilder.single(link("convertToInternal", resourceLinks.user().toInternal(user.getName())));
} else {
linksBuilder.single(link("password", resourceLinks.user().passwordChange(user.getName())));
linksBuilder.single(link("convertToExternal", resourceLinks.user().toExternal(user.getName())));
}
}
if (PermissionPermissions.read().isPermitted()) {

View File

@@ -389,8 +389,8 @@ public class DefaultUserManager extends AbstractUserManager
if (user == null) {
throw new NotFoundException(User.class, userId);
}
if (!isTypeDefault(user) || isAnonymousUser(user)) {
throw new ChangePasswordNotAllowedException(ContextEntry.ContextBuilder.entity("PasswordChange", "-").in(User.class, user.getName()), user.getType());
if (isAnonymousUser(user) || user.isExternal()) {
throw new ChangePasswordNotAllowedException(ContextEntry.ContextBuilder.entity("PasswordChange", "-").in(User.class, user.getName()), "external");
}
user.setPassword(newPassword);
this.modify(user);

View File

@@ -0,0 +1,56 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.plugin.Extension;
import javax.inject.Inject;
@Slf4j
@Extension
public class InternalToExternalUserConverter implements ExternalUserConverter{
private final ScmConfiguration scmConfiguration;
@Inject
public InternalToExternalUserConverter(ScmConfiguration scmConfiguration) {
this.scmConfiguration = scmConfiguration;
}
public User convert(User user) {
if (shouldConvertUser(user)) {
log.info("Convert internal user {} to external", user.getId());
user.setExternal(true);
user.setPassword(null);
}
return user;
}
private boolean shouldConvertUser(User user) {
return !user.isExternal() && scmConfiguration.isEnabledUserConverter();
}
}

View File

@@ -41,7 +41,7 @@ public class BrowserUserAgentProvider implements UserAgentProvider
/** Field description */
@VisibleForTesting
static final UserAgent CHROME = UserAgent.builder(
static final UserAgent CHROME = UserAgent.browser(
"Chrome").basicAuthenticationCharset(
Charsets.UTF_8).build();
@@ -50,21 +50,21 @@ public class BrowserUserAgentProvider implements UserAgentProvider
/** Field description */
@VisibleForTesting
static final UserAgent FIREFOX = UserAgent.builder("Firefox").build();
static final UserAgent FIREFOX = UserAgent.browser("Firefox").build();
/** Field description */
private static final String FIREFOX_PATTERN = "firefox";
/** Field description */
@VisibleForTesting
static final UserAgent MSIE = UserAgent.builder("Internet Explorer").build();
static final UserAgent MSIE = UserAgent.browser("Internet Explorer").build();
/** Field description */
private static final String MSIE_PATTERN = "msie";
/** Field description */
@VisibleForTesting // todo check charset
static final UserAgent SAFARI = UserAgent.builder("Safari").build();
static final UserAgent SAFARI = UserAgent.browser("Safari").build();
/** Field description */
private static final String OPERA_PATTERN = "opera";
@@ -74,7 +74,7 @@ public class BrowserUserAgentProvider implements UserAgentProvider
/** Field description */
@VisibleForTesting // todo check charset
static final UserAgent OPERA = UserAgent.builder(
static final UserAgent OPERA = UserAgent.browser(
"Opera").basicAuthenticationCharset(
Charsets.UTF_8).build();

View File

@@ -74,10 +74,7 @@ public class HttpProtocolServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
UserAgent userAgent = userAgentParser.parse(request);
if (userAgent.isBrowser()) {
log.trace("dispatch browser request for user agent {}", userAgent);
dispatcher.dispatch(request, response, request.getRequestURI());
} else {
if (userAgent.isScmClient()) {
String pathInfo = request.getPathInfo();
Optional<NamespaceAndName> namespaceAndName = pathExtractor.fromUri(pathInfo);
if (namespaceAndName.isPresent()) {
@@ -86,6 +83,9 @@ public class HttpProtocolServlet extends HttpServlet {
log.debug("namespace and name not found in request path {}", pathInfo);
response.setStatus(HttpStatus.SC_BAD_REQUEST);
}
} else {
log.trace("dispatch non-scm-client request for user agent {}", userAgent);
dispatcher.dispatch(request, response, request.getRequestURI());
}
}

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.api.v2.resources;
import org.junit.jupiter.params.ParameterizedTest;
@@ -29,6 +29,7 @@ import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static sonia.scm.repository.Branch.VALID_BRANCH_NAMES;
class BranchDtoTest {
@@ -54,10 +55,11 @@ class BranchDtoTest {
"val{d",
"val{}d",
"val|kill",
"val}"
"val}",
"va/li/d"
})
void shouldAcceptValidBranchName(String branchName) {
assertTrue(branchName.matches(BranchDto.VALID_BRANCH_NAMES));
assertTrue(branchName.matches(VALID_BRANCH_NAMES));
}
@ParameterizedTest
@@ -70,6 +72,6 @@ class BranchDtoTest {
"val id"
})
void shouldRejectInvalidBranchName(String branchName) {
assertFalse(branchName.matches(BranchDto.VALID_BRANCH_NAMES));
assertFalse(branchName.matches(VALID_BRANCH_NAMES));
}
}

View File

@@ -34,6 +34,7 @@ import sonia.scm.security.AnonymousMode;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -42,9 +43,7 @@ public class ConfigDtoToScmConfigurationMapperTest {
@InjectMocks
private ConfigDtoToScmConfigurationMapperImpl mapper;
private String[] expectedUsers = {"trillian", "arthur"};
private String[] expectedGroups = {"admin", "plebs"};
private String[] expectedExcludes = {"ex", "clude"};
private final String[] expectedExcludes = {"ex", "clude"};
@Before
public void init() {
@@ -73,6 +72,7 @@ public class ConfigDtoToScmConfigurationMapperTest {
assertEquals("https://plug.ins", config.getPluginUrl());
assertEquals(40, config.getLoginAttemptLimitTimeout());
assertTrue(config.isEnabledXsrfProtection());
assertFalse(config.isEnabledUserConverter());
assertEquals("username", config.getNamespaceStrategy());
assertEquals("https://scm-manager.org/login-info", config.getLoginInfoUrl());
assertEquals("hitchhiker.mail", config.getMailDomainName());
@@ -115,6 +115,7 @@ public class ConfigDtoToScmConfigurationMapperTest {
configDto.setNamespaceStrategy("username");
configDto.setLoginInfoUrl("https://scm-manager.org/login-info");
configDto.setMailDomainName("hitchhiker.mail");
configDto.setEnabledUserConverter(false);
return configDto;
}

View File

@@ -43,6 +43,7 @@ import sonia.scm.ContextEntry;
import sonia.scm.group.GroupCollector;
import sonia.scm.security.ApiKey;
import sonia.scm.security.ApiKeyService;
import sonia.scm.user.EMail;
import sonia.scm.user.InvalidPasswordException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
@@ -96,6 +97,9 @@ public class MeResourceTest {
@Mock
private ApiKeyService apiKeyService;
@Mock
private EMail eMail;
@InjectMocks
private MeDtoFactory meDtoFactory;
@InjectMocks

View File

@@ -28,6 +28,7 @@ import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
import com.google.inject.util.Providers;
import com.sun.mail.iap.Argument;
import org.apache.shiro.authc.credential.PasswordService;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
@@ -58,10 +59,12 @@ import java.util.Collection;
import java.util.function.Predicate;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
@@ -457,6 +460,43 @@ public class UserRootResourceTest {
assertEquals("other:*", captor.getValue().iterator().next().getValue());
}
@Test
public void shouldConvertUserToInternalAndSetNewPassword() throws URISyntaxException {
when(passwordService.encryptPassword(anyString())).thenReturn("abc");
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
MockHttpRequest request = MockHttpRequest
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/convert-to-internal")
.contentType(VndMediaType.USER)
.content("{\"newPassword\":\"trillian\"}".getBytes());
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
verify(passwordService).encryptPassword("trillian");
verify(userManager).overwritePassword("Neo", "abc");
verify(userManager).modify(userCaptor.capture());
User user = userCaptor.getValue();
assertThat(user.isExternal()).isFalse();
}
@Test
public void shouldConvertUserToExternalAndRemoveLocalPassword() throws URISyntaxException {
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
MockHttpRequest request = MockHttpRequest
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/convert-to-external")
.contentType(VndMediaType.USER);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
verify(userManager).overwritePassword("Neo", null);
verify(userManager).modify(userCaptor.capture());
User user = userCaptor.getValue();
assertThat(user.isExternal()).isTrue();
}
private PageResult<User> createSingletonPageResult(int overallCount) {
return new PageResult<>(singletonList(createDummyUser("Neo")), overallCount);
}

View File

@@ -90,25 +90,15 @@ public class UserToUserDtoMapperTest {
}
@Test
public void shouldGetPasswordLinkForAdmin() {
public void shouldGetInternalUserLinks() {
User user = createDefaultUser();
user.setExternal(false);
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
when(userManager.isTypeDefault(eq(user))).thenReturn(true);
UserDto userDto = mapper.map(user);
assertEquals("expected password link with modify permission", expectedBaseUri.resolve("abc/password").toString(), userDto.getLinks().getLinkBy("password").get().getHref());
}
@Test
public void shouldGetPasswordLinkOnlyForDefaultUserType() {
User user = createDefaultUser();
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
when(userManager.isTypeDefault(eq(user))).thenReturn(false);
UserDto userDto = mapper.map(user);
assertFalse("expected no password link", userDto.getLinks().getLinkBy("password").isPresent());
assertEquals("expected convert to external link with modify permission", expectedBaseUri.resolve("abc/convert-to-external").toString(), userDto.getLinks().getLinkBy("convertToExternal").get().getHref());
}
@Test

View File

@@ -148,12 +148,6 @@ class DefaultRepositoryRoleManagerTest {
verify(dao).modify(role);
}
@Test
void shouldNotModifyRole_whenTypeChanged() {
assertThrows(ScmConstraintViolationException.class, () -> manager.modify(new RepositoryRole(CUSTOM_ROLE_NAME, singletonList("changed"), null)));
verify(dao, never()).modify(any());
}
@Test
void shouldNotModifyRole_whenRoleDoesNotExists() {
assertThrows(NotFoundException.class, () -> manager.modify(new RepositoryRole("noSuchRole", singletonList("changed"), null)));

View File

@@ -103,7 +103,7 @@ class JwtAccessTokenBuilderTest {
void testBuild() {
JwtAccessToken token = factory.create().subject("dent")
.issuer("https://www.scm-manager.org")
.expiresIn(5, TimeUnit.SECONDS)
.expiresIn(1, TimeUnit.MINUTES)
.custom("a", "b")
.scope(Scope.valueOf("repo:*"))
.build();

View File

@@ -21,66 +21,52 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user;
//~--- non-JDK imports --------------------------------------------------------
package sonia.scm.user;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.collect.Lists;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import sonia.scm.NotFoundException;
import sonia.scm.store.JAXBConfigurationStoreFactory;
import sonia.scm.user.xml.XmlUserDAO;
import static org.mockito.Mockito.*;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
*
* @author Sebastian Sdorra
*/
@SubjectAware(
username = "trillian",
password = "secret",
configuration = "classpath:sonia/scm/repository/shiro.ini"
username = "trillian",
password = "secret",
configuration = "classpath:sonia/scm/repository/shiro.ini"
)
public class DefaultUserManagerTest extends UserManagerTestBase
{
public class DefaultUserManagerTest extends UserManagerTestBase {
@Rule
public ShiroRule shiro = new ShiroRule();
private UserDAO userDAO ;
private User trillian;
private UserDAO userDAO;
/**
* Method description
*
*
* @return
*/
@Override
public UserManager createManager()
{
public UserManager createManager() {
return new DefaultUserManager(createXmlUserDAO());
}
@Before
public void initDao() {
trillian = UserTestData.createTrillian();
User trillian = UserTestData.createTrillian();
trillian.setPassword("oldEncrypted");
userDAO = mock(UserDAO.class);
@@ -108,15 +94,6 @@ public class DefaultUserManagerTest extends UserManagerTestBase
Assertions.assertThat(userCaptor.getValue().getPassword()).isEqualTo("newEncrypted");
}
@Test(expected = ChangePasswordNotAllowedException.class)
public void shouldFailOverwritePasswordForWrongType() {
trillian.setType("wrongType");
UserManager userManager = new DefaultUserManager(userDAO);
userManager.overwritePassword("trillian", "---");
}
@Test(expected = NotFoundException.class)
public void shouldFailOverwritePasswordForMissingUser() {
UserManager userManager = new DefaultUserManager(userDAO);
@@ -124,6 +101,16 @@ public class DefaultUserManagerTest extends UserManagerTestBase
userManager.overwritePassword("notExisting", "---");
}
@Test(expected = ChangePasswordNotAllowedException.class)
public void shouldFailOverwritePasswordForExternalUser() {
User trillian = new User("trillian");
trillian.setExternal(true);
when(userDAO.get("trillian")).thenReturn(trillian);
UserManager userManager = new DefaultUserManager(userDAO);
userManager.overwritePassword("trillian", "---");
}
@Test
public void shouldSucceedOverwritePassword() {
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);

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();
}
}

View File

@@ -91,15 +91,12 @@ class HttpProtocolServletTest {
@BeforeEach
void prepareMocks() {
when(userAgentParser.parse(request)).thenReturn(userAgent);
when(userAgent.isBrowser()).thenReturn(true);
when(userAgent.isScmClient()).thenReturn(false);
when(request.getRequestURI()).thenReturn("uri");
}
@Test
void shouldDispatchBrowserRequests() throws ServletException, IOException {
when(userAgent.isBrowser()).thenReturn(true);
when(request.getRequestURI()).thenReturn("uri");
servlet.service(request, response);
verify(dispatcher).dispatch(request, response, "uri");
@@ -113,7 +110,7 @@ class HttpProtocolServletTest {
@BeforeEach
void prepareMocks() {
when(userAgentParser.parse(request)).thenReturn(userAgent);
when(userAgent.isBrowser()).thenReturn(false);
when(userAgent.isScmClient()).thenReturn(true);
}
@Test