mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-05-07 05:05:50 +02:00
Merged in feature/consolidate_admin_permissions (pull request #219)
Feature/consolidate admin permissions
This commit is contained in:
@@ -1,181 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
|
||||
/**
|
||||
* Configuration object for a SCM-Manager
|
||||
* client (WebInterface, RestClient, ...).
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class ScmClientConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs {@link ScmClientConfig} object
|
||||
*
|
||||
*/
|
||||
public ScmClientConfig() {}
|
||||
|
||||
/**
|
||||
* Constructs {@link ScmClientConfig} object
|
||||
*
|
||||
*
|
||||
* @param configuration SCM-Manager main configuration
|
||||
* @since 1.14
|
||||
*/
|
||||
public ScmClientConfig(ScmConfiguration configuration)
|
||||
{
|
||||
this.dateFormat = configuration.getDateFormat();
|
||||
this.disableGroupingGrid = configuration.isDisableGroupingGrid();
|
||||
this.enableRepositoryArchive = configuration.isEnableRepositoryArchive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs {@link ScmClientConfig} object
|
||||
*
|
||||
*
|
||||
* @param dateFormat
|
||||
*/
|
||||
public ScmClientConfig(String dateFormat)
|
||||
{
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs {@link ScmClientConfig} object
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param dateFormat
|
||||
* @param disableGroupingGrid true to disable repository grouping
|
||||
*/
|
||||
public ScmClientConfig(String dateFormat, boolean disableGroupingGrid)
|
||||
{
|
||||
this.dateFormat = dateFormat;
|
||||
this.disableGroupingGrid = disableGroupingGrid;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the date format for the user interface. This format is a
|
||||
* JavaScript date format.
|
||||
*
|
||||
* @see <a target="_blank" href="http://jacwright.com/projects/javascript/date_format">Date Format</a>
|
||||
* @return JavaScript date format
|
||||
*/
|
||||
public String getDateFormat()
|
||||
{
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the grouping of repositories is disabled.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return true if the grouping of repositories is disabled
|
||||
*/
|
||||
public boolean isDisableGroupingGrid()
|
||||
{
|
||||
return disableGroupingGrid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the repository archive is disabled.
|
||||
*
|
||||
*
|
||||
* @return true if the repository archive is disabled
|
||||
* @since 1.14
|
||||
*/
|
||||
public boolean isEnableRepositoryArchive()
|
||||
{
|
||||
return enableRepositoryArchive;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Setter for the date format
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param dateFormat - JavaScript date format
|
||||
*/
|
||||
public void setDateFormat(String dateFormat)
|
||||
{
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the grouping of repositories.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
*
|
||||
* @param disableGroupingGrid
|
||||
*/
|
||||
public void setDisableGroupingGrid(boolean disableGroupingGrid)
|
||||
{
|
||||
this.disableGroupingGrid = disableGroupingGrid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the repository archive. Default is disabled.
|
||||
*
|
||||
*
|
||||
* @param enableRepositoryArchive true to disable the repository archive
|
||||
* @since 1.14
|
||||
*/
|
||||
public void setEnableRepositoryArchive(boolean enableRepositoryArchive)
|
||||
{
|
||||
this.enableRepositoryArchive = enableRepositoryArchive;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private String dateFormat;
|
||||
|
||||
/** Field description */
|
||||
private boolean enableRepositoryArchive = true;
|
||||
|
||||
/** Field description */
|
||||
private boolean disableGroupingGrid = true;
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.RepositoryType;
|
||||
import sonia.scm.security.PermissionDescriptor;
|
||||
import sonia.scm.user.User;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* This class represents the current state of the SCM-Manager.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@XmlRootElement(name = "state")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public final class ScmState
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs {@link ScmState} object.
|
||||
* This constructor is required by JAXB.
|
||||
*
|
||||
*/
|
||||
ScmState() {}
|
||||
|
||||
/**
|
||||
* Constructs {@link ScmState} object.
|
||||
*
|
||||
*
|
||||
* @param version scm-manager version
|
||||
* @param user current user
|
||||
* @param groups groups of the current user
|
||||
* @param token authentication token
|
||||
* @param repositoryTypes available repository types
|
||||
* @param defaultUserType default user type
|
||||
* @param clientConfig client configuration
|
||||
* @param availablePermissions list of available permissions
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public ScmState(String version, User user, Collection<String> groups,
|
||||
String token, Collection<RepositoryType> repositoryTypes, String defaultUserType,
|
||||
ScmClientConfig clientConfig, Collection<PermissionDescriptor> availablePermissions)
|
||||
{
|
||||
this.version = version;
|
||||
this.user = user;
|
||||
this.groups = groups;
|
||||
this.token = token;
|
||||
this.repositoryTypes = repositoryTypes;
|
||||
this.clientConfig = clientConfig;
|
||||
this.defaultUserType = defaultUserType;
|
||||
this.availablePermissions = availablePermissions;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a list of available global permissions.
|
||||
*
|
||||
*
|
||||
* @return available global permissions
|
||||
* @since 1.31
|
||||
*/
|
||||
public Collection<PermissionDescriptor> getAvailablePermissions()
|
||||
{
|
||||
return availablePermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configuration for SCM-Manager clients.
|
||||
*
|
||||
*
|
||||
* @return configuration for SCM-Manager clients
|
||||
*/
|
||||
public ScmClientConfig getClientConfig()
|
||||
{
|
||||
return clientConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default user type
|
||||
*
|
||||
*
|
||||
* @return default user type
|
||||
*
|
||||
* @since 1.14
|
||||
*/
|
||||
public String getDefaultUserType()
|
||||
{
|
||||
return defaultUserType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link java.util.Collection} of groups names which are associated
|
||||
* to the current user.
|
||||
*
|
||||
*
|
||||
* @return a {@link java.util.Collection} of groups names
|
||||
*/
|
||||
public Collection<String> getGroups()
|
||||
{
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available repository types.
|
||||
*
|
||||
*
|
||||
* @return all available repository types
|
||||
*/
|
||||
public Collection<RepositoryType> getRepositoryTypes()
|
||||
{
|
||||
return repositoryTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns authentication token or {@code null}.
|
||||
*
|
||||
*
|
||||
* @return authentication token or {@code null}
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public String getToken()
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current logged in user.
|
||||
*
|
||||
*
|
||||
* @return current logged in user
|
||||
*/
|
||||
public User getUser()
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the SCM-Manager.
|
||||
*
|
||||
*
|
||||
* @return version of the SCM-Manager
|
||||
*/
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request was successful.
|
||||
* This method is required by extjs.
|
||||
*
|
||||
* @return true if the request was successful
|
||||
*/
|
||||
public boolean isSuccess()
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** marker for extjs */
|
||||
private final boolean success = true;
|
||||
|
||||
/** authentication token */
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* Avaliable global permission
|
||||
* @since 1.31
|
||||
*/
|
||||
private Collection<PermissionDescriptor> availablePermissions;
|
||||
|
||||
/** Field description */
|
||||
private ScmClientConfig clientConfig;
|
||||
|
||||
/** Field description */
|
||||
private String defaultUserType;
|
||||
|
||||
/** Field description */
|
||||
private Collection<String> groups;
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "repositoryTypes")
|
||||
private Collection<RepositoryType> repositoryTypes;
|
||||
|
||||
/** Field description */
|
||||
private User user;
|
||||
|
||||
/** Field description */
|
||||
private String version;
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Sebastian Sdorra All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.security.AuthorizationCollector;
|
||||
import sonia.scm.security.PermissionDescriptor;
|
||||
import sonia.scm.security.Role;
|
||||
import sonia.scm.security.SecuritySystem;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserManager;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Factory to create {@link ScmState}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class ScmStateFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ScmStateFactory}.
|
||||
*
|
||||
*
|
||||
* @param contextProvider context provider
|
||||
* @param configuration configuration
|
||||
* @param repositoryManger repository manager
|
||||
* @param userManager user manager
|
||||
* @param securitySystem security system
|
||||
*/
|
||||
@Inject
|
||||
public ScmStateFactory(SCMContextProvider contextProvider,
|
||||
ScmConfiguration configuration, RepositoryManager repositoryManger,
|
||||
UserManager userManager, SecuritySystem securitySystem)
|
||||
{
|
||||
this.contextProvider = contextProvider;
|
||||
this.configuration = configuration;
|
||||
this.repositoryManger = repositoryManger;
|
||||
this.userManager = userManager;
|
||||
this.securitySystem = securitySystem;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns anonymous state.
|
||||
*
|
||||
*
|
||||
* @return anonymous state
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ScmState createAnonymousState()
|
||||
{
|
||||
return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST, null, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an state from the given subject.
|
||||
*
|
||||
*
|
||||
* @param subject subject
|
||||
*
|
||||
* @return state from subject
|
||||
*/
|
||||
public ScmState createState(Subject subject)
|
||||
{
|
||||
return createState(subject, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an state from the given subject and authentication token.
|
||||
*
|
||||
*
|
||||
* @param subject subject
|
||||
* @param token authentication token
|
||||
*
|
||||
* @return state from subject and authentication token
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ScmState createState(Subject subject, String token)
|
||||
{
|
||||
PrincipalCollection collection = subject.getPrincipals();
|
||||
User user = collection.oneByType(User.class);
|
||||
GroupNames groups = collection.oneByType(GroupNames.class);
|
||||
|
||||
Collection<PermissionDescriptor> ap = Collections.EMPTY_LIST;
|
||||
|
||||
if (subject.hasRole(Role.ADMIN))
|
||||
{
|
||||
ap = securitySystem.getAvailablePermissions();
|
||||
}
|
||||
|
||||
return createState(user, groups.getCollection(), token, ap);
|
||||
}
|
||||
|
||||
private ScmState createState(User user, Collection<String> groups,
|
||||
String token,
|
||||
Collection<PermissionDescriptor> availablePermissions)
|
||||
{
|
||||
User u = user.clone();
|
||||
|
||||
// do not return password on authentication
|
||||
u.setPassword(null);
|
||||
|
||||
return new ScmState(contextProvider.getVersion(), u, groups, token,
|
||||
repositoryManger.getConfiguredTypes(), userManager.getDefaultType(),
|
||||
new ScmClientConfig(configuration), availablePermissions);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** configuration */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
/** context provider */
|
||||
private final SCMContextProvider contextProvider;
|
||||
|
||||
/** repository manager */
|
||||
private final RepositoryManager repositoryManger;
|
||||
|
||||
/** security system */
|
||||
private final SecuritySystem securitySystem;
|
||||
|
||||
/** user manager */
|
||||
private final UserManager userManager;
|
||||
}
|
||||
@@ -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.namespaceStrategy = other.namespaceStrategy;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
@@ -195,6 +196,7 @@ public class Group extends BasicPropertiesAware
|
||||
group.setMembers(members);
|
||||
group.setType(type);
|
||||
group.setDescription(description);
|
||||
group.setExternal(external);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,6 +226,7 @@ public class Group extends BasicPropertiesAware
|
||||
&& Objects.equal(description, other.description)
|
||||
&& Objects.equal(members, other.members)
|
||||
&& Objects.equal(type, other.type)
|
||||
&& Objects.equal(external, other.external)
|
||||
&& Objects.equal(creationDate, other.creationDate)
|
||||
&& Objects.equal(lastModified, other.lastModified)
|
||||
&& Objects.equal(properties, other.properties);
|
||||
@@ -270,6 +273,7 @@ public class Group extends BasicPropertiesAware
|
||||
.add("description", description)
|
||||
.add("members", members)
|
||||
.add("type", type)
|
||||
.add("external", external)
|
||||
.add("creationDate", creationDate)
|
||||
.add("lastModified", lastModified)
|
||||
.add("properties", properties)
|
||||
@@ -339,8 +343,9 @@ public class Group extends BasicPropertiesAware
|
||||
*/
|
||||
public List<String> getMembers()
|
||||
{
|
||||
if (members == null)
|
||||
{
|
||||
if (external) {
|
||||
return Collections.emptyList();
|
||||
} else if (members == null) {
|
||||
members = Lists.newArrayList();
|
||||
}
|
||||
|
||||
@@ -370,6 +375,15 @@ public class Group extends BasicPropertiesAware
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the members of the groups managed external of scm-manager.
|
||||
*
|
||||
* @return {@code true} if the group is an external group
|
||||
*/
|
||||
public boolean isExternal() {
|
||||
return external;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the member is a member of this group.
|
||||
*
|
||||
@@ -463,8 +477,21 @@ public class Group extends BasicPropertiesAware
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code true} to mark the group as external.
|
||||
*
|
||||
* @param {@code true} for a external group
|
||||
*/
|
||||
public void setExternal(boolean external)
|
||||
{
|
||||
this.external = external;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** external group */
|
||||
private boolean external = false;
|
||||
|
||||
/** timestamp of the creation date of this group */
|
||||
private Long creationDate;
|
||||
|
||||
|
||||
@@ -41,9 +41,6 @@ package sonia.scm.security;
|
||||
public final class Role
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String ADMIN = "admin";
|
||||
|
||||
/** Field description */
|
||||
public static final String USER = "user";
|
||||
|
||||
|
||||
@@ -157,12 +157,6 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
if (user.isAdmin() != admin)
|
||||
{
|
||||
result = true;
|
||||
user.setAdmin(admin);
|
||||
}
|
||||
|
||||
if (user.isActive() != active)
|
||||
{
|
||||
result = true;
|
||||
@@ -229,7 +223,6 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
||||
&& Objects.equal(displayName, other.displayName)
|
||||
&& Objects.equal(mail, other.mail)
|
||||
&& Objects.equal(type, other.type)
|
||||
&& Objects.equal(admin, other.admin)
|
||||
&& Objects.equal(active, other.active)
|
||||
&& Objects.equal(password, other.password)
|
||||
&& Objects.equal(creationDate, other.creationDate)
|
||||
@@ -246,7 +239,7 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(name, displayName, mail, type, admin, password,
|
||||
return Objects.hashCode(name, displayName, mail, type, password,
|
||||
active, creationDate, lastModified, properties);
|
||||
}
|
||||
|
||||
@@ -269,7 +262,6 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
||||
.add("displayName",displayName)
|
||||
.add("mail", mail)
|
||||
.add("password", pwd)
|
||||
.add("admin", admin)
|
||||
.add("type", type)
|
||||
.add("active", active)
|
||||
.add("creationDate", creationDate)
|
||||
@@ -385,17 +377,6 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAdmin()
|
||||
{
|
||||
return admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -424,17 +405,6 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param admin
|
||||
*/
|
||||
public void setAdmin(boolean admin)
|
||||
{
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -518,9 +488,6 @@ public class User extends BasicPropertiesAware implements Principal, ModelObject
|
||||
/** Field description */
|
||||
private boolean active = true;
|
||||
|
||||
/** Field description */
|
||||
private boolean admin = false;
|
||||
|
||||
/** Field description */
|
||||
private Long creationDate;
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2010, Sebastian Sdorra
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of SCM-Manager; nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
http://bitbucket.org/sdorra/scm-manager
|
||||
|
||||
|
||||
-->
|
||||
<users>
|
||||
<name>scmadmin</name>
|
||||
<displayName>SCM Administrator</displayName>
|
||||
<mail>scm-admin@scm-manager.org</mail>
|
||||
<password>$shiro1$SHA-512$8192$$yrNahBVDa4Gz+y5gat4msdjyvjtHlVE+N5nTl4WIDhtBFwhSIib13mKJt1sWmVqgHDWi3VwX7fkdkJ2+WToTbw==</password>
|
||||
<admin>true</admin>
|
||||
<type>xml</type>
|
||||
</users>
|
||||
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2010, Sebastian Sdorra
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of SCM-Manager; nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
http://bitbucket.org/sdorra/scm-manager
|
||||
|
||||
|
||||
-->
|
||||
|
||||
<!--
|
||||
Document : anonymous-account.xml
|
||||
Created on : December 30, 2010, 12:44 PM
|
||||
Author : sdorra
|
||||
Description:
|
||||
Purpose of the document follows.
|
||||
-->
|
||||
|
||||
<users>
|
||||
<name>anonymous</name>
|
||||
<displayName>SCM Anonymous</displayName>
|
||||
<mail>scm-anonymous@scm-manager.org</mail>
|
||||
<admin>false</admin>
|
||||
<type>xml</type>
|
||||
</users>
|
||||
@@ -27,7 +27,6 @@ public class UserITCase {
|
||||
.assertStatusCode(200)
|
||||
.requestUser(newUser)
|
||||
.assertStatusCode(200)
|
||||
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE))
|
||||
.assertPassword(Assert::assertNull)
|
||||
.requestChangePassword(newPassword)
|
||||
.assertStatusCode(204);
|
||||
@@ -36,7 +35,6 @@ public class UserITCase {
|
||||
.requestIndexResource(newUser, newPassword)
|
||||
.assertStatusCode(200)
|
||||
.requestUser(newUser)
|
||||
.assertAdmin(isAdmin -> assertThat(isAdmin).isEqualTo(Boolean.TRUE))
|
||||
.assertPassword(Assert::assertNull);
|
||||
}
|
||||
|
||||
@@ -52,7 +50,6 @@ public class UserITCase {
|
||||
.assertStatusCode(200)
|
||||
.requestUser(newUser)
|
||||
.assertStatusCode(200)
|
||||
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE)) // the user anonymous is not an admin
|
||||
.assertPassword(Assert::assertNull)
|
||||
.requestChangePassword(newPassword) // the oldPassword is not needed in the user resource
|
||||
.assertStatusCode(204);
|
||||
@@ -96,7 +93,6 @@ public class UserITCase {
|
||||
.assertStatusCode(200)
|
||||
.requestUser(newUser)
|
||||
.assertStatusCode(200)
|
||||
.assertAdmin(aBoolean -> assertThat(aBoolean).isEqualTo(Boolean.TRUE))
|
||||
.assertPassword(Assert::assertNull)
|
||||
.assertType(s -> assertThat(s).isEqualTo(type))
|
||||
.assertPasswordLinkDoesNotExists();
|
||||
|
||||
@@ -338,18 +338,10 @@ public class ScmRequests {
|
||||
return assertSingleProperty(assertType, "type");
|
||||
}
|
||||
|
||||
public UserResponse<PREV> assertAdmin(Consumer<Boolean> assertAdmin) {
|
||||
return assertSingleProperty(assertAdmin, "admin");
|
||||
}
|
||||
|
||||
public UserResponse<PREV> assertPasswordLinkDoesNotExists() {
|
||||
return assertPropertyPathDoesNotExists(LINKS_PASSWORD_HREF);
|
||||
}
|
||||
|
||||
public UserResponse<PREV> assertPasswordLinkExists() {
|
||||
return assertPropertyPathExists(LINKS_PASSWORD_HREF);
|
||||
}
|
||||
|
||||
public ChangePasswordResponse<UserResponse> requestChangePassword(String newPassword) {
|
||||
return new ChangePasswordResponse<>(applyPUTRequestFromLink(super.response, LINKS_PASSWORD_HREF, VndMediaType.PASSWORD_OVERWRITE, createPasswordChangeJson(null, newPassword)), this);
|
||||
}
|
||||
|
||||
@@ -74,9 +74,27 @@ public class TestData {
|
||||
.append(" }").toString())
|
||||
.post(getUsersUrl())
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_CREATED)
|
||||
;
|
||||
.statusCode(HttpStatus.SC_CREATED);
|
||||
|
||||
if (isAdmin) {
|
||||
assignAdminPermissions(username);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assignAdminPermissions(String username) {
|
||||
LOG.info("assign admin permissions to user {}", username);
|
||||
given(VndMediaType.PERMISSION_COLLECTION)
|
||||
.when()
|
||||
.body("{'permissions': ['*']}".replaceAll("'", "\""))
|
||||
.put(getPermissionUrl(username))
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_NO_CONTENT);
|
||||
}
|
||||
|
||||
private static URI getPermissionUrl(String username) {
|
||||
return RestUtil.createResourceUrl(String.format("users/%s/permissions", username));
|
||||
}
|
||||
|
||||
public static void createGroup(String groupName, String desc) {
|
||||
LOG.info("create group with group name: {} and description {}", groupName, desc);
|
||||
given(VndMediaType.GROUP)
|
||||
|
||||
@@ -117,7 +117,6 @@ public final class MockUtil
|
||||
when(subject.isPermittedAll(anyCollectionOf(Permission.class))).thenReturn(
|
||||
Boolean.TRUE);
|
||||
when(subject.isPermittedAll()).thenReturn(Boolean.TRUE);
|
||||
when(subject.hasRole(Role.ADMIN)).thenReturn(Boolean.TRUE);
|
||||
when(subject.hasRole(Role.USER)).thenReturn(Boolean.TRUE);
|
||||
|
||||
PrincipalCollection collection = mock(PrincipalCollection.class);
|
||||
|
||||
@@ -63,4 +63,4 @@
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,4 @@
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,6 @@ export type Config = {
|
||||
disableGroupingGrid: boolean,
|
||||
dateFormat: string,
|
||||
anonymousAccessEnabled: boolean,
|
||||
adminGroups: string[],
|
||||
adminUsers: string[],
|
||||
baseUrl: string,
|
||||
forceBaseUrl: boolean,
|
||||
loginAttemptLimit: number,
|
||||
|
||||
@@ -10,6 +10,7 @@ export type Group = Collection & {
|
||||
name: string,
|
||||
description: string,
|
||||
type: string,
|
||||
external: boolean,
|
||||
members: string[],
|
||||
_embedded: {
|
||||
members: Member[]
|
||||
|
||||
@@ -6,7 +6,6 @@ export type User = {
|
||||
name: string,
|
||||
mail: string,
|
||||
password: string,
|
||||
admin: boolean,
|
||||
active: boolean,
|
||||
type?: string,
|
||||
creationDate?: string,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"creationDate": "Erstellt",
|
||||
"lastModified": "Zuletzt bearbeitet",
|
||||
"type": "Typ",
|
||||
"external": "Extern",
|
||||
"members": "Mitglieder"
|
||||
},
|
||||
"groups": {
|
||||
@@ -49,13 +50,15 @@
|
||||
},
|
||||
"groupForm": {
|
||||
"subtitle": "Gruppe bearbeiten",
|
||||
"externalSubtitle": "Externe Gruppe bearbeiten",
|
||||
"submit": "Speichern",
|
||||
"nameError": "Name ist ungültig",
|
||||
"descriptionError": "Beschreibung ist ungültig",
|
||||
"help": {
|
||||
"nameHelpText": "Eindeutiger Name der Gruppe",
|
||||
"descriptionHelpText": "Eine kurze Beschreibung der Gruppe",
|
||||
"memberHelpText": "Benutzername des Mitglieds der Gruppe"
|
||||
"memberHelpText": "Benutzername des Mitglieds der Gruppe",
|
||||
"externalHelpText": "Mitglieder dieser Gruppe werden von einem externen System wie z.B.: einem LDAP-Server verwaltet"
|
||||
}
|
||||
},
|
||||
"deleteGroup": {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"displayName": "Anzeigename",
|
||||
"mail": "E-Mail",
|
||||
"password": "Passwort",
|
||||
"admin": "Admin",
|
||||
"active": "Aktiv",
|
||||
"type": "Typ",
|
||||
"creationDate": "Erstellt",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"creationDate": "Creation Date",
|
||||
"lastModified": "Last Modified",
|
||||
"type": "Type",
|
||||
"external": "External",
|
||||
"members": "Members"
|
||||
},
|
||||
"groups": {
|
||||
@@ -49,13 +50,15 @@
|
||||
},
|
||||
"groupForm": {
|
||||
"subtitle": "Edit Group",
|
||||
"externalSubtitle": "Edit external group",
|
||||
"submit": "Submit",
|
||||
"nameError": "Group name is invalid",
|
||||
"descriptionError": "Description is invalid",
|
||||
"help": {
|
||||
"nameHelpText": "Unique name of the group",
|
||||
"descriptionHelpText": "A short description of the group",
|
||||
"memberHelpText": "Usernames of the group members"
|
||||
"memberHelpText": "Usernames of the group members",
|
||||
"externalHelpText": "Members are managed by an external system such as LDAP"
|
||||
}
|
||||
},
|
||||
"deleteGroup": {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"displayName": "Display Name",
|
||||
"mail": "E-Mail",
|
||||
"password": "Password",
|
||||
"admin": "Admin",
|
||||
"active": "Active",
|
||||
"type": "Type",
|
||||
"creationDate": "Creation Date",
|
||||
|
||||
@@ -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);
|
||||
@@ -6,7 +6,6 @@ import type { NamespaceStrategies, 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 = {
|
||||
@@ -46,8 +45,6 @@ class ConfigForm extends React.Component<Props, State> {
|
||||
disableGroupingGrid: false,
|
||||
dateFormat: "",
|
||||
anonymousAccessEnabled: false,
|
||||
adminGroups: [],
|
||||
adminUsers: [],
|
||||
baseUrl: "",
|
||||
forceBaseUrl: false,
|
||||
loginAttemptLimit: 0,
|
||||
@@ -155,15 +152,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}
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
MemberNameTable,
|
||||
InputField,
|
||||
SubmitButton,
|
||||
Textarea
|
||||
Textarea,
|
||||
Checkbox
|
||||
} from "@scm-manager/ui-components";
|
||||
import type { Group, SelectValue } from "@scm-manager/ui-types";
|
||||
|
||||
@@ -67,16 +68,68 @@ class GroupForm extends React.Component<Props, State> {
|
||||
submit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
if (this.isValid()) {
|
||||
this.props.submitForm(this.state.group);
|
||||
const { group } = this.state;
|
||||
if (group.external) {
|
||||
group.members = [];
|
||||
}
|
||||
this.props.submitForm(group);
|
||||
}
|
||||
};
|
||||
|
||||
renderMemberfields = (group: Group) => {
|
||||
if (group.external) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { loadUserSuggestions, t } = this.props;
|
||||
return (
|
||||
<>
|
||||
<LabelWithHelpIcon
|
||||
label={t("group.members")}
|
||||
helpText={t("groupForm.help.memberHelpText")}
|
||||
/>
|
||||
<MemberNameTable
|
||||
members={group.members}
|
||||
memberListChanged={this.memberListChanged}
|
||||
/>
|
||||
<AutocompleteAddEntryToTableField
|
||||
addEntry={this.addMember}
|
||||
disabled={false}
|
||||
buttonLabel={t("add-member-button.label")}
|
||||
fieldLabel={t("add-member-textfield.label")}
|
||||
errorMessage={t("add-member-textfield.error")}
|
||||
loadSuggestions={loadUserSuggestions}
|
||||
placeholder={t("add-member-autocomplete.placeholder")}
|
||||
loadingMessage={t("add-member-autocomplete.loading")}
|
||||
noOptionsMessage={t("add-member-autocomplete.no-options")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderExternalField = (group: Group) => {
|
||||
const { t } = this.props;
|
||||
if (this.isExistingGroup()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Checkbox
|
||||
label={t("group.external")}
|
||||
checked={group.external}
|
||||
helpText={t("groupForm.help.externalHelpText")}
|
||||
onChange={this.handleExternalChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
isExistingGroup = () => !! this.props.group;
|
||||
|
||||
render() {
|
||||
const { loading, t } = this.props;
|
||||
const { group } = this.state;
|
||||
let nameField = null;
|
||||
let subtitle = null;
|
||||
if (!this.props.group) {
|
||||
if (!this.isExistingGroup()) {
|
||||
// create new group
|
||||
nameField = (
|
||||
<InputField
|
||||
@@ -88,8 +141,9 @@ class GroupForm extends React.Component<Props, State> {
|
||||
helpText={t("groupForm.help.nameHelpText")}
|
||||
/>
|
||||
);
|
||||
} else if (group.external) {
|
||||
subtitle = <Subtitle subtitle={t("groupForm.externalSubtitle")} />;
|
||||
} else {
|
||||
// edit existing group
|
||||
subtitle = <Subtitle subtitle={t("groupForm.subtitle")} />;
|
||||
}
|
||||
|
||||
@@ -106,26 +160,8 @@ class GroupForm extends React.Component<Props, State> {
|
||||
validationError={false}
|
||||
helpText={t("groupForm.help.descriptionHelpText")}
|
||||
/>
|
||||
<LabelWithHelpIcon
|
||||
label={t("group.members")}
|
||||
helpText={t("groupForm.help.memberHelpText")}
|
||||
/>
|
||||
<MemberNameTable
|
||||
members={group.members}
|
||||
memberListChanged={this.memberListChanged}
|
||||
/>
|
||||
|
||||
<AutocompleteAddEntryToTableField
|
||||
addEntry={this.addMember}
|
||||
disabled={false}
|
||||
buttonLabel={t("add-member-button.label")}
|
||||
fieldLabel={t("add-member-textfield.label")}
|
||||
errorMessage={t("add-member-textfield.error")}
|
||||
loadSuggestions={this.props.loadUserSuggestions}
|
||||
placeholder={t("add-member-autocomplete.placeholder")}
|
||||
loadingMessage={t("add-member-autocomplete.loading")}
|
||||
noOptionsMessage={t("add-member-autocomplete.no-options")}
|
||||
/>
|
||||
{this.renderExternalField(group)}
|
||||
{this.renderMemberfields(group)}
|
||||
<SubmitButton
|
||||
disabled={!this.isValid()}
|
||||
label={t("groupForm.submit")}
|
||||
@@ -176,6 +212,12 @@ class GroupForm extends React.Component<Props, State> {
|
||||
group: { ...this.state.group, description }
|
||||
});
|
||||
};
|
||||
|
||||
handleExternalChange = (external: boolean) => {
|
||||
this.setState({
|
||||
group: { ...this.state.group, external }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("groups")(GroupForm);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import React from "react";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import GroupMember from "./GroupMember";
|
||||
import { DateFromNow } from "@scm-manager/ui-components";
|
||||
import { DateFromNow, Checkbox } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import injectSheet from "react-jss";
|
||||
|
||||
@@ -34,6 +34,12 @@ class Details extends React.Component<Props> {
|
||||
<th>{t("group.description")}</th>
|
||||
<td>{group.description}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{t("group.external")}</th>
|
||||
<td>
|
||||
<Checkbox checked={group.external} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{t("group.type")}</th>
|
||||
<td>{group.type}</td>
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
group: Group
|
||||
};
|
||||
|
||||
export default class GroupRow extends React.Component<Props> {
|
||||
renderLink(to: string, label: string) {
|
||||
return <Link to={to}>{label}</Link>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { group } = this.props;
|
||||
const to = `/group/${group.name}`;
|
||||
return (
|
||||
<tr>
|
||||
<td>{this.renderLink(to, group.name)}</td>
|
||||
<td className="is-hidden-mobile">{group.description}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import { Checkbox } from "@scm-manager/ui-components"
|
||||
|
||||
type Props = {
|
||||
group: Group
|
||||
};
|
||||
|
||||
export default class GroupRow extends React.Component<Props> {
|
||||
renderLink(to: string, label: string) {
|
||||
return <Link to={to}>{label}</Link>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { group } = this.props;
|
||||
const to = `/group/${group.name}`;
|
||||
return (
|
||||
<tr>
|
||||
<td>{this.renderLink(to, group.name)}</td>
|
||||
<td className="is-hidden-mobile">{group.description}</td>
|
||||
<td>
|
||||
<Checkbox checked={group.external} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import GroupRow from "./GroupRow";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
groups: Group[]
|
||||
};
|
||||
|
||||
class GroupTable extends React.Component<Props> {
|
||||
render() {
|
||||
const { groups, t } = this.props;
|
||||
return (
|
||||
<table className="card-table table is-hoverable is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("group.name")}</th>
|
||||
<th className="is-hidden-mobile">{t("group.description")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{groups.map((group, index) => {
|
||||
return <GroupRow key={index} group={group} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("groups")(GroupTable);
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import GroupRow from "./GroupRow";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
groups: Group[]
|
||||
};
|
||||
|
||||
class GroupTable extends React.Component<Props> {
|
||||
render() {
|
||||
const { groups, t } = this.props;
|
||||
return (
|
||||
<table className="card-table table is-hoverable is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("group.name")}</th>
|
||||
<th className="is-hidden-mobile">{t("group.description")}</th>
|
||||
<th>{t("group.external")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{groups.map((group, index) => {
|
||||
return <GroupRow key={index} group={group} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("groups")(GroupTable);
|
||||
|
||||
@@ -37,7 +37,6 @@ class UserForm extends React.Component<Props, State> {
|
||||
displayName: "",
|
||||
mail: "",
|
||||
password: "",
|
||||
admin: false,
|
||||
active: true,
|
||||
_links: {}
|
||||
},
|
||||
@@ -167,12 +166,6 @@ class UserForm extends React.Component<Props, State> {
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
{passwordChangeField}
|
||||
<Checkbox
|
||||
label={t("user.admin")}
|
||||
onChange={this.handleAdminChange}
|
||||
checked={user ? user.admin : false}
|
||||
helpText={t("help.adminHelpText")}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("user.active")}
|
||||
onChange={this.handleActiveChange}
|
||||
@@ -225,10 +218,6 @@ class UserForm extends React.Component<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
handleAdminChange = (admin: boolean) => {
|
||||
this.setState({ user: { ...this.state.user, admin } });
|
||||
};
|
||||
|
||||
handleActiveChange = (active: boolean) => {
|
||||
this.setState({ user: { ...this.state.user, active } });
|
||||
};
|
||||
|
||||
@@ -1,66 +1,60 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { User } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import { Checkbox, MailLink, DateFromNow } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
user: User,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class Details extends React.Component<Props> {
|
||||
render() {
|
||||
const { user, t } = this.props;
|
||||
return (
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.name")}</td>
|
||||
<td>{user.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.displayName")}</td>
|
||||
<td>{user.displayName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.mail")}</td>
|
||||
<td>
|
||||
<MailLink address={user.mail} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.admin")}</td>
|
||||
<td>
|
||||
<Checkbox checked={user.admin} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.active")}</td>
|
||||
<td>
|
||||
<Checkbox checked={user.active} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.type")}</td>
|
||||
<td>{user.type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.creationDate")}</td>
|
||||
<td>
|
||||
<DateFromNow date={user.creationDate} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.lastModified")}</td>
|
||||
<td>
|
||||
<DateFromNow date={user.lastModified} />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("users")(Details);
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { User } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import { Checkbox, MailLink, DateFromNow } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
user: User,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class Details extends React.Component<Props> {
|
||||
render() {
|
||||
const { user, t } = this.props;
|
||||
return (
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.name")}</td>
|
||||
<td>{user.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.displayName")}</td>
|
||||
<td>{user.displayName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.mail")}</td>
|
||||
<td>
|
||||
<MailLink address={user.mail} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.active")}</td>
|
||||
<td>
|
||||
<Checkbox checked={user.active} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.type")}</td>
|
||||
<td>{user.type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.creationDate")}</td>
|
||||
<td>
|
||||
<DateFromNow date={user.creationDate} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="has-text-weight-semibold">{t("user.lastModified")}</td>
|
||||
<td>
|
||||
<DateFromNow date={user.lastModified} />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("users")(Details);
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { User } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
user: User
|
||||
};
|
||||
|
||||
export default class UserRow extends React.Component<Props> {
|
||||
renderLink(to: string, label: string) {
|
||||
return <Link to={to}>{label}</Link>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
const to = `/user/${user.name}`;
|
||||
return (
|
||||
<tr>
|
||||
<td className="is-hidden-mobile">{this.renderLink(to, user.name)}</td>
|
||||
<td>{this.renderLink(to, user.displayName)}</td>
|
||||
<td>
|
||||
<a href={`mailto: ${user.mail}`}>{user.mail}</a>
|
||||
</td>
|
||||
<td className="is-hidden-mobile">
|
||||
<input type="checkbox" id="admin" checked={user.admin} readOnly />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { User } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
user: User
|
||||
};
|
||||
|
||||
export default class UserRow extends React.Component<Props> {
|
||||
renderLink(to: string, label: string) {
|
||||
return <Link to={to}>{label}</Link>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
const to = `/user/${user.name}`;
|
||||
return (
|
||||
<tr>
|
||||
<td className="is-hidden-mobile">{this.renderLink(to, user.name)}</td>
|
||||
<td>{this.renderLink(to, user.displayName)}</td>
|
||||
<td>
|
||||
<a href={`mailto: ${user.mail}`}>{user.mail}</a>
|
||||
</td>
|
||||
<td className="is-hidden-mobile">
|
||||
<input type="checkbox" id="active" checked={user.active} readOnly />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import UserRow from "./UserRow";
|
||||
import type { User } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
users: User[]
|
||||
};
|
||||
|
||||
;
|
||||
|
||||
class UserTable extends React.Component<Props> {
|
||||
render() {
|
||||
const { users, t } = this.props;
|
||||
return (
|
||||
<table className="card-table table is-hoverable is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="is-hidden-mobile">{t("user.name")}</th>
|
||||
<th>{t("user.displayName")}</th>
|
||||
<th>{t("user.mail")}</th>
|
||||
<th className="is-hidden-mobile">{t("user.admin")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((user, index) => {
|
||||
return <UserRow key={index} user={user} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("users")(UserTable);
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import UserRow from "./UserRow";
|
||||
import type { User } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
users: User[]
|
||||
};
|
||||
|
||||
;
|
||||
|
||||
class UserTable extends React.Component<Props> {
|
||||
render() {
|
||||
const { users, t } = this.props;
|
||||
return (
|
||||
<table className="card-table table is-hoverable is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="is-hidden-mobile">{t("user.name")}</th>
|
||||
<th>{t("user.displayName")}</th>
|
||||
<th>{t("user.mail")}</th>
|
||||
<th className="is-hidden-mobile">{t("user.active")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((user, index) => {
|
||||
return <UserRow key={index} user={user} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("users")(UserTable);
|
||||
|
||||
@@ -1,463 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.api.rest.resources;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.DisabledAccountException;
|
||||
import org.apache.shiro.authc.ExcessiveAttemptsException;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.ScmState;
|
||||
import sonia.scm.ScmStateFactory;
|
||||
import sonia.scm.api.rest.RestActionResult;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.security.Tokens;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import sonia.scm.security.AccessToken;
|
||||
import sonia.scm.security.AccessTokenBuilder;
|
||||
import sonia.scm.security.AccessTokenBuilderFactory;
|
||||
import sonia.scm.security.AccessTokenCookieIssuer;
|
||||
import sonia.scm.security.Scope;
|
||||
|
||||
/**
|
||||
* Authentication related RESTful Web Service endpoint.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Singleton
|
||||
@Path("auth")
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
public class AuthenticationResource
|
||||
{
|
||||
|
||||
/** the logger for AuthenticationResource */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(AuthenticationResource.class);
|
||||
|
||||
//~--- constant enums -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Enum description
|
||||
*
|
||||
*/
|
||||
private static enum WUIAuthenticationFailure { LOCKED, TEMPORARY_LOCKED,
|
||||
WRONG_CREDENTIALS; }
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param configuration
|
||||
* @param stateFactory
|
||||
* @param tokenBuilderFactory
|
||||
* @param cookieIssuer
|
||||
*/
|
||||
@Inject
|
||||
public AuthenticationResource(ScmConfiguration configuration,
|
||||
ScmStateFactory stateFactory, AccessTokenBuilderFactory tokenBuilderFactory, AccessTokenCookieIssuer cookieIssuer)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
this.stateFactory = stateFactory;
|
||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||
this.cookieIssuer = cookieIssuer;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Authenticate a user and return the state of the application.
|
||||
*
|
||||
* @param request current http request
|
||||
* @param response current http response
|
||||
* @param grantType grant type, currently only password is supported
|
||||
* @param username the username for the authentication
|
||||
* @param password the password for the authentication
|
||||
* @param cookie create authentication token
|
||||
* @param scope scope of created token
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@POST
|
||||
@Path("access_token")
|
||||
@TypeHint(ScmState.class)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 400, condition = "bad request, required parameter is missing"),
|
||||
@ResponseCode(code = 401, condition = "unauthorized, the specified username or password is wrong"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response authenticate(
|
||||
@Context HttpServletRequest request,
|
||||
@Context HttpServletResponse response,
|
||||
@FormParam("grant_type") String grantType,
|
||||
@FormParam("username") String username,
|
||||
@FormParam("password") String password,
|
||||
@FormParam("cookie") boolean cookie,
|
||||
@FormParam("scope") List<String> scope)
|
||||
{
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(grantType), "grant_type parameter is required");
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(username), "username parameter is required");
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(password), "password parameter is required");
|
||||
|
||||
Response res;
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
try
|
||||
{
|
||||
subject.login(Tokens.createAuthenticationToken(request, username, password));
|
||||
|
||||
AccessTokenBuilder tokenBuilder = tokenBuilderFactory.create();
|
||||
if ( scope != null ) {
|
||||
tokenBuilder.scope(Scope.valueOf(scope));
|
||||
}
|
||||
AccessToken token = tokenBuilder.build();
|
||||
|
||||
ScmState state;
|
||||
|
||||
if (cookie) {
|
||||
cookieIssuer.authenticate(request, response, token);
|
||||
state = stateFactory.createState(subject);
|
||||
} else {
|
||||
state = stateFactory.createState(subject, token.compact());
|
||||
}
|
||||
|
||||
res = Response.ok(state).build();
|
||||
}
|
||||
catch (DisabledAccountException ex)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(
|
||||
"authentication failed, account user ".concat(username).concat(
|
||||
" is locked"), ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("authentication failed, account {} is locked", username);
|
||||
}
|
||||
|
||||
res = handleFailedAuthentication(request, ex, Response.Status.FORBIDDEN,
|
||||
WUIAuthenticationFailure.LOCKED);
|
||||
}
|
||||
catch (ExcessiveAttemptsException ex)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(
|
||||
"authentication failed, account user ".concat(username).concat(
|
||||
" is temporary locked"), ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("authentication failed, account {} is temporary locked", username);
|
||||
}
|
||||
|
||||
res = handleFailedAuthentication(request, ex, Response.Status.FORBIDDEN,
|
||||
WUIAuthenticationFailure.TEMPORARY_LOCKED);
|
||||
}
|
||||
catch (AuthenticationException ex)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("authentication failed for user ".concat(username), ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("authentication failed for user {}", username);
|
||||
}
|
||||
|
||||
res = handleFailedAuthentication(request, ex, Response.Status.UNAUTHORIZED,
|
||||
WUIAuthenticationFailure.WRONG_CREDENTIALS);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout the current user. Returns the current state of the application, if public access is enabled.
|
||||
*
|
||||
* @param request the current http request
|
||||
* @param response the current http response
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@Path("logout")
|
||||
@TypeHint(ScmState.class)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response logout(@Context HttpServletRequest request, @Context HttpServletResponse response)
|
||||
{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
subject.logout();
|
||||
|
||||
// remove authentication cookie
|
||||
cookieIssuer.invalidate(request, response);
|
||||
|
||||
Response resp;
|
||||
|
||||
if (configuration.isAnonymousAccessEnabled())
|
||||
{
|
||||
resp = Response.ok(stateFactory.createAnonymousState()).build();
|
||||
}
|
||||
else
|
||||
{
|
||||
resp = Response.ok().build();
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This method is an alias of the {@link #getState(HttpServletRequest)} method.
|
||||
* The only difference between the methods, is that this one could not be used with basic authentication.
|
||||
*
|
||||
* @param request the current http request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@Path("state")
|
||||
@TypeHint(ScmState.class)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 401, condition = "unauthorized, user is not authenticated and public access is disabled"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response getCurrentState(@Context HttpServletRequest request)
|
||||
{
|
||||
return getState(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the application.
|
||||
*
|
||||
* @param request the current http request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@TypeHint(ScmState.class)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 401, condition = "unauthorized, user is not authenticated and public access is disabled"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response getState(@Context HttpServletRequest request)
|
||||
{
|
||||
Response response;
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject.isAuthenticated() || subject.isRemembered())
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
String auth = subject.isRemembered()
|
||||
? "remembered"
|
||||
: "authenticated";
|
||||
|
||||
logger.debug("return state for {} user {}", auth,
|
||||
subject.getPrincipal());
|
||||
}
|
||||
|
||||
ScmState state = stateFactory.createState(subject);
|
||||
|
||||
response = Response.ok(state).build();
|
||||
}
|
||||
else if (configuration.isAnonymousAccessEnabled())
|
||||
{
|
||||
|
||||
response = Response.ok(stateFactory.createAnonymousState()).build();
|
||||
}
|
||||
else
|
||||
{
|
||||
response = Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param ex
|
||||
* @param status
|
||||
* @param failure
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Response handleFailedAuthentication(HttpServletRequest request,
|
||||
AuthenticationException ex, Response.Status status,
|
||||
WUIAuthenticationFailure failure)
|
||||
{
|
||||
Response response;
|
||||
|
||||
if (HttpUtil.isWUIRequest(request))
|
||||
{
|
||||
response = Response.ok(new WUIAuthenticationFailedResult(failure,
|
||||
ex.getMessage())).build();
|
||||
}
|
||||
else
|
||||
{
|
||||
response = Response.status(status).build();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 13/09/28
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
@XmlRootElement(name = "result")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
private static final class WUIAuthenticationFailedResult
|
||||
extends RestActionResult
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param failure
|
||||
* @param mesage
|
||||
*/
|
||||
public WUIAuthenticationFailedResult(WUIAuthenticationFailure failure,
|
||||
String mesage)
|
||||
{
|
||||
super(false);
|
||||
this.failure = failure;
|
||||
this.mesage = mesage;
|
||||
}
|
||||
|
||||
//~--- get methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public WUIAuthenticationFailure getFailure()
|
||||
{
|
||||
return failure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getMesage()
|
||||
{
|
||||
return mesage;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final WUIAuthenticationFailure failure;
|
||||
|
||||
/** Field description */
|
||||
private final String mesage;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
/** Field description */
|
||||
private final ScmStateFactory stateFactory;
|
||||
|
||||
/** Field description */
|
||||
private final AccessTokenBuilderFactory tokenBuilderFactory;
|
||||
|
||||
/** Field description */
|
||||
private final AccessTokenCookieIssuer cookieIssuer;
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.api.rest.resources;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
||||
import sonia.scm.security.CipherUtil;
|
||||
import sonia.scm.security.Role;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
* Rest resource to encrypt values.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.41
|
||||
*/
|
||||
@Path("security/cipher")
|
||||
public class CipherResource
|
||||
{
|
||||
|
||||
/**
|
||||
* Encrypts the request body and returns an encrypted string. This method can
|
||||
* only executed with administration privileges.
|
||||
*
|
||||
* @param value value to encrypt
|
||||
*
|
||||
* @return unique key
|
||||
*/
|
||||
@POST
|
||||
@Path("encrypt")
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String encrypt(String value)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(value),
|
||||
"value is required");
|
||||
|
||||
return CipherUtil.getInstance().encode(value);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.api.rest.resources;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.security.Role;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
* Rest resource to generate unique keys.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.41
|
||||
*/
|
||||
@Path("security/key")
|
||||
public class KeyResource
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new KeyResource.
|
||||
*
|
||||
*
|
||||
* @param keyGenerator key generator
|
||||
*/
|
||||
@Inject
|
||||
public KeyResource(KeyGenerator keyGenerator)
|
||||
{
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates a unique key. <strong>Note:</strong> This method can only executed with administration privileges.
|
||||
*
|
||||
* @return unique key
|
||||
*/
|
||||
@GET
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String generateKey()
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
|
||||
return keyGenerator.createKey();
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** key generator */
|
||||
private final KeyGenerator keyGenerator;
|
||||
}
|
||||
@@ -42,24 +42,43 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.FeatureNotSupportedException;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.Type;
|
||||
import sonia.scm.api.rest.RestActionUploadResult;
|
||||
import sonia.scm.api.v2.resources.RepositoryResource;
|
||||
import sonia.scm.repository.*;
|
||||
import sonia.scm.repository.AdvancedImportHandler;
|
||||
import sonia.scm.repository.ImportHandler;
|
||||
import sonia.scm.repository.ImportResult;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryHandler;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.repository.RepositoryType;
|
||||
import sonia.scm.repository.api.Command;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.repository.api.UnbundleCommandBuilder;
|
||||
import sonia.scm.security.Role;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.*;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.GenericEntity;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
@@ -233,7 +252,7 @@ public class RepositoryImportResource
|
||||
public Response importFromUrl(@Context UriInfo uriInfo,
|
||||
@PathParam("type") String type, UrlImportRequest request)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
RepositoryPermissions.create().check();
|
||||
checkNotNull(request, "request is required");
|
||||
checkArgument(!Strings.isNullOrEmpty(request.getName()),
|
||||
"request does not contain name of the repository");
|
||||
@@ -288,7 +307,7 @@ public class RepositoryImportResource
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
public Response importRepositories(@PathParam("type") String type)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
RepositoryPermissions.create().check();
|
||||
|
||||
List<Repository> repositories = new ArrayList<Repository>();
|
||||
|
||||
@@ -320,7 +339,7 @@ public class RepositoryImportResource
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
public Response importRepositories()
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
RepositoryPermissions.create().check();
|
||||
|
||||
logger.info("start directory import for all supported repository types");
|
||||
|
||||
@@ -363,7 +382,7 @@ public class RepositoryImportResource
|
||||
public Response importRepositoriesFromDirectory(
|
||||
@PathParam("type") String type)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
RepositoryPermissions.create().check();
|
||||
|
||||
Response response;
|
||||
|
||||
@@ -438,7 +457,7 @@ public class RepositoryImportResource
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
public Response getImportableTypes()
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
RepositoryPermissions.create().check();
|
||||
|
||||
List<Type> types = findImportableTypes();
|
||||
|
||||
@@ -537,7 +556,7 @@ public class RepositoryImportResource
|
||||
private Repository doImportFromBundle(String type, String name,
|
||||
InputStream inputStream, boolean compressed)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
RepositoryPermissions.create().check();
|
||||
|
||||
checkArgument(!Strings.isNullOrEmpty(name),
|
||||
"request does not contain name of the repository");
|
||||
|
||||
@@ -1,463 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.api.rest.resources;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.ServletContainerDetector;
|
||||
import sonia.scm.Type;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.plugin.PluginManager;
|
||||
import sonia.scm.repository.RepositoryHandler;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.security.Role;
|
||||
import sonia.scm.security.ScmSecurityException;
|
||||
import sonia.scm.util.SystemUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import sonia.scm.template.Viewable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Path("support")
|
||||
public class SupportResource
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String TEMPLATE = "/templates/support.mustache";
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param securityContext
|
||||
* @param context
|
||||
* @param templateHandler
|
||||
* @param configuration
|
||||
* @param pluginManager
|
||||
* @param storeFactory
|
||||
* @param repositoryManager
|
||||
* @param request
|
||||
*/
|
||||
@Inject
|
||||
public SupportResource(SCMContextProvider context,
|
||||
ScmConfiguration configuration, PluginManager pluginManager,
|
||||
ConfigurationStoreFactory storeFactory, RepositoryManager repositoryManager,
|
||||
HttpServletRequest request)
|
||||
{
|
||||
this.context = context;
|
||||
this.configuration = configuration;
|
||||
this.pluginManager = pluginManager;
|
||||
this.storeFactoryClass = storeFactory.getClass();
|
||||
this.repositoryManager = repositoryManager;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
public Viewable getSupport() throws IOException
|
||||
{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (!subject.hasRole(Role.ADMIN))
|
||||
{
|
||||
throw new ScmSecurityException("admin privileges required");
|
||||
}
|
||||
|
||||
Map<String, Object> env = Maps.newHashMap();
|
||||
|
||||
env.put("version", new VersionInformation(context, storeFactoryClass));
|
||||
env.put("configuration", configuration);
|
||||
env.put("pluginManager", pluginManager);
|
||||
env.put("runtime", new RuntimeInformation());
|
||||
env.put("system", new SystemInformation(request));
|
||||
env.put("repositoryHandlers", getRepositoryHandlers());
|
||||
|
||||
return new Viewable(TEMPLATE, env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private List<RepositoryHandler> getRepositoryHandlers()
|
||||
{
|
||||
List<RepositoryHandler> handlers = Lists.newArrayList();
|
||||
|
||||
for (Type type : repositoryManager.getConfiguredTypes())
|
||||
{
|
||||
handlers.add(repositoryManager.getHandler(type.getName()));
|
||||
}
|
||||
|
||||
return handlers;
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 12/04/30
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
public static class RuntimeInformation
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
public RuntimeInformation()
|
||||
{
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
totalMemory = runtime.totalMemory();
|
||||
freeMemory = runtime.freeMemory();
|
||||
maxMemory = runtime.maxMemory();
|
||||
availableProcessors = runtime.availableProcessors();
|
||||
}
|
||||
|
||||
//~--- get methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getAvailableProcessors()
|
||||
{
|
||||
return availableProcessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getFreeMemory()
|
||||
{
|
||||
return freeMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getMaxMemory()
|
||||
{
|
||||
return maxMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getTotalMemory()
|
||||
{
|
||||
return totalMemory;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private int availableProcessors;
|
||||
|
||||
/** Field description */
|
||||
private long freeMemory;
|
||||
|
||||
/** Field description */
|
||||
private long maxMemory;
|
||||
|
||||
/** Field description */
|
||||
private long totalMemory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 12/04/30
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
public static class SystemInformation
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public SystemInformation(HttpServletRequest request)
|
||||
{
|
||||
os = SystemUtil.getOS();
|
||||
arch = SystemUtil.getArch();
|
||||
container = ServletContainerDetector.detect(request).name();
|
||||
java = System.getProperty("java.vendor").concat("/").concat(
|
||||
System.getProperty("java.version"));
|
||||
locale = Locale.getDefault().toString();
|
||||
timeZone = TimeZone.getDefault().getID();
|
||||
}
|
||||
|
||||
//~--- get methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getArch()
|
||||
{
|
||||
return arch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getContainer()
|
||||
{
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getJava()
|
||||
{
|
||||
return java;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLocale()
|
||||
{
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getOs()
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getTimeZone()
|
||||
{
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private String arch;
|
||||
|
||||
/** Field description */
|
||||
private String container;
|
||||
|
||||
/** Field description */
|
||||
private String java;
|
||||
|
||||
/** Field description */
|
||||
private String locale;
|
||||
|
||||
/** Field description */
|
||||
private String os;
|
||||
|
||||
/** Field description */
|
||||
private String timeZone;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 12/04/30
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
public static class VersionInformation
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param storeFactoryClass
|
||||
*/
|
||||
public VersionInformation(SCMContextProvider context,
|
||||
Class<?> storeFactoryClass)
|
||||
{
|
||||
version = context.getVersion();
|
||||
stage = context.getStage().name();
|
||||
storeFactory = storeFactoryClass.getName();
|
||||
}
|
||||
|
||||
//~--- get methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getStage()
|
||||
{
|
||||
return stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getStoreFactory()
|
||||
{
|
||||
return storeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private String stage;
|
||||
|
||||
/** Field description */
|
||||
private String storeFactory;
|
||||
|
||||
/** Field description */
|
||||
private String version;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private ScmConfiguration configuration;
|
||||
|
||||
/** Field description */
|
||||
private SCMContextProvider context;
|
||||
|
||||
/** Field description */
|
||||
private PluginManager pluginManager;
|
||||
|
||||
/** Field description */
|
||||
private RepositoryManager repositoryManager;
|
||||
|
||||
/** Field description */
|
||||
private HttpServletRequest request;
|
||||
|
||||
/** Field description */
|
||||
private Class<?> storeFactoryClass;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -27,6 +27,7 @@ public class GroupDto extends HalRepresentation {
|
||||
private String type;
|
||||
private Map<String, String> properties;
|
||||
private List<String> members;
|
||||
private boolean external;
|
||||
|
||||
GroupDto(Links links, Embedded embedded) {
|
||||
super(links, embedded);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.shiro.authc.credential.PasswordService;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.security.PermissionAssigner;
|
||||
import sonia.scm.security.PermissionDescriptor;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.web.security.AdministrationContext;
|
||||
import sonia.scm.web.security.PrivilegedAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import java.util.Collections;
|
||||
|
||||
@Extension
|
||||
public class SetupContextListener implements ServletContextListener {
|
||||
|
||||
private final AdministrationContext administrationContext;
|
||||
|
||||
@Inject
|
||||
public SetupContextListener(AdministrationContext administrationContext) {
|
||||
this.administrationContext = administrationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
administrationContext.runAsAdmin(SetupAction.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {}
|
||||
|
||||
@VisibleForTesting
|
||||
static class SetupAction implements PrivilegedAction {
|
||||
|
||||
private final UserManager userManager;
|
||||
private final PasswordService passwordService;
|
||||
private final PermissionAssigner permissionAssigner;
|
||||
|
||||
@Inject
|
||||
public SetupAction(UserManager userManager, PasswordService passwordService, PermissionAssigner permissionAssigner) {
|
||||
this.userManager = userManager;
|
||||
this.passwordService = passwordService;
|
||||
this.permissionAssigner = permissionAssigner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (isFirstStart()) {
|
||||
createAdminAccount();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFirstStart() {
|
||||
return userManager.getAll().isEmpty();
|
||||
}
|
||||
|
||||
private void createAdminAccount() {
|
||||
User scmadmin = new User("scmadmin", "SCM Administrator", "scm-admin@scm-manager.org");
|
||||
String password = passwordService.encryptPassword("scmadmin");
|
||||
scmadmin.setPassword(password);
|
||||
userManager.create(scmadmin);
|
||||
|
||||
PermissionDescriptor descriptor = new PermissionDescriptor("*");
|
||||
permissionAssigner.setPermissionsForUser("scmadmin", Collections.singleton(descriptor));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import com.google.common.collect.Multimap;
|
||||
import com.google.inject.Singleton;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.security.Role;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -63,7 +64,8 @@ public final class DebugService
|
||||
* Returns the last received hook data for the given repository.
|
||||
*/
|
||||
public DebugHookData getLast(NamespaceAndName namespaceAndName){
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
// debug permission does not exists, so only accounts with "*" permission can use these resource
|
||||
SecurityUtils.getSubject().checkPermission("debug");
|
||||
DebugHookData hookData = null;
|
||||
Collection<DebugHookData> receivedHookData = receivedHooks.get(namespaceAndName);
|
||||
if (receivedHookData != null && ! receivedHookData.isEmpty()){
|
||||
@@ -76,7 +78,8 @@ public final class DebugService
|
||||
* Returns all received hook data for the given repository.
|
||||
*/
|
||||
public Collection<DebugHookData> getAll(NamespaceAndName namespaceAndName){
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
// debug permission does not exists, so only accounts with "*" permission can use these resource
|
||||
SecurityUtils.getSubject().checkPermission("debug");
|
||||
return receivedHooks.get(namespaceAndName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,15 +49,15 @@ import sonia.scm.user.UserEvent;
|
||||
import sonia.scm.user.UserModificationEvent;
|
||||
|
||||
/**
|
||||
* Receives all kinds of events, which affects authorization relevant data and fires an
|
||||
* Receives all kinds of events, which affects authorization relevant data and fires an
|
||||
* {@link AuthorizationChangedEvent} if authorization data has changed.
|
||||
*
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.52
|
||||
*/
|
||||
@EagerSingleton
|
||||
public class AuthorizationChangedEventProducer {
|
||||
|
||||
|
||||
/**
|
||||
* the logger for AuthorizationChangedEventProducer
|
||||
*/
|
||||
@@ -68,7 +68,7 @@ public class AuthorizationChangedEventProducer {
|
||||
*/
|
||||
public AuthorizationChangedEventProducer() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invalidates the cache of a user which was modified. The cache entries for the user will be invalidated for the
|
||||
* following reasons:
|
||||
@@ -90,11 +90,11 @@ public class AuthorizationChangedEventProducer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isModificationEvent(HandlerEvent<?> event) {
|
||||
return event instanceof ModificationHandlerEvent;
|
||||
}
|
||||
|
||||
|
||||
private void handleUserEvent(UserEvent event) {
|
||||
String username = event.getItem().getName();
|
||||
logger.debug(
|
||||
@@ -102,26 +102,26 @@ public class AuthorizationChangedEventProducer {
|
||||
);
|
||||
fireEventForUser(username);
|
||||
}
|
||||
|
||||
|
||||
private void handleUserModificationEvent(UserModificationEvent event) {
|
||||
String username = event.getItem().getId();
|
||||
User beforeModification = event.getItemBeforeModification();
|
||||
if (isAuthorizationDataModified(event.getItem(), beforeModification)) {
|
||||
logger.debug(
|
||||
"fire authorization changed event for user {}, because of a authorization relevant field has changed",
|
||||
"fire authorization changed event for user {}, because of a authorization relevant field has changed",
|
||||
username
|
||||
);
|
||||
fireEventForUser(username);
|
||||
} else {
|
||||
logger.debug(
|
||||
"authorization changed event for user {} is not fired, because no authorization relevant field has changed",
|
||||
"authorization changed event for user {} is not fired, because no authorization relevant field has changed",
|
||||
username
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAuthorizationDataModified(User user, User beforeModification) {
|
||||
return user.isAdmin() != beforeModification.isAdmin() || user.isActive() != beforeModification.isActive();
|
||||
return user.isActive() != beforeModification.isActive();
|
||||
}
|
||||
|
||||
private void fireEventForUser(String username) {
|
||||
@@ -148,7 +148,7 @@ public class AuthorizationChangedEventProducer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleRepositoryModificationEvent(RepositoryModificationEvent event) {
|
||||
Repository repository = event.getItem();
|
||||
if (isAuthorizationDataModified(repository, event.getItemBeforeModification())) {
|
||||
@@ -169,14 +169,14 @@ public class AuthorizationChangedEventProducer {
|
||||
|| repository.isPublicReadable() != beforeModification.isPublicReadable()
|
||||
|| !(repository.getPermissions().containsAll(beforeModification.getPermissions()) && beforeModification.getPermissions().containsAll(repository.getPermissions()));
|
||||
}
|
||||
|
||||
|
||||
private void fireEventForEveryUser() {
|
||||
sendEvent(AuthorizationChangedEvent.createForEveryUser());
|
||||
}
|
||||
|
||||
|
||||
private void handleRepositoryEvent(RepositoryEvent event){
|
||||
logger.debug(
|
||||
"fire authorization changed event, because of received {} event for repository {}",
|
||||
"fire authorization changed event, because of received {} event for repository {}",
|
||||
event.getEventType(), event.getItem().getName()
|
||||
);
|
||||
fireEventForEveryUser();
|
||||
@@ -199,7 +199,7 @@ public class AuthorizationChangedEventProducer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleGroupPermissionChange(AssignedPermission permission) {
|
||||
logger.debug(
|
||||
"fire authorization changed event for group {}, because permission {} has changed",
|
||||
@@ -207,13 +207,13 @@ public class AuthorizationChangedEventProducer {
|
||||
);
|
||||
fireEventForEveryUser();
|
||||
}
|
||||
|
||||
|
||||
private void handleUserPermissionChange(AssignedPermission permission) {
|
||||
logger.debug(
|
||||
"fire authorization changed event for user {}, because permission {} has changed",
|
||||
permission.getName(), permission.getPermission()
|
||||
);
|
||||
fireEventForUser(permission.getName());
|
||||
fireEventForUser(permission.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,7 +230,7 @@ public class AuthorizationChangedEventProducer {
|
||||
public void onEvent(GroupEvent event) {
|
||||
if (event.getEventType().isPost()) {
|
||||
if (isModificationEvent(event)) {
|
||||
handleGroupModificationEvent((GroupModificationEvent) event);
|
||||
handleGroupModificationEvent((GroupModificationEvent) event);
|
||||
} else {
|
||||
handleGroupEvent(event);
|
||||
}
|
||||
@@ -244,28 +244,28 @@ public class AuthorizationChangedEventProducer {
|
||||
fireEventForEveryUser();
|
||||
} else {
|
||||
logger.debug(
|
||||
"authorization changed event is not fired, because non relevant field of group {} has changed",
|
||||
"authorization changed event is not fired, because non relevant field of group {} has changed",
|
||||
group.getId()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isAuthorizationDataModified(Group group, Group beforeModification) {
|
||||
return !group.getMembers().equals(beforeModification.getMembers());
|
||||
}
|
||||
|
||||
|
||||
private void handleGroupEvent(GroupEvent event){
|
||||
logger.debug(
|
||||
"fire authorization changed event, because of received group event {} for group {}",
|
||||
event.getEventType(),
|
||||
"fire authorization changed event, because of received group event {} for group {}",
|
||||
event.getEventType(),
|
||||
event.getItem().getId()
|
||||
);
|
||||
fireEventForEveryUser();
|
||||
fireEventForEveryUser();
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
protected void sendEvent(AuthorizationChangedEvent event) {
|
||||
ScmEventBus.getInstance().post(event);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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,74 +226,22 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
}
|
||||
}
|
||||
|
||||
private AuthorizationInfo createAuthorizationInfo(User user,
|
||||
GroupNames groups)
|
||||
{
|
||||
Set<String> roles;
|
||||
Set<String> permissions;
|
||||
private AuthorizationInfo createAuthorizationInfo(User user, GroupNames groups) {
|
||||
Builder<String> builder = ImmutableSet.builder();
|
||||
|
||||
if (isAdmin(user, groups))
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("grant admin role for user {}", user.getName());
|
||||
}
|
||||
collectGlobalPermissions(builder, user, groups);
|
||||
collectRepositoryPermissions(builder, user, groups);
|
||||
builder.add(canReadOwnUser(user));
|
||||
builder.add(getUserAutocompletePermission());
|
||||
builder.add(getGroupAutocompletePermission());
|
||||
builder.add(getChangeOwnPasswordPermission(user));
|
||||
|
||||
roles = ImmutableSet.of(Role.USER, Role.ADMIN);
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(ImmutableSet.of(Role.USER));
|
||||
info.addStringPermissions(builder.build());
|
||||
|
||||
permissions = ImmutableSet.of(ADMIN_PERMISSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
roles = ImmutableSet.of(Role.USER);
|
||||
|
||||
Builder<String> builder = ImmutableSet.builder();
|
||||
|
||||
collectGlobalPermissions(builder, user, groups);
|
||||
collectRepositoryPermissions(builder, user, groups);
|
||||
builder.add(canReadOwnUser(user));
|
||||
builder.add(getUserAutocompletePermission());
|
||||
builder.add(getGroupAutocompletePermission());
|
||||
builder.add(getChangeOwnPasswordPermission(user));
|
||||
permissions = builder.build();
|
||||
}
|
||||
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
|
||||
info.addStringPermissions(permissions);
|
||||
return info;
|
||||
}
|
||||
|
||||
private boolean isAdmin(User user, GroupNames groups) {
|
||||
boolean admin = user.isAdmin();
|
||||
if (admin) {
|
||||
logger.debug("user {} is marked as admin, because of the user flag", user.getName());
|
||||
return true;
|
||||
}
|
||||
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();
|
||||
}
|
||||
@@ -404,8 +345,6 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
/** authorization cache */
|
||||
private final Cache<CacheKey, AuthorizationInfo> cache;
|
||||
|
||||
|
||||
@@ -71,13 +71,6 @@ import java.util.List;
|
||||
public class DefaultUserManager extends AbstractUserManager
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String ADMIN_PATH = "/sonia/scm/config/admin-account.xml";
|
||||
|
||||
/** Field description */
|
||||
public static final String ANONYMOUS_PATH =
|
||||
"/sonia/scm/config/anonymous-account.xml";
|
||||
|
||||
/** Field description */
|
||||
public static final String STORE_NAME = "users";
|
||||
|
||||
@@ -173,12 +166,6 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
@Override
|
||||
public void init(SCMContextProvider context)
|
||||
{
|
||||
|
||||
// create default account only, if no other account is available
|
||||
if (userDAO.getAll().isEmpty())
|
||||
{
|
||||
createDefaultAccounts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -457,28 +444,6 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
private void createDefaultAccounts()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.info("create default accounts");
|
||||
|
||||
JAXBContext context = JAXBContext.newInstance(User.class);
|
||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
|
||||
createDefaultAccount(unmarshaller, ADMIN_PATH);
|
||||
createDefaultAccount(unmarshaller, ANONYMOUS_PATH);
|
||||
}
|
||||
catch (JAXBException ex)
|
||||
{
|
||||
logger.error("could not create default accounts", ex);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
private final UserDAO userDAO;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package sonia.scm.web.security;
|
||||
|
||||
final class AdministrationContextMarker {
|
||||
|
||||
static final AdministrationContextMarker MARKER = new AdministrationContextMarker();
|
||||
|
||||
private AdministrationContextMarker() {}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package sonia.scm.web.security;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.security.Role;
|
||||
|
||||
@Extension
|
||||
public class AdministrationContextRealm extends AuthorizingRealm {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AdministrationContextRealm.class);
|
||||
|
||||
public AdministrationContextRealm() {
|
||||
setName(DefaultAdministrationContext.REALM);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
|
||||
AdministrationContextMarker marker = principals.oneByType(AdministrationContextMarker.class);
|
||||
if (marker == AdministrationContextMarker.MARKER) {
|
||||
LOG.info("assign admin permissions to admin context user {}", principals.getPrimaryPrincipal());
|
||||
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(Sets.newHashSet(Role.USER));
|
||||
authorizationInfo.setStringPermissions(Sets.newHashSet("*"));
|
||||
return authorizationInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
|
||||
// we make no authentication we do only authorization
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class DefaultAdministrationContext implements AdministrationContext
|
||||
"/sonia/scm/web/security/system-account.xml";
|
||||
|
||||
/** Field description */
|
||||
private static final String REALM = "AdminRealm";
|
||||
static final String REALM = "AdminRealm";
|
||||
|
||||
/** the logger for DefaultAdministrationContext */
|
||||
private static final Logger logger =
|
||||
@@ -124,19 +124,7 @@ public class DefaultAdministrationContext implements AdministrationContext
|
||||
|
||||
if (ThreadContext.getSecurityManager() != null)
|
||||
{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject.hasRole(Role.ADMIN))
|
||||
{
|
||||
logger.debug(
|
||||
"user is already an admin, we need no system account session, execute action {}",
|
||||
action.getClass().getName());
|
||||
action.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
doRunAsInWebSessionContext(action);
|
||||
}
|
||||
doRunAsInWebSessionContext(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -174,6 +162,7 @@ public class DefaultAdministrationContext implements AdministrationContext
|
||||
collection.add(adminUser.getId(), REALM);
|
||||
collection.add(adminUser, REALM);
|
||||
collection.add(new GroupNames(), REALM);
|
||||
collection.add(AdministrationContextMarker.MARKER, REALM);
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -125,27 +125,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());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.shiro.authc.credential.PasswordService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.security.PermissionAssigner;
|
||||
import sonia.scm.security.PermissionDescriptor;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.user.UserTestData;
|
||||
import sonia.scm.web.security.AdministrationContext;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SetupContextListenerTest {
|
||||
|
||||
@Mock
|
||||
private AdministrationContext administrationContext;
|
||||
|
||||
@InjectMocks
|
||||
private SetupContextListener setupContextListener;
|
||||
|
||||
@Mock
|
||||
private UserManager userManager;
|
||||
|
||||
@Mock
|
||||
private PasswordService passwordService;
|
||||
|
||||
@Mock
|
||||
private PermissionAssigner permissionAssigner;
|
||||
|
||||
@InjectMocks
|
||||
private SetupContextListener.SetupAction setupAction;
|
||||
|
||||
@BeforeEach
|
||||
void setupObjectUnderTest() {
|
||||
doAnswer(ic -> {
|
||||
setupAction.run();
|
||||
return null;
|
||||
}).when(administrationContext).runAsAdmin(SetupContextListener.SetupAction.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateAdminAccountAndAssignPermissions() {
|
||||
when(passwordService.encryptPassword("scmadmin")).thenReturn("secret");
|
||||
|
||||
setupContextListener.contextInitialized(null);
|
||||
|
||||
verifyAdminCreated();
|
||||
verifyAdminPermissionsAssigned();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDoNothingOnSecondStart() {
|
||||
List<User> users = Lists.newArrayList(UserTestData.createTrillian());
|
||||
when(userManager.getAll()).thenReturn(users);
|
||||
|
||||
setupContextListener.contextInitialized(null);
|
||||
|
||||
verify(userManager, never()).create(any(User.class));
|
||||
verify(permissionAssigner, never()).setPermissionsForUser(anyString(), any(Collection.class));
|
||||
}
|
||||
|
||||
private void verifyAdminPermissionsAssigned() {
|
||||
ArgumentCaptor<String> usernameCaptor = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<Collection<PermissionDescriptor>> permissionCaptor = ArgumentCaptor.forClass(Collection.class);
|
||||
verify(permissionAssigner).setPermissionsForUser(usernameCaptor.capture(), permissionCaptor.capture());
|
||||
String username = usernameCaptor.getValue();
|
||||
assertThat(username).isEqualTo("scmadmin");
|
||||
PermissionDescriptor descriptor = permissionCaptor.getValue().iterator().next();
|
||||
assertThat(descriptor.getValue()).isEqualTo("*");
|
||||
}
|
||||
|
||||
private void verifyAdminCreated() {
|
||||
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
||||
verify(userManager).create(userCaptor.capture());
|
||||
User user = userCaptor.getValue();
|
||||
assertThat(user.getName()).isEqualTo("scmadmin");
|
||||
assertThat(user.getPassword()).isEqualTo("secret");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -142,7 +142,6 @@ public class GitLfsITCase {
|
||||
dto.setDisplayName(user.getDisplayName());
|
||||
dto.setType(user.getType());
|
||||
dto.setActive(user.isActive());
|
||||
dto.setAdmin(user.isAdmin());
|
||||
dto.setPassword(user.getPassword());
|
||||
createResource(adminClient, "users")
|
||||
.accept("*/*")
|
||||
|
||||
@@ -133,7 +133,6 @@ public class UserPermissionITCase extends AbstractPermissionITCaseBase<User>
|
||||
"scm-admin@scm-manager.org");
|
||||
|
||||
user.setPassword("hallo123");
|
||||
user.setAdmin(true);
|
||||
user.setType("xml");
|
||||
|
||||
return user;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
@@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -24,9 +24,9 @@
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package sonia.scm.security;
|
||||
@@ -58,18 +58,18 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AuthorizationChangedEventProducer}.
|
||||
*
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class AuthorizationChangedEventProducerTest {
|
||||
|
||||
|
||||
private StoringAuthorizationChangedEventProducer producer;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUpProducer() {
|
||||
producer = new StoringAuthorizationChangedEventProducer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.user.UserEvent)}.
|
||||
*/
|
||||
@@ -79,15 +79,15 @@ public class AuthorizationChangedEventProducerTest {
|
||||
User user = UserTestData.createDent();
|
||||
producer.onEvent(new UserEvent(HandlerEventType.BEFORE_CREATE, user));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
producer.onEvent(new UserEvent(HandlerEventType.CREATE, user));
|
||||
assertUserEventIsFired("dent");
|
||||
}
|
||||
|
||||
|
||||
private void assertEventIsNotFired(){
|
||||
assertNull(producer.event);
|
||||
}
|
||||
|
||||
|
||||
private void assertUserEventIsFired(String username){
|
||||
assertNotNull(producer.event);
|
||||
assertTrue(producer.event.isEveryUserAffected());
|
||||
@@ -102,28 +102,28 @@ public class AuthorizationChangedEventProducerTest {
|
||||
/**
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.user.UserEvent)} with modified user.
|
||||
*/
|
||||
@Test
|
||||
@Test
|
||||
public void testOnUserModificationEvent()
|
||||
{
|
||||
User user = UserTestData.createDent();
|
||||
User userModified = UserTestData.createDent();
|
||||
userModified.setDisplayName("Super Dent");
|
||||
|
||||
|
||||
producer.onEvent(new UserModificationEvent(HandlerEventType.BEFORE_CREATE, userModified, user));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
producer.onEvent(new UserModificationEvent(HandlerEventType.CREATE, userModified, user));
|
||||
assertEventIsNotFired();
|
||||
|
||||
userModified.setAdmin(true);
|
||||
|
||||
|
||||
userModified.setActive(false);
|
||||
|
||||
producer.onEvent(new UserModificationEvent(HandlerEventType.BEFORE_CREATE, userModified, user));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
producer.onEvent(new UserModificationEvent(HandlerEventType.CREATE, userModified, user));
|
||||
assertUserEventIsFired("dent");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.group.GroupEvent)}.
|
||||
*/
|
||||
@@ -133,11 +133,11 @@ public class AuthorizationChangedEventProducerTest {
|
||||
Group group = new Group("xml", "base");
|
||||
producer.onEvent(new GroupEvent(HandlerEventType.BEFORE_CREATE, group));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
producer.onEvent(new GroupEvent(HandlerEventType.CREATE, group));
|
||||
assertGlobalEventIsFired();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.group.GroupEvent)} with modified groups.
|
||||
*/
|
||||
@@ -148,15 +148,15 @@ public class AuthorizationChangedEventProducerTest {
|
||||
Group modifiedGroup = new Group("xml", "base");
|
||||
producer.onEvent(new GroupModificationEvent(HandlerEventType.BEFORE_MODIFY, modifiedGroup, group));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
producer.onEvent(new GroupModificationEvent(HandlerEventType.MODIFY, modifiedGroup, group));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
modifiedGroup.add("test");
|
||||
producer.onEvent(new GroupModificationEvent(HandlerEventType.MODIFY, modifiedGroup, group));
|
||||
assertGlobalEventIsFired();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.repository.RepositoryEvent)}.
|
||||
*/
|
||||
@@ -166,13 +166,13 @@ public class AuthorizationChangedEventProducerTest {
|
||||
Repository repository = RepositoryTestData.createHeartOfGold();
|
||||
producer.onEvent(new RepositoryEvent(HandlerEventType.BEFORE_CREATE, repository));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
producer.onEvent(new RepositoryEvent(HandlerEventType.CREATE, repository));
|
||||
assertGlobalEventIsFired();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.repository.RepositoryEvent)} with modified
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.repository.RepositoryEvent)} with modified
|
||||
* repository.
|
||||
*/
|
||||
@Test
|
||||
@@ -224,11 +224,11 @@ public class AuthorizationChangedEventProducerTest {
|
||||
producer.onEvent(new RepositoryModificationEvent(HandlerEventType.CREATE, repositoryModified, repository));
|
||||
assertEventIsNotFired();
|
||||
}
|
||||
|
||||
|
||||
private void resetStoredEvent(){
|
||||
producer.event = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationChangedEventProducer#onEvent(AssignedPermissionEvent)}.
|
||||
*/
|
||||
@@ -240,33 +240,33 @@ public class AuthorizationChangedEventProducerTest {
|
||||
);
|
||||
producer.onEvent(new AssignedPermissionEvent(HandlerEventType.BEFORE_CREATE, groupPermission));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
producer.onEvent(new AssignedPermissionEvent(HandlerEventType.CREATE, groupPermission));
|
||||
assertGlobalEventIsFired();
|
||||
|
||||
|
||||
resetStoredEvent();
|
||||
|
||||
|
||||
StoredAssignedPermission userPermission = new StoredAssignedPermission(
|
||||
"123", new AssignedPermission("trillian", false, "repository:read:*")
|
||||
);
|
||||
producer.onEvent(new AssignedPermissionEvent(HandlerEventType.BEFORE_CREATE, userPermission));
|
||||
assertEventIsNotFired();
|
||||
|
||||
|
||||
resetStoredEvent();
|
||||
|
||||
|
||||
producer.onEvent(new AssignedPermissionEvent(HandlerEventType.CREATE, userPermission));
|
||||
assertUserEventIsFired("trillian");
|
||||
}
|
||||
|
||||
|
||||
private static class StoringAuthorizationChangedEventProducer extends AuthorizationChangedEventProducer {
|
||||
|
||||
|
||||
private AuthorizationChangedEvent event;
|
||||
|
||||
@Override
|
||||
protected void sendEvent(AuthorizationChangedEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,22 +164,6 @@ public class DefaultAuthorizationCollectorTest {
|
||||
assertThat(authInfo.getObjectPermissions(), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationCollector#collect()} as admin.
|
||||
*/
|
||||
@Test
|
||||
@SubjectAware(
|
||||
configuration = "classpath:sonia/scm/shiro-001.ini"
|
||||
)
|
||||
public void testCollectAsAdmin() {
|
||||
User trillian = UserTestData.createTrillian();
|
||||
trillian.setAdmin(true);
|
||||
authenticate(trillian, "main");
|
||||
|
||||
AuthorizationInfo authInfo = collector.collect();
|
||||
assertIsAdmin(authInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AuthorizationCollector#collect()} with repository permissions.
|
||||
*/
|
||||
|
||||
@@ -67,7 +67,7 @@ import org.junit.Rule;
|
||||
)
|
||||
public class DefaultUserManagerTest extends UserManagerTestBase
|
||||
{
|
||||
|
||||
|
||||
@Rule
|
||||
public ShiroRule shiro = new ShiroRule();
|
||||
|
||||
@@ -97,39 +97,6 @@ public class DefaultUserManagerTest extends UserManagerTestBase
|
||||
when(userDAO.get("trillian")).thenReturn(trillian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testDefaultAccountAfterFristStart()
|
||||
{
|
||||
List<User> users = Lists.newArrayList(new User("tuser"));
|
||||
|
||||
when(userDAO.getAll()).thenReturn(users);
|
||||
|
||||
UserManager userManager = new DefaultUserManager(userDAO);
|
||||
|
||||
userManager.init(contextProvider);
|
||||
verify(userDAO, never()).add(any(User.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDefaultAccountCreation()
|
||||
{
|
||||
when(userDAO.getAll()).thenReturn(Collections.EMPTY_LIST);
|
||||
|
||||
UserManager userManager = new DefaultUserManager(userDAO);
|
||||
|
||||
userManager.init(contextProvider);
|
||||
verify(userDAO, times(2)).add(any(User.class));
|
||||
}
|
||||
|
||||
@Test(expected = InvalidPasswordException.class)
|
||||
public void shouldFailChangePasswordForWrongOldPassword() {
|
||||
UserManager userManager = new DefaultUserManager(userDAO);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package sonia.scm.web.security;
|
||||
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AdministrationContextRealmTest {
|
||||
|
||||
private AdministrationContextRealm realm = new AdministrationContextRealm();
|
||||
|
||||
@Test
|
||||
void shouldAssignAdminPermissions() {
|
||||
SimplePrincipalCollection collection = new SimplePrincipalCollection();
|
||||
collection.add("scm-system", DefaultAdministrationContext.REALM);
|
||||
collection.add(AdministrationContextMarker.MARKER, DefaultAdministrationContext.REALM);
|
||||
|
||||
AuthorizationInfo authorizationInfo = realm.doGetAuthorizationInfo(collection);
|
||||
|
||||
assertThat(authorizationInfo.getStringPermissions()).containsOnly("*");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull() {
|
||||
SimplePrincipalCollection collection = new SimplePrincipalCollection();
|
||||
collection.add("scm-system", DefaultAdministrationContext.REALM);
|
||||
|
||||
AuthorizationInfo authorizationInfo = realm.doGetAuthorizationInfo(collection);
|
||||
|
||||
assertThat(authorizationInfo).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"adminGroups": [""]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"adminUsers": [""]
|
||||
}
|
||||
Reference in New Issue
Block a user