removes admin user and group configuration in favor of permissions

This commit is contained in:
Sebastian Sdorra
2019-03-13 12:54:50 +01:00
parent 017879619c
commit 1627518954
19 changed files with 12 additions and 376 deletions

View File

@@ -95,16 +95,6 @@ public class ScmConfiguration implements Configuration {
@SuppressWarnings("WeakerAccess") // This might be needed for permission checking
public static final String PERMISSION = "global";
@XmlElement(name = "admin-groups")
@XmlJavaTypeAdapter(XmlSetStringAdapter.class)
private Set<String> adminGroups;
@XmlElement(name = "admin-users")
@XmlJavaTypeAdapter(XmlSetStringAdapter.class)
private Set<String> adminUsers;
@XmlElement(name = "base-url")
private String baseUrl;
@@ -211,8 +201,6 @@ public class ScmConfiguration implements Configuration {
this.dateFormat = other.dateFormat;
this.pluginUrl = other.pluginUrl;
this.anonymousAccessEnabled = other.anonymousAccessEnabled;
this.adminUsers = other.adminUsers;
this.adminGroups = other.adminGroups;
this.enableProxy = other.enableProxy;
this.proxyPort = other.proxyPort;
this.proxyServer = other.proxyServer;
@@ -230,14 +218,6 @@ public class ScmConfiguration implements Configuration {
this.defaultNamespaceStrategy = other.defaultNamespaceStrategy;
}
public Set<String> getAdminGroups() {
return adminGroups;
}
public Set<String> getAdminUsers() {
return adminUsers;
}
/**
* Returns the complete base url of the scm-manager including the context path.
* For example http://localhost:8080/scm
@@ -381,14 +361,6 @@ public class ScmConfiguration implements Configuration {
return skipFailedAuthenticators;
}
public void setAdminGroups(Set<String> adminGroups) {
this.adminGroups = adminGroups;
}
public void setAdminUsers(Set<String> adminUsers) {
this.adminUsers = adminUsers;
}
public void setAnonymousAccessEnabled(boolean anonymousAccessEnabled) {
this.anonymousAccessEnabled = anonymousAccessEnabled;
}

View File

@@ -12,8 +12,6 @@ export type Config = {
disableGroupingGrid: boolean,
dateFormat: string,
anonymousAccessEnabled: boolean,
adminGroups: string[],
adminUsers: string[],
baseUrl: string,
forceBaseUrl: boolean,
loginAttemptLimit: number,

View File

@@ -30,19 +30,6 @@
"base-url": "Base URL",
"force-base-url": "Base URL erzwingen"
},
"admin-settings": {
"name": "Administrations Einstellungen",
"admin-groups": "Admin Gruppen",
"admin-users": "Admin Benutzer",
"remove-group-button": "Admin Group löschen",
"remove-user-button": "Admin Benutzer löschen",
"add-group-error": "Der eingegebene Gruppenname ist ungültig",
"add-group-textfield": "Neue Gruppe mit Administrationsrechten hinzufügen",
"add-group-button": "Admin Gruppe hinzufügen",
"add-user-error": "Der eingegebene Benutzername ist ungültig",
"add-user-textfield": "Neuen Benutzer mit Administrationsrechten hinzufügen",
"add-user-button": "Admin Benutzer hinzufügen"
},
"login-attempt": {
"name": "Anmeldeversuche",
"login-attempt-limit": "Limit für Anmeldeversuche",

View File

@@ -4,7 +4,6 @@
"displayName": "Anzeigename",
"mail": "E-Mail",
"password": "Passwort",
"admin": "Admin",
"active": "Aktiv",
"type": "Typ",
"creationDate": "Erstellt",

View File

@@ -30,19 +30,6 @@
"base-url": "Base URL",
"force-base-url": "Force Base URL"
},
"admin-settings": {
"name": "Administration Settings",
"admin-groups": "Admin Groups",
"admin-users": "Admin Users",
"remove-group-button": "Remove Admin Group",
"remove-user-button": "Remove Admin User",
"add-group-error": "The group name you want to add is not valid",
"add-group-textfield": "Add group you want to add to admin groups here",
"add-group-button": "Add Admin Group",
"add-user-error": "The user name you want to add is not valid",
"add-user-textfield": "Add user you want to add to admin users here",
"add-user-button": "Add Admin User"
},
"login-attempt": {
"name": "Login Attempt",
"login-attempt-limit": "Login Attempt Limit",

View File

@@ -4,7 +4,6 @@
"displayName": "Display Name",
"mail": "E-Mail",
"password": "Password",
"admin": "Admin",
"active": "Active",
"type": "Type",
"creationDate": "Creation Date",

View File

@@ -1,93 +0,0 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import { Subtitle, AddEntryToTableField } from "@scm-manager/ui-components";
import AdminGroupTable from "../table/AdminGroupTable";
import AdminUserTable from "../table/AdminUserTable";
type Props = {
adminGroups: string[],
adminUsers: string[],
t: string => string,
onChange: (boolean, any, string) => void,
hasUpdatePermission: boolean
};
class AdminSettings extends React.Component<Props> {
render() {
const { t, adminGroups, adminUsers, hasUpdatePermission } = this.props;
return (
<div>
<Subtitle subtitle={t("admin-settings.name")} />
<div className="columns">
<div className="column is-half">
<AdminGroupTable
adminGroups={adminGroups}
onChange={(isValid, changedValue, name) =>
this.props.onChange(isValid, changedValue, name)
}
disabled={!hasUpdatePermission}
/>
<AddEntryToTableField
addEntry={this.addGroup}
disabled={!hasUpdatePermission}
buttonLabel={t("admin-settings.add-group-button")}
fieldLabel={t("admin-settings.add-group-textfield")}
errorMessage={t("admin-settings.add-group-error")}
/>
</div>
<div className="column is-half">
<AdminUserTable
adminUsers={adminUsers}
onChange={(isValid, changedValue, name) =>
this.props.onChange(isValid, changedValue, name)
}
disabled={!hasUpdatePermission}
/>
<AddEntryToTableField
addEntry={this.addUser}
disabled={!hasUpdatePermission}
buttonLabel={t("admin-settings.add-user-button")}
fieldLabel={t("admin-settings.add-user-textfield")}
errorMessage={t("admin-settings.add-user-error")}
/>
</div>
</div>
</div>
);
}
addGroup = (groupname: string) => {
if (this.isAdminGroupMember(groupname)) {
return;
}
this.props.onChange(
true,
[...this.props.adminGroups, groupname],
"adminGroups"
);
};
isAdminGroupMember = (groupname: string) => {
return this.props.adminGroups.includes(groupname);
};
addUser = (username: string) => {
if (this.isAdminUserMember(username)) {
return;
}
this.props.onChange(
true,
[...this.props.adminUsers, username],
"adminUsers"
);
};
isAdminUserMember = (username: string) => {
return this.props.adminUsers.includes(username);
};
}
export default translate("config")(AdminSettings);

View File

@@ -6,7 +6,6 @@ import type { Config } from "@scm-manager/ui-types";
import ProxySettings from "./ProxySettings";
import GeneralSettings from "./GeneralSettings";
import BaseUrlSettings from "./BaseUrlSettings";
import AdminSettings from "./AdminSettings";
import LoginAttempt from "./LoginAttempt";
type Props = {
@@ -44,8 +43,6 @@ class ConfigForm extends React.Component<Props, State> {
disableGroupingGrid: false,
dateFormat: "",
anonymousAccessEnabled: false,
adminGroups: [],
adminUsers: [],
baseUrl: "",
forceBaseUrl: false,
loginAttemptLimit: 0,
@@ -151,15 +148,6 @@ class ConfigForm extends React.Component<Props, State> {
hasUpdatePermission={configUpdatePermission}
/>
<hr />
<AdminSettings
adminGroups={config.adminGroups}
adminUsers={config.adminUsers}
onChange={(isValid, changedValue, name) =>
this.onChange(isValid, changedValue, name)
}
hasUpdatePermission={configUpdatePermission}
/>
<hr />
<ProxySettings
proxyPassword={config.proxyPassword ? config.proxyPassword : ""}
proxyPort={config.proxyPort}

View File

@@ -1,37 +0,0 @@
//@flow
import React from "react";
import { translate } from "react-i18next";
import ArrayConfigTable from "./ArrayConfigTable";
type Props = {
adminGroups: string[],
onChange: (boolean, any, string) => void,
disabled: boolean,
// context props
t: string => string
};
type State = {};
class AdminGroupTable extends React.Component<Props, State> {
render() {
const { t, disabled, adminGroups } = this.props;
return (
<ArrayConfigTable
items={adminGroups}
label={t("admin-settings.admin-groups")}
removeLabel={t("admin-settings.remove-group-button")}
onRemove={this.removeEntry}
disabled={disabled}
helpText={t("help.adminGroupsHelpText")}
/>
);
}
removeEntry = (newGroups: string[]) => {
this.props.onChange(true, newGroups, "adminGroups");
};
}
export default translate("config")(AdminGroupTable);

View File

@@ -1,35 +0,0 @@
//@flow
import React from "react";
import { translate } from "react-i18next";
import ArrayConfigTable from "./ArrayConfigTable";
type Props = {
adminUsers: string[],
onChange: (boolean, any, string) => void,
disabled: boolean,
// context props
t: string => string
};
class AdminUserTable extends React.Component<Props> {
render() {
const { adminUsers, t, disabled } = this.props;
return (
<ArrayConfigTable
items={adminUsers}
label={t("admin-settings.admin-users")}
removeLabel={t("admin-settings.remove-user-button")}
onRemove={this.removeEntry}
disabled={disabled}
helpText={t("help.adminUsersHelpText")}
/>
);
}
removeEntry = (newUsers: string[]) => {
this.props.onChange(true, newUsers, "adminUsers");
};
}
export default translate("config")(AdminUserTable);

View File

@@ -23,10 +23,6 @@ public class ConfigDto extends HalRepresentation {
private boolean disableGroupingGrid;
private String dateFormat;
private boolean anonymousAccessEnabled;
@NoBlankStrings
private Set<String> adminGroups;
@NoBlankStrings
private Set<String> adminUsers;
private String baseUrl;
private boolean forceBaseUrl;
private int loginAttemptLimit;

View File

@@ -19,7 +19,6 @@ import static sonia.scm.api.v2.ValidationConstraints.USER_GROUP_PATTERN;
@NoArgsConstructor @Getter @Setter
public class UserDto extends HalRepresentation {
private boolean active;
private boolean admin;
private Instant creationDate;
@NotEmpty
private String displayName;

View File

@@ -77,9 +77,6 @@ import java.util.Set;
public class DefaultAuthorizationCollector implements AuthorizationCollector
{
/** Field description */
private static final String ADMIN_PERMISSION = "*";
/** Field description */
private static final String CACHE_NAME = "sonia.cache.authorizing";
@@ -94,18 +91,14 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
/**
* Constructs ...
*
*
*
* @param configuration
* @param cacheManager
* @param repositoryDAO
* @param securitySystem
*/
@Inject
public DefaultAuthorizationCollector(ScmConfiguration configuration, CacheManager cacheManager,
public DefaultAuthorizationCollector(CacheManager cacheManager,
RepositoryDAO repositoryDAO, SecuritySystem securitySystem)
{
this.configuration = configuration;
this.cache = cacheManager.getCache(CACHE_NAME);
this.repositoryDAO = repositoryDAO;
this.securitySystem = securitySystem;
@@ -233,27 +226,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
}
}
private AuthorizationInfo createAuthorizationInfo(User user,
GroupNames groups)
{
Set<String> roles;
Set<String> permissions;
if (isAdmin(user, groups))
{
if (logger.isDebugEnabled())
{
logger.debug("grant admin role for user {}", user.getName());
}
roles = ImmutableSet.of(Role.USER, Role.ADMIN);
permissions = ImmutableSet.of(ADMIN_PERMISSION);
}
else
{
roles = ImmutableSet.of(Role.USER);
private AuthorizationInfo createAuthorizationInfo(User user, GroupNames groups) {
Builder<String> builder = ImmutableSet.builder();
collectGlobalPermissions(builder, user, groups);
@@ -262,40 +235,13 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
builder.add(getUserAutocompletePermission());
builder.add(getGroupAutocompletePermission());
builder.add(getChangeOwnPasswordPermission(user));
permissions = builder.build();
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
info.addStringPermissions(permissions);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(ImmutableSet.of(Role.USER));
info.addStringPermissions(builder.build());
return info;
}
private boolean isAdmin(User user, GroupNames groups) {
if (isUserAdminInConfiguration(user)) {
logger.debug("user {} is marked as admin, because of the admin user configuration", user.getName());
return true;
}
return isUserAdminInGroupConfiguration(user, groups);
}
private boolean isUserAdminInGroupConfiguration(User user, GroupNames groups) {
Set<String> adminGroups = configuration.getAdminGroups();
if (adminGroups != null && groups != null) {
for (String group : groups) {
if (adminGroups.contains(group)) {
logger.debug("user {} is marked as admin, because of the admin group configuration for group {}", user.getName(), group);
return true;
}
}
}
return false;
}
private boolean isUserAdminInConfiguration(User user) {
Set<String> adminUsers = configuration.getAdminUsers();
return adminUsers != null && adminUsers.contains(user.getName());
}
private String getGroupAutocompletePermission() {
return GroupPermissions.autocomplete().asShiroString();
}
@@ -399,8 +345,6 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
//~--- fields ---------------------------------------------------------------
private final ScmConfiguration configuration;
/** authorization cache */
private final Cache<CacheKey, AuthorizationInfo> cache;

View File

@@ -41,8 +41,6 @@ public class ConfigDtoToScmConfigurationMapperTest {
assertTrue(config.isDisableGroupingGrid());
assertEquals("yyyy" , config.getDateFormat());
assertTrue(config.isAnonymousAccessEnabled());
assertTrue("adminGroups", config.getAdminGroups().containsAll(Arrays.asList(expectedGroups)));
assertTrue("adminUsers", config.getAdminUsers().containsAll(Arrays.asList(expectedUsers)));
assertEquals("baseurl" , config.getBaseUrl());
assertTrue(config.isForceBaseUrl());
assertEquals(41 , config.getLoginAttemptLimit());
@@ -66,8 +64,6 @@ public class ConfigDtoToScmConfigurationMapperTest {
configDto.setDisableGroupingGrid(true);
configDto.setDateFormat("yyyy");
configDto.setAnonymousAccessEnabled(true);
configDto.setAdminGroups(Sets.newSet(expectedGroups));
configDto.setAdminUsers(Sets.newSet(expectedUsers));
configDto.setBaseUrl("baseurl");
configDto.setForceBaseUrl(true);
configDto.setLoginAttemptLimit(41);

View File

@@ -118,27 +118,6 @@ public class ConfigResourceTest {
dispatcher.invoke(request, response);
}
@Test
@SubjectAware(username = "readWrite")
public void shouldFailForEmptyAdminUsers() throws URISyntaxException, IOException {
MockHttpRequest request = post("sonia/scm/api/v2/config-test-empty-admin-user.json");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
}
@Test
@SubjectAware(username = "readWrite")
public void shouldFailForEmptyAdminGroups() throws URISyntaxException, IOException {
MockHttpRequest request = post("sonia/scm/api/v2/config-test-empty-admin-group.json");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
}
private MockHttpRequest post(String resourceName) throws IOException, URISyntaxException {
URL url = Resources.getResource(resourceName);

View File

@@ -71,8 +71,6 @@ public class ScmConfigurationToConfigDtoMapperTest {
assertTrue(dto.isDisableGroupingGrid());
assertEquals("dd" , dto.getDateFormat());
assertTrue(dto.isAnonymousAccessEnabled());
assertTrue("adminGroups", dto.getAdminGroups().containsAll(Arrays.asList(expectedGroups)));
assertTrue("adminUsers", dto.getAdminUsers().containsAll(Arrays.asList(expectedUsers)));
assertEquals("baseurl" , dto.getBaseUrl());
assertTrue(dto.isForceBaseUrl());
assertEquals(1 , dto.getLoginAttemptLimit());
@@ -111,8 +109,6 @@ public class ScmConfigurationToConfigDtoMapperTest {
config.setDisableGroupingGrid(true);
config.setDateFormat("dd");
config.setAnonymousAccessEnabled(true);
config.setAdminGroups(Sets.newSet(expectedGroups));
config.setAdminUsers(Sets.newSet(expectedUsers));
config.setBaseUrl("baseurl");
config.setForceBaseUrl(true);
config.setLoginAttemptLimit(1);

View File

@@ -78,8 +78,6 @@ import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class DefaultAuthorizationCollectorTest {
private ScmConfiguration configuration;
@Mock
private Cache cache;
@@ -103,38 +101,7 @@ public class DefaultAuthorizationCollectorTest {
@Before
public void setUp(){
when(cacheManager.getCache(Mockito.any(String.class))).thenReturn(cache);
configuration = new ScmConfiguration();
collector = new DefaultAuthorizationCollector(configuration, cacheManager, repositoryDAO, securitySystem);
}
@Test
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void shouldGetAdminPrivilegedByConfiguration() {
configuration.setAdminUsers(ImmutableSet.of("trillian"));
authenticate(UserTestData.createTrillian(), "main");
AuthorizationInfo authInfo = collector.collect();
assertIsAdmin(authInfo);
}
private void assertIsAdmin(AuthorizationInfo authInfo) {
assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER, Role.ADMIN));
assertThat(authInfo.getObjectPermissions(), nullValue());
assertThat(authInfo.getStringPermissions(), Matchers.contains("*"));
}
@Test
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void shouldGetAdminPrivilegedByGroupConfiguration() {
configuration.setAdminGroups(ImmutableSet.of("heartOfGold"));
authenticate(UserTestData.createTrillian(), "heartOfGold");
AuthorizationInfo authInfo = collector.collect();
assertIsAdmin(authInfo);
collector = new DefaultAuthorizationCollector(cacheManager, repositoryDAO, securitySystem);
}
/**

View File

@@ -1,3 +0,0 @@
{
"adminGroups": [""]
}

View File

@@ -1,3 +0,0 @@
{
"adminUsers": [""]
}