mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 17:05:43 +01:00
Mind review findings
This commit is contained in:
@@ -36,7 +36,7 @@ Example: If anonymous access is enabled and the "_anonymous" user has full acces
|
|||||||
The url of the RSS Release Feed for SCM-Manager. This provides up-to-date version information. To disable this feature just leave the url blank.
|
The url of the RSS Release Feed for SCM-Manager. This provides up-to-date version information. To disable this feature just leave the url blank.
|
||||||
|
|
||||||
#### User converter
|
#### User converter
|
||||||
Internal users will automatically be converted to external on their first login using an external system. After convertion the users may only log in using the external system.
|
Internal users will automatically be converted to external on their first login using an external system. After conversion the users may only log in using the external system.
|
||||||
|
|
||||||
#### Fallback Mail Domain Name
|
#### Fallback Mail Domain Name
|
||||||
This domain name will be used to create email addresses for users without one when needed. It will not be used to send mails nor will be accessed otherwise.
|
This domain name will be used to create email addresses for users without one when needed. It will not be used to send mails nor will be accessed otherwise.
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ The "Create User" form can be used to create new users in SCM-Manager. New users
|
|||||||
### User Details Page
|
### User Details Page
|
||||||
The user details page shows the information about the user.
|
The user details page shows the information about the user.
|
||||||
|
|
||||||
The active box shows whether the user is able to use SCM-Manager. The external box shows if it is an internal user or is managed by an external system.
|
The active box shows whether the user is able to use SCM-Manager. The external box shows if it is an internal user or whether it is managed by an external system.
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -5,7 +5,7 @@ subtitle: Settings
|
|||||||
### General
|
### General
|
||||||
In the general settings the display name, e-mail address, external flag and active status of an account can be edited.
|
In the general settings the display name, e-mail address, external flag and active status of an account can be edited.
|
||||||
|
|
||||||
If an user is converted from internal to external the passwords will be removed. Switching an external user to internal user a password must be set using the password modal dialogue.
|
If a user is converted from internal to external the password is going to be removed. When switching an external user to an internal one, a password must be set using the password modal dialogue.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public final class SyncingRealmHelper {
|
|||||||
* @param ctx administration context
|
* @param ctx administration context
|
||||||
* @param userManager user manager
|
* @param userManager user manager
|
||||||
* @param groupManager group manager
|
* @param groupManager group manager
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public SyncingRealmHelper(AdministrationContext ctx, UserManager userManager, GroupManager groupManager) {
|
public SyncingRealmHelper(AdministrationContext ctx, UserManager userManager, GroupManager groupManager) {
|
||||||
@@ -110,17 +111,9 @@ public final class SyncingRealmHelper {
|
|||||||
public void store(final Group group) {
|
public void store(final Group group) {
|
||||||
ctx.runAsAdmin(() -> {
|
ctx.runAsAdmin(() -> {
|
||||||
if (groupManager.get(group.getId()) != null) {
|
if (groupManager.get(group.getId()) != null) {
|
||||||
try {
|
modifyGroup(group);
|
||||||
groupManager.modify(group);
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
throw new IllegalStateException("got NotFoundException though group " + group.getName() + " could be loaded", e);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
createNewGroup(group);
|
||||||
groupManager.create(group);
|
|
||||||
} catch (AlreadyExistsException e) {
|
|
||||||
throw new IllegalStateException("got AlreadyExistsException though group " + group.getName() + " could not be loaded", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -133,6 +126,26 @@ public final class SyncingRealmHelper {
|
|||||||
public void store(final User user) {
|
public void store(final User user) {
|
||||||
ctx.runAsAdmin(() -> {
|
ctx.runAsAdmin(() -> {
|
||||||
if (userManager.contains(user.getName())) {
|
if (userManager.contains(user.getName())) {
|
||||||
|
modifyUser(user);
|
||||||
|
} else {
|
||||||
|
createNewUser(user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNewUser(User user) {
|
||||||
|
try {
|
||||||
|
User clone = user.clone();
|
||||||
|
// New user created by syncing realm helper is always external
|
||||||
|
clone.setExternal(true);
|
||||||
|
userManager.create(clone);
|
||||||
|
} catch (AlreadyExistsException e) {
|
||||||
|
throw new IllegalStateException("got AlreadyExistsException though user " + user.getName() + " could not be loaded", e);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void modifyUser(User user) {
|
||||||
User clone = user.clone();
|
User clone = user.clone();
|
||||||
if (!externalUserConverters.isEmpty()) {
|
if (!externalUserConverters.isEmpty()) {
|
||||||
log.debug("execute available user converters");
|
log.debug("execute available user converters");
|
||||||
@@ -146,17 +159,21 @@ public final class SyncingRealmHelper {
|
|||||||
} catch (NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
throw new IllegalStateException("got NotFoundException though user " + clone.getName() + " could be loaded", e);
|
throw new IllegalStateException("got NotFoundException though user " + clone.getName() + " could be loaded", e);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
try {
|
|
||||||
User clone = user.clone();
|
|
||||||
// New user created by syncing realm helper is always external
|
|
||||||
clone.setExternal(true);
|
|
||||||
userManager.create(clone);
|
|
||||||
} catch (AlreadyExistsException e) {
|
|
||||||
throw new IllegalStateException("got AlreadyExistsException though user " + user.getName() + " could not be loaded", e);
|
|
||||||
|
|
||||||
|
private void createNewGroup(Group group) {
|
||||||
|
try {
|
||||||
|
groupManager.create(group);
|
||||||
|
} catch (AlreadyExistsException e) {
|
||||||
|
throw new IllegalStateException("got AlreadyExistsException though group " + group.getName() + " could not be loaded", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
private void modifyGroup(Group group) {
|
||||||
|
try {
|
||||||
|
groupManager.modify(group);
|
||||||
|
} catch (NotFoundException e) {
|
||||||
|
throw new IllegalStateException("got NotFoundException though group " + group.getName() + " could be loaded", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
|||||||
private String password;
|
private String password;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
/**
|
/**
|
||||||
* @deprecated Use external instead.
|
|
||||||
* The user type is replaced by {@link external} flag
|
* The user type is replaced by {@link external} flag
|
||||||
* Since 2.8.0
|
* @since 2.8.0
|
||||||
|
* @deprecated Use {@link external} instead.
|
||||||
*/
|
*/
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
|
|||||||
@@ -128,10 +128,10 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
const { user, passwordValid } = this.state;
|
const { user, passwordValid } = this.state;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!this.isInvalid()) {
|
if (!this.isInvalid()) {
|
||||||
|
this.props.submitForm(this.state.user);
|
||||||
if (user.password && passwordValid && user._links?.password) {
|
if (user.password && passwordValid && user._links?.password) {
|
||||||
setPassword((user._links.password as Link).href, user.password).catch(error => this.setState({ error }));
|
setPassword((user._links.password as Link).href, user.password).catch(error => this.setState({ error }));
|
||||||
}
|
}
|
||||||
this.props.submitForm(this.state.user);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
label={t("user.externalFlag")}
|
label={t("user.externalFlag")}
|
||||||
onChange={this.handleExternalFlagChange}
|
onChange={this.handleExternalFlagChange}
|
||||||
checked={!!user?.external && user.external}
|
checked={!!user?.external}
|
||||||
helpText={t("help.externalFlagHelpText")}
|
helpText={t("help.externalFlagHelpText")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ class ChangePasswordNavLink extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasPermissionToSetPassword = () => {
|
hasPermissionToSetPassword = () => {
|
||||||
return this.props.user._links.password;
|
const { user } = this.props;
|
||||||
|
return !user.external && user._links.password;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class Details extends React.Component<Props> {
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{t("user.externalFlag")}</th>
|
<th>{t("user.externalFlag")}</th>
|
||||||
<td>
|
<td>
|
||||||
<Checkbox checked={!!user?.external && user.external} />
|
<Checkbox checked={!!user.external} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -389,8 +389,8 @@ public class DefaultUserManager extends AbstractUserManager
|
|||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new NotFoundException(User.class, userId);
|
throw new NotFoundException(User.class, userId);
|
||||||
}
|
}
|
||||||
if (isAnonymousUser(user)) {
|
if (isAnonymousUser(user) || user.isExternal()) {
|
||||||
throw new ChangePasswordNotAllowedException(ContextEntry.ContextBuilder.entity("PasswordChange", "-").in(User.class, user.getName()), user.getType());
|
throw new ChangePasswordNotAllowedException(ContextEntry.ContextBuilder.entity("PasswordChange", "-").in(User.class, user.getName()), "external");
|
||||||
}
|
}
|
||||||
user.setPassword(newPassword);
|
user.setPassword(newPassword);
|
||||||
this.modify(user);
|
this.modify(user);
|
||||||
|
|||||||
@@ -24,31 +24,22 @@
|
|||||||
|
|
||||||
package sonia.scm.user;
|
package sonia.scm.user;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.github.sdorra.shiro.ShiroRule;
|
import com.github.sdorra.shiro.ShiroRule;
|
||||||
import com.github.sdorra.shiro.SubjectAware;
|
import com.github.sdorra.shiro.SubjectAware;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions;
|
import org.assertj.core.api.Assertions;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
||||||
import sonia.scm.user.xml.XmlUserDAO;
|
import sonia.scm.user.xml.XmlUserDAO;
|
||||||
|
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import org.junit.Rule;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@SubjectAware(
|
@SubjectAware(
|
||||||
@@ -56,31 +47,26 @@ import org.junit.Rule;
|
|||||||
password = "secret",
|
password = "secret",
|
||||||
configuration = "classpath:sonia/scm/repository/shiro.ini"
|
configuration = "classpath:sonia/scm/repository/shiro.ini"
|
||||||
)
|
)
|
||||||
public class DefaultUserManagerTest extends UserManagerTestBase
|
public class DefaultUserManagerTest extends UserManagerTestBase {
|
||||||
{
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShiroRule shiro = new ShiroRule();
|
public ShiroRule shiro = new ShiroRule();
|
||||||
|
|
||||||
|
private UserDAO userDAO;
|
||||||
private UserDAO userDAO ;
|
|
||||||
private User trillian;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method description
|
* Method description
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public UserManager createManager()
|
public UserManager createManager() {
|
||||||
{
|
|
||||||
return new DefaultUserManager(createXmlUserDAO());
|
return new DefaultUserManager(createXmlUserDAO());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void initDao() {
|
public void initDao() {
|
||||||
trillian = UserTestData.createTrillian();
|
User trillian = UserTestData.createTrillian();
|
||||||
trillian.setPassword("oldEncrypted");
|
trillian.setPassword("oldEncrypted");
|
||||||
|
|
||||||
userDAO = mock(UserDAO.class);
|
userDAO = mock(UserDAO.class);
|
||||||
@@ -115,6 +101,16 @@ public class DefaultUserManagerTest extends UserManagerTestBase
|
|||||||
userManager.overwritePassword("notExisting", "---");
|
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
|
@Test
|
||||||
public void shouldSucceedOverwritePassword() {
|
public void shouldSucceedOverwritePassword() {
|
||||||
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user