diff --git a/scm-core/src/main/java/sonia/scm/ScmState.java b/scm-core/src/main/java/sonia/scm/ScmState.java index 8508d90c67..a04413f199 100644 --- a/scm-core/src/main/java/sonia/scm/ScmState.java +++ b/scm-core/src/main/java/sonia/scm/ScmState.java @@ -35,12 +35,14 @@ package sonia.scm; //~--- non-JDK imports -------------------------------------------------------- +import sonia.scm.security.PermissionDescriptor; import sonia.scm.user.User; import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ import java.util.Collection; +import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -122,6 +124,29 @@ public class ScmState public ScmState(SCMContextProvider provider, User user, Collection groups, Collection repositoryTypes, String defaultUserType, ScmClientConfig clientConfig) + { + this(provider, user, groups, repositoryTypes, defaultUserType, + clientConfig, null); + } + + /** + * Constructs {@link ScmState} object. + * + * + * @param provider context provider + * @param user current user + * @param groups groups of the current user + * @param repositoryTypes available repository types + * @param defaultUserType default user type + * @param clientConfig client configuration + * @param availablePermissions list of available permissions + * + * @since 1.31 + */ + public ScmState(SCMContextProvider provider, User user, + Collection groups, Collection repositoryTypes, + String defaultUserType, ScmClientConfig clientConfig, + List availablePermissions) { this.version = provider.getVersion(); this.user = user; @@ -129,10 +154,23 @@ public class ScmState 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 List getAvailablePermissions() + { + return availablePermissions; + } + /** * Returns configuration for SCM-Manager clients. * @@ -215,6 +253,18 @@ public class ScmState //~--- set methods ---------------------------------------------------------- + /** + * Sets a list of available global permissions. + * + * + * @param permissions list of available global permisisons + * @since 1.31 + */ + public void setAvailablePermissions(List permissions) + { + this.availablePermissions = permissions; + } + /** * Setter for the client configuration * @@ -299,6 +349,12 @@ public class ScmState //~--- fields --------------------------------------------------------------- + /** + * Avaliable global permission + * @since 1.31 + */ + private List availablePermissions; + /** Field description */ private ScmClientConfig clientConfig; diff --git a/scm-core/src/main/java/sonia/scm/repository/Permission.java b/scm-core/src/main/java/sonia/scm/repository/Permission.java index fe1e442f98..60c6628d61 100644 --- a/scm-core/src/main/java/sonia/scm/repository/Permission.java +++ b/scm-core/src/main/java/sonia/scm/repository/Permission.java @@ -37,6 +37,8 @@ package sonia.scm.repository; import com.google.common.base.Objects; +import sonia.scm.security.PermissionObject; + //~--- JDK imports ------------------------------------------------------------ import java.io.Serializable; @@ -52,7 +54,7 @@ import javax.xml.bind.annotation.XmlRootElement; */ @XmlRootElement(name = "permissions") @XmlAccessorType(XmlAccessType.FIELD) -public class Permission implements Serializable +public class Permission implements PermissionObject, Serializable { /** Field description */ @@ -135,7 +137,7 @@ public class Permission implements Serializable final Permission other = (Permission) obj; return Objects.equal(name, other.name) && Objects.equal(type, other.type) - && Objects.equal(groupPermission, groupPermission); + && Objects.equal(groupPermission, groupPermission); } /** @@ -176,6 +178,7 @@ public class Permission implements Serializable * * @return name of the user or group */ + @Override public String getName() { return name; @@ -198,6 +201,7 @@ public class Permission implements Serializable * * @return true if the permision is a group permission */ + @Override public boolean isGroupPermission() { return groupPermission; diff --git a/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java b/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java index 57ecda6da2..3e928c4c1a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java @@ -44,16 +44,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.config.ScmConfiguration; -import sonia.scm.group.GroupNames; +import sonia.scm.security.RepositoryPermission; import sonia.scm.security.Role; import sonia.scm.security.ScmSecurityException; import sonia.scm.util.AssertUtil; import sonia.scm.web.security.WebSecurityContext; -//~--- JDK imports ------------------------------------------------------------ - -import java.util.List; - /** * * @author Sebastian Sdorra @@ -179,7 +175,7 @@ public final class PermissionUtil public static boolean hasPermission(ScmConfiguration configuration, Repository repository, PermissionType pt) { - boolean result = false; + boolean result; Subject subject = SecurityUtils.getSubject(); @@ -196,16 +192,7 @@ public final class PermissionUtil } else { - List permissions = repository.getPermissions(); - - if (permissions != null) - { - GroupNames groupNames = - subject.getPrincipals().oneByType(GroupNames.class); - - result = hasPermission(permissions, username, groupNames, pt); - - } + result = subject.isPermitted(new RepositoryPermission(repository, pt)); } } else @@ -270,37 +257,4 @@ public final class PermissionUtil return permitted; } - - /** - * Method description - * - * - * @param permissions - * @param username - * @param groups - * @param pt - * - * @return - */ - private static boolean hasPermission(List permissions, - String username, GroupNames groups, PermissionType pt) - { - boolean result = false; - - for (Permission p : permissions) - { - String name = p.getName(); - - if (((name != null) && (p.getType().getValue() >= pt.getValue())) - && (name.equals(username) - || (p.isGroupPermission() && groups.contains(p.getName())))) - { - result = true; - - break; - } - } - - return result; - } } diff --git a/scm-core/src/main/java/sonia/scm/security/AssignedPermission.java b/scm-core/src/main/java/sonia/scm/security/AssignedPermission.java new file mode 100644 index 0000000000..a5e21cc7a6 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/AssignedPermission.java @@ -0,0 +1,208 @@ +/** + * 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.security; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Objects; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Permission object which is assigned to a specific user or group. + * + * @author Sebastian Sdorra + * @since 1.31 + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "assigned-permission") +public class AssignedPermission implements PermissionObject, Serializable +{ + + /** serial version uid */ + private static final long serialVersionUID = -7411338422110323879L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructor is only visible for JAXB. + * + */ + public AssignedPermission() {} + + /** + * Constructs a new AssignedPermission. + * + * + * @param permission assigned permission + */ + public AssignedPermission(AssignedPermission permission) + { + this.name = permission.name; + this.groupPermission = permission.groupPermission; + this.permission = permission.permission; + } + + /** + * Constructs a new AssingnedPermmission. + * + * + * @param name name of the user + * @param permission permission string + */ + public AssignedPermission(String name, String permission) + { + this.name = name; + this.permission = permission; + } + + /** + * Constructs a new AssingnedPermmission. + * + * + * @param name name of the user or group + * @param groupPermission true if the permission should be assigned to a group + * @param permission permission string + */ + public AssignedPermission(String name, boolean groupPermission, + String permission) + { + this.name = name; + this.groupPermission = groupPermission; + this.permission = permission; + } + + //~--- methods -------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final AssignedPermission other = (AssignedPermission) obj; + + return Objects.equal(name, other.name) + && Objects.equal(groupPermission, other.groupPermission) + && Objects.equal(permission, other.permission); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() + { + return Objects.hashCode(name, groupPermission, permission); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + //J- + return Objects.toStringHelper(this) + .add("name", name) + .add("groupPermisison", groupPermission) + .add("permission", permission) + .toString(); + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the name of the user or group which the permission is assigned. + * + * + * @return name of user or group + */ + @Override + public String getName() + { + return name; + } + + /** + * Returns the string representation of the permission. + * + * + * @return string representation of the permission + */ + public String getPermission() + { + return permission; + } + + /** + * Returns true if the permission is assigned to a group. + * + * + * @return true if the permission is assigned to a group + */ + @Override + public boolean isGroupPermission() + { + return groupPermission; + } + + //~--- fields --------------------------------------------------------------- + + /** group permission indicator */ + @XmlElement(name = "group-permission") + private boolean groupPermission; + + /** name of the user or group */ + private String name; + + /** string representation of the permission */ + private String permission; +} diff --git a/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java new file mode 100644 index 0000000000..8db5a9dcaf --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java @@ -0,0 +1,182 @@ +/** + * 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.security; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Objects; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Descriptor for available permission objects. + * + * @author Sebastian Sdorra + * @since 1.31 + */ +@XmlRootElement(name = "permission") +@XmlAccessorType(XmlAccessType.FIELD) +public class PermissionDescriptor implements Serializable +{ + + /** Field description */ + private static final long serialVersionUID = -9141065458354047154L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructor is only visible for JAXB. + * + */ + public PermissionDescriptor() {} + + /** + * Constructs ... + * + * + * @param displayName + * @param description + * @param value + */ + public PermissionDescriptor(String displayName, String description, + String value) + { + this.displayName = displayName; + this.description = description; + this.value = value; + } + + //~--- methods -------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final PermissionDescriptor other = (PermissionDescriptor) obj; + + return Objects.equal(displayName, other.displayName) + && Objects.equal(description, other.description) + && Objects.equal(value, other.value); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() + { + return Objects.hashCode(displayName, description, value); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + + //J- + return Objects.toStringHelper(this) + .add("displayName", displayName) + .add("description", description) + .add("value", value) + .toString(); + + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the description of the permission. + * + * + * @return description + */ + public String getDescription() + { + return description; + } + + /** + * Returns the display name of the permission. + * + * + * @return display name + */ + public String getDisplayName() + { + return displayName; + } + + /** + * Returns the string representation of the permission. + * + * + * @return string representation + */ + public String getValue() + { + return value; + } + + //~--- fields --------------------------------------------------------------- + + /** description */ + private String description; + + /** display name */ + @XmlElement(name = "display-name") + private String displayName; + + /** value */ + private String value; +} diff --git a/scm-core/src/main/java/sonia/scm/security/PermissionObject.java b/scm-core/src/main/java/sonia/scm/security/PermissionObject.java new file mode 100644 index 0000000000..0b3025f16e --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/PermissionObject.java @@ -0,0 +1,58 @@ +/** + * 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.security; + +/** + * Interface for permission objects. + * + * @author Sebastian Sdorra + * @since 1.31 + */ +public interface PermissionObject +{ + + /** + * Returns the name of the user or group which the permission is assigned. + * + * + * @return name of user or group + */ + public String getName(); + + /** + * Returns the id of the stored permission object. + * + * + * @return id of permission + */ + public boolean isGroupPermission(); +} diff --git a/scm-core/src/main/java/sonia/scm/security/SecuritySystem.java b/scm-core/src/main/java/sonia/scm/security/SecuritySystem.java new file mode 100644 index 0000000000..6c59f54bee --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/SecuritySystem.java @@ -0,0 +1,124 @@ +/** + * 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.security; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Predicate; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * The SecuritySystem manages global permissions. + * + * @author Sebastian Sdorra + * @since 1.31 + */ +public interface SecuritySystem +{ + + /** + * Store a new permission. + * + * + * @param permission permission to be stored + * + * @return stored permission + */ + public StoredAssignedPermission addPermission(AssignedPermission permission); + + /** + * Delete stored permission. + * + * + * @param permission permission to be deleted + */ + public void deletePermission(StoredAssignedPermission permission); + + /** + * Delete stored permission. + * + * + * @param id id of the permission + */ + public void deletePermission(String id); + + /** + * Modify stored permission. + * + * + * @param permission stored permisison + */ + public void modifyPermission(StoredAssignedPermission permission); + + //~--- get methods ---------------------------------------------------------- + + /** + * Return all stored permissions. + * + * + * @return stored permission + */ + public List getAllPermissions(); + + /** + * Return all available permissions. + * + * + * @return available permissions + */ + public List getAvailablePermissions(); + + /** + * Return the stored permission which is stored with the given id. + * + * + * @param id id of the stored permission + * + * @return stored permission + */ + public StoredAssignedPermission getPermission(String id); + + /** + * Returns all stored permissions which are matched by the given + * {@link Predicate}. + * + * + * @param predicate predicate to filter + * + * @return filtered permissions + */ + public List getPermissions( + Predicate predicate); +} diff --git a/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java new file mode 100644 index 0000000000..a1ab1fdee1 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java @@ -0,0 +1,93 @@ +/** + * 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.security; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Permission object which is stored and assigned to a specific user or group. + * + * @author Sebastian Sdorra + * @since 1.31 + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "assigned-permission") +public class StoredAssignedPermission extends AssignedPermission +{ + + /** serial version uid */ + private static final long serialVersionUID = -4593919877023168090L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructor is only visible for JAXB. + * + */ + public StoredAssignedPermission() {} + + /** + * Constructs a new StoredAssignedPermission. + * + * + * @param id id of the permission object + * @param permission assigned permission object + */ + public StoredAssignedPermission(String id, AssignedPermission permission) + { + super(permission); + this.id = id; + + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the id of the stored permission object. + * + * + * @return id of permission + */ + public String getId() + { + return id; + } + + //~--- fields --------------------------------------------------------------- + + /** id */ + private String id; +} diff --git a/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermissionEvent.java b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermissionEvent.java new file mode 100644 index 0000000000..530cc602a8 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermissionEvent.java @@ -0,0 +1,152 @@ +/** + * 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.security; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Objects; + +import sonia.scm.HandlerEvent; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.Serializable; + +/** + * Event which is fired after a {@link StoredAssignedPermission} was added, + * removed or changed. + * + * @author Sebastian Sdorra + * @since 1.31 + */ +public final class StoredAssignedPermissionEvent implements Serializable +{ + + /** serial version uid */ + private static final long serialVersionUID = 706824497813169009L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs a new StoredAssignedPermissionEvent. + * + * + * @param type type of the event + * @param permission permission object which has changed + */ + public StoredAssignedPermissionEvent(HandlerEvent type, + StoredAssignedPermission permission) + { + this.type = type; + this.permission = permission; + } + + //~--- methods -------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final StoredAssignedPermissionEvent other = + (StoredAssignedPermissionEvent) obj; + + return Objects.equal(type, other.type) + && Objects.equal(permission, other.permission); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() + { + return Objects.hashCode(type, permission); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + //J- + return Objects.toStringHelper(this) + .add("type", type) + .add("permission", permission) + .toString(); + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Return the type of the event. + * + * + * @return type of event + */ + public HandlerEvent getEventType() + { + return type; + } + + /** + * Returns the changed permission object. + * + * + * @return changed permission + */ + public StoredAssignedPermission getPermission() + { + return permission; + } + + //~--- fields --------------------------------------------------------------- + + /** changed permission */ + private StoredAssignedPermission permission; + + /** type of the event */ + private HandlerEvent type; +} diff --git a/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStore.java b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStore.java new file mode 100644 index 0000000000..116e781717 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStore.java @@ -0,0 +1,64 @@ +/** + * 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.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Predicate; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Collection; + +/** + * A ConfigurationEntryStore can be used to store multiple entries of structured + * configuration data. Note: the default implementation use JAXB to + * marshall the items. + * + * @author Sebastian Sdorra + * + * @param store value type + * @since 1.31 + */ +public interface ConfigurationEntryStore extends DataStore +{ + + /** + * Return all values matching the given {@link Predicate}. + * + * + * @param predicate predicate to match values + * + * @return filtered collection of values + */ + public Collection getMatchingValues(Predicate predicate); +} diff --git a/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStoreFactory.java b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStoreFactory.java new file mode 100644 index 0000000000..f83e2e164c --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStoreFactory.java @@ -0,0 +1,61 @@ +/** + * 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.store; + +/** + * The ConfigurationEntryStoreFactory can be used to create new or get existing + * {@link ConfigurationEntryStore}s. Note: the default implementation + * uses the same location as the {@link StoreFactory}, so be sure that the + * store names are unique for all {@link ConfigurationEntryStore}s and + * {@link Store}s. + * + * @author Sebastian Sdorra + * @since 1.31 + * + * @apiviz.landmark + * @apiviz.uses sonia.scm.store.ConfigurationEntryStore + */ +public interface ConfigurationEntryStoreFactory +{ + + /** + * Get an existing {@link ConfigurationEntryStore} or create a new one. + * + * + * @param type type of the store objects + * @param name name of the store + * @param type of the store objects + * + * @return {@link ConfigurationEntryStore} with given name and type + */ + public ConfigurationEntryStore getStore(Class type, String name); +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java new file mode 100644 index 0000000000..6eeb25d96b --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java @@ -0,0 +1,407 @@ +/** + * 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.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Maps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.security.KeyGenerator; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileOutputStream; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.stream.StreamSource; + +/** + * + * @author Sebastian Sdorra + * + * @param + */ +public class JAXBConfigurationEntryStore + implements ConfigurationEntryStore +{ + + /** Field description */ + private static final Object LOCK = new Object(); + + /** Field description */ + private static final String TAG_CONFIGURATION = "configuration"; + + /** Field description */ + private static final String TAG_ENTRY = "entry"; + + /** Field description */ + private static final String TAG_KEY = "key"; + + /** Field description */ + private static final String TAG_VALUE = "value"; + + /** + * the logger for JAXBConfigurationEntryStore + */ + private static final Logger logger = + LoggerFactory.getLogger(JAXBConfigurationEntryStore.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * + * @param keyGenerator + * @param file + * @param type + */ + JAXBConfigurationEntryStore(KeyGenerator keyGenerator, File file, + Class type) + { + this.keyGenerator = keyGenerator; + this.file = file; + this.type = type; + + try + { + this.context = JAXBContext.newInstance(type); + + if (file.exists()) + { + load(); + } + } + catch (JAXBException ex) + { + throw new StoreException("could not create jaxb context", ex); + } + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + @Override + public void clear() + { + logger.debug("clear configuration store"); + + synchronized (LOCK) + { + entries.clear(); + store(); + } + } + + /** + * Method description + * + * + * @param item + * + * @return + */ + @Override + public String put(V item) + { + String id = keyGenerator.createKey(); + + put(id, item); + + return id; + } + + /** + * Method description + * + * + * @param id + * @param item + */ + @Override + public void put(String id, V item) + { + logger.debug("put item {} to configuration store", id); + + synchronized (LOCK) + { + entries.put(id, item); + store(); + } + } + + /** + * Method description + * + * + * @param id + */ + @Override + public void remove(String id) + { + logger.debug("remove item {} from configuration store", id); + + synchronized (LOCK) + { + entries.remove(id); + store(); + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param id + * + * @return + */ + @Override + public V get(String id) + { + logger.trace("get item {} from configuration store", id); + + return entries.get(id); + } + + /** + * Method description + * + * + * @return + */ + @Override + public Map getAll() + { + logger.trace("get all items from configuration store"); + + return Collections.unmodifiableMap(entries); + } + + /** + * Method description + * + * + * @param predicate + * + * @return + */ + @Override + public Collection getMatchingValues(Predicate predicate) + { + return Collections2.filter(entries.values(), predicate); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param writer + */ + private void close(XMLStreamWriter writer) + { + if (writer != null) + { + try + { + writer.close(); + } + catch (XMLStreamException ex) + { + logger.error("could not close writer", ex); + } + } + } + + /** + * Method description + * + * + * @param reader + */ + private void close(XMLStreamReader reader) + { + if (reader != null) + { + try + { + reader.close(); + } + catch (XMLStreamException ex) + { + logger.error("could not close reader", ex); + } + } + } + + /** + * Method description + * + * + * @return + */ + private void load() + { + logger.debug("load configuration from {}", file); + + XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + + XMLStreamReader reader = null; + + try + { + Unmarshaller u = context.createUnmarshaller(); + + reader = xmlInputFactory.createXMLStreamReader(new StreamSource(file)); + reader.nextTag(); + reader.nextTag(); + + while (reader.isStartElement() && reader.getLocalName().equals(TAG_ENTRY)) + { + + // read key + reader.nextTag(); + + String key = reader.getElementText(); + + // read entry + reader.nextTag(); + + V v = (V) u.unmarshal(reader, type).getValue(); + + entries.put(key, v); + } + } + catch (Exception ex) + { + throw new StoreException("could not load configuration", ex); + } + finally + { + close(reader); + } + } + + /** + * Method description + * + */ + private void store() + { + logger.debug("store configuration to {}", file); + + XMLStreamWriter writer = null; + + try + { + writer = XMLOutputFactory.newFactory().createXMLStreamWriter( + new FileOutputStream(file)); + writer.writeStartDocument(); + writer.writeStartElement(TAG_CONFIGURATION); + + Marshaller m = context.createMarshaller(); + + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + + for (Entry e : entries.entrySet()) + { + writer.writeStartElement(TAG_ENTRY); + writer.writeStartElement(TAG_KEY); + writer.writeCharacters(e.getKey()); + writer.writeEndElement(); + + JAXBElement je = new JAXBElement(QName.valueOf(TAG_VALUE), type, + e.getValue()); + + m.marshal(je, writer); + } + + writer.writeEndElement(); + writer.writeEndDocument(); + } + catch (Exception ex) + { + throw new StoreException("could not store configuration", ex); + } + finally + { + close(writer); + } + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private JAXBContext context; + + /** Field description */ + private Map entries = Maps.newHashMap(); + + /** Field description */ + private File file; + + /** Field description */ + private KeyGenerator keyGenerator; + + /** Field description */ + private Class type; +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java new file mode 100644 index 0000000000..296d03cd70 --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java @@ -0,0 +1,113 @@ +/** + * 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.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.SCMContextProvider; +import sonia.scm.security.KeyGenerator; +import sonia.scm.util.IOUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; + +/** + * + * @author Sebastian Sdorra + */ +@Singleton +public class JAXBConfigurationEntryStoreFactory + implements ConfigurationEntryStoreFactory +{ + + /** + * the logger for JAXBConfigurationEntryStoreFactory + */ + private static final Logger logger = + LoggerFactory.getLogger(JAXBConfigurationEntryStoreFactory.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param keyGenerator + * @param context + */ + @Inject + public JAXBConfigurationEntryStoreFactory(KeyGenerator keyGenerator, + SCMContextProvider context) + { + this.keyGenerator = keyGenerator; + directory = new File(context.getBaseDirectory(), + StoreConstants.CONFIGDIRECTORY_NAME); + IOUtil.mkdirs(directory); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param type + * @param name + * @param + * + * @return + */ + @Override + public ConfigurationEntryStore getStore(Class type, String name) + { + logger.debug("create new configuration store for type {} with name {}", + type, name); + + return new JAXBConfigurationEntryStore(keyGenerator, + new File(directory, name.concat(StoreConstants.FILE_EXTENSION)), type); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private File directory; + + /** Field description */ + private KeyGenerator keyGenerator; +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java index 40b22a182b..40ba0a2053 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java @@ -61,9 +61,6 @@ import javax.xml.bind.Marshaller; public class JAXBDataStore extends FileBasedStore implements DataStore { - /** Field description */ - private static final String SUFFIX = ".xml"; - /** * the logger for JAXBDataStore */ @@ -82,7 +79,7 @@ public class JAXBDataStore extends FileBasedStore implements DataStore */ public JAXBDataStore(KeyGenerator keyGenerator, Class type, File directory) { - super(directory, SUFFIX); + super(directory, StoreConstants.FILE_EXTENSION); this.keyGenerator = keyGenerator; try diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java index 3291be4806..34f8fb3c98 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java @@ -56,12 +56,6 @@ import java.io.IOException; public class JAXBStoreFactory implements ListenableStoreFactory { - /** Field description */ - public static final String CONFIGDIRECTORY_NAME = "config"; - - /** Field description */ - public static final String FILE_EXTENSION = ".xml"; - /** the logger for JAXBStoreFactory */ private static final Logger logger = LoggerFactory.getLogger(JAXBStoreFactory.class); @@ -91,7 +85,7 @@ public class JAXBStoreFactory implements ListenableStoreFactory public void init(SCMContextProvider context) { configDirectory = new File(context.getBaseDirectory(), - CONFIGDIRECTORY_NAME); + StoreConstants.CONFIGDIRECTORY_NAME); IOUtil.mkdirs(configDirectory); } @@ -115,7 +109,8 @@ public class JAXBStoreFactory implements ListenableStoreFactory throw new IllegalStateException("store factory is not initialized"); } - File configFile = new File(configDirectory, name.concat(FILE_EXTENSION)); + File configFile = new File(configDirectory, + name.concat(StoreConstants.FILE_EXTENSION)); if (logger.isDebugEnabled()) { diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/StoreConstants.java b/scm-dao-xml/src/main/java/sonia/scm/store/StoreConstants.java new file mode 100644 index 0000000000..a44fda417d --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/StoreConstants.java @@ -0,0 +1,46 @@ +/** + * 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.store; + +/** + * + * @author Sebastian Sdorra + */ +public interface StoreConstants +{ + + /** Field description */ + public static final String CONFIGDIRECTORY_NAME = "config"; + + /** Field description */ + public static final String FILE_EXTENSION = ".xml"; +} diff --git a/scm-dao-xml/src/test/java/sonia/scm/store/JAXBConfigurationEntryStoreTest.java b/scm-dao-xml/src/test/java/sonia/scm/store/JAXBConfigurationEntryStoreTest.java new file mode 100644 index 0000000000..7e393454aa --- /dev/null +++ b/scm-dao-xml/src/test/java/sonia/scm/store/JAXBConfigurationEntryStoreTest.java @@ -0,0 +1,57 @@ +/** + * 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.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.security.UUIDKeyGenerator; + +/** + * + * @author Sebastian Sdorra + */ +public class JAXBConfigurationEntryStoreTest extends ConfigurationEntryStoreTestBase +{ + + /** + * Method description + * + * + * @return + */ + @Override + protected ConfigurationEntryStoreFactory createConfigurationStoreFactory() + { + return new JAXBConfigurationEntryStoreFactory(new UUIDKeyGenerator(), + contextProvider); + } +} diff --git a/scm-test/src/main/java/sonia/scm/AbstractTestBase.java b/scm-test/src/main/java/sonia/scm/AbstractTestBase.java index b991b67c4f..47ee59b075 100644 --- a/scm-test/src/main/java/sonia/scm/AbstractTestBase.java +++ b/scm-test/src/main/java/sonia/scm/AbstractTestBase.java @@ -151,6 +151,7 @@ public class AbstractTestBase try { preTearDown(); + clearSubject(); } finally { @@ -179,7 +180,7 @@ public class AbstractTestBase //~--- methods -------------------------------------------------------------- /** - * Clears Shiro's thread state, ensuring the thread remains clean for + * Clears Shiro's thread state, ensuring the thread remains clean for * future test execution. */ protected void clearSubject() diff --git a/scm-test/src/main/java/sonia/scm/store/ConfigurationEntryStoreTestBase.java b/scm-test/src/main/java/sonia/scm/store/ConfigurationEntryStoreTestBase.java new file mode 100644 index 0000000000..144c00e2d6 --- /dev/null +++ b/scm-test/src/main/java/sonia/scm/store/ConfigurationEntryStoreTestBase.java @@ -0,0 +1,63 @@ +/** + * 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.store; + +/** + * + * @author Sebastian Sdorra + */ +public abstract class ConfigurationEntryStoreTestBase extends KeyValueStoreTestBase +{ + + /** + * Method description + * + * + * @return + */ + protected abstract ConfigurationEntryStoreFactory createConfigurationStoreFactory(); + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + protected ConfigurationEntryStore getDataStore() + { + return createConfigurationStoreFactory().getStore(StoreObject.class, + "test"); + } +} diff --git a/scm-test/src/main/java/sonia/scm/store/DataStoreTestBase.java b/scm-test/src/main/java/sonia/scm/store/DataStoreTestBase.java index ad4e290213..3129d3a339 100644 --- a/scm-test/src/main/java/sonia/scm/store/DataStoreTestBase.java +++ b/scm-test/src/main/java/sonia/scm/store/DataStoreTestBase.java @@ -30,26 +30,14 @@ */ + package sonia.scm.store; -//~--- non-JDK imports -------------------------------------------------------- - -import org.junit.Before; -import org.junit.Test; - -import sonia.scm.AbstractTestBase; - -import static org.junit.Assert.*; - -//~--- JDK imports ------------------------------------------------------------ - -import java.util.Map; - /** * * @author Sebastian Sdorra */ -public abstract class DataStoreTestBase extends AbstractTestBase +public abstract class DataStoreTestBase extends KeyValueStoreTestBase { /** @@ -60,155 +48,6 @@ public abstract class DataStoreTestBase extends AbstractTestBase */ protected abstract DataStoreFactory createDataStoreFactory(); - /** - * Method description - * - */ - @Before - public void before() - { - store = getDataStore(); - store.clear(); - } - - /** - * Method description - * - */ - @Test - public void testClear() - { - testPutWithId(); - - store.clear(); - assertNull(store.get("1")); - assertNull(store.get("2")); - - assertTrue(store.getAll().isEmpty()); - } - - /** - * Method description - * - */ - @Test - public void testGet() - { - StoreObject other = store.get("1"); - - assertNull(other); - - StoreObject obj = new StoreObject("test-1"); - - store.put("1", obj); - other = store.get("1"); - assertNotNull(other); - assertEquals(obj, other); - } - - /** - * Method description - * - */ - @Test - public void testGetAll() - { - StoreObject obj1 = new StoreObject("test-1"); - - store.put("1", obj1); - - StoreObject obj2 = new StoreObject("test-2"); - - store.put("2", obj2); - - Map map = store.getAll(); - - assertNotNull(map); - - assertFalse(map.isEmpty()); - assertEquals(2, map.size()); - - assertEquals(obj1, map.get("1")); - assertEquals(obj2, map.get("2")); - - assertNull(map.get("3")); - } - - /** - * Method description - * - */ - @Test - public void testGetAllFromEmpty() - { - Map map = store.getAll(); - - assertNotNull(map); - assertTrue(map.isEmpty()); - } - - /** - * Method description - * - */ - @Test - public void testGetFromEmpty() - { - StoreObject obj = store.get("test"); - - assertNull(obj); - } - - /** - * Method description - * - */ - @Test - public void testPutWithId() - { - StoreObject obj1 = new StoreObject("test-1"); - - store.put("1", obj1); - - StoreObject obj2 = new StoreObject("test-2"); - - store.put("2", obj2); - - assertEquals(obj1, store.get("1")); - assertEquals(obj2, store.get("2")); - } - - /** - * Method description - * - */ - @Test - public void testPutWithoutId() - { - StoreObject obj = new StoreObject("test-1"); - String id = store.put(obj); - - assertNotNull(id); - - assertEquals(obj, store.get(id)); - } - - /** - * Method description - * - */ - @Test - public void testRemove() - { - testPutWithId(); - - store.remove("1"); - assertNull(store.get("1")); - assertNotNull(store.get("2")); - store.remove("2"); - assertNull(store.get("2")); - } - //~--- get methods ---------------------------------------------------------- /** @@ -217,13 +56,9 @@ public abstract class DataStoreTestBase extends AbstractTestBase * * @return */ - private DataStore getDataStore() + @Override + protected DataStore getDataStore() { return createDataStoreFactory().getStore(StoreObject.class, "test"); } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private DataStore store; } diff --git a/scm-test/src/main/java/sonia/scm/store/KeyValueStoreTestBase.java b/scm-test/src/main/java/sonia/scm/store/KeyValueStoreTestBase.java new file mode 100644 index 0000000000..16c6824812 --- /dev/null +++ b/scm-test/src/main/java/sonia/scm/store/KeyValueStoreTestBase.java @@ -0,0 +1,221 @@ +/** + * 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.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.junit.Before; +import org.junit.Test; + +import sonia.scm.AbstractTestBase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Map; + +/** + * + * @author Sebastian Sdorra + */ +public abstract class KeyValueStoreTestBase extends AbstractTestBase +{ + + /** + * Method description + * + * + * @return + */ + protected abstract DataStore getDataStore(); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + @Before + public void before() + { + store = getDataStore(); + store.clear(); + } + + /** + * Method description + * + */ + @Test + public void testClear() + { + testPutWithId(); + + store.clear(); + assertNull(store.get("1")); + assertNull(store.get("2")); + + assertTrue(store.getAll().isEmpty()); + } + + /** + * Method description + * + */ + @Test + public void testGet() + { + StoreObject other = store.get("1"); + + assertNull(other); + + StoreObject obj = new StoreObject("test-1"); + + store.put("1", obj); + other = store.get("1"); + assertNotNull(other); + assertEquals(obj, other); + } + + /** + * Method description + * + */ + @Test + public void testGetAll() + { + StoreObject obj1 = new StoreObject("test-1"); + + store.put("1", obj1); + + StoreObject obj2 = new StoreObject("test-2"); + + store.put("2", obj2); + + Map map = store.getAll(); + + assertNotNull(map); + + assertFalse(map.isEmpty()); + assertEquals(2, map.size()); + + assertEquals(obj1, map.get("1")); + assertEquals(obj2, map.get("2")); + + assertNull(map.get("3")); + } + + /** + * Method description + * + */ + @Test + public void testGetAllFromEmpty() + { + Map map = store.getAll(); + + assertNotNull(map); + assertTrue(map.isEmpty()); + } + + /** + * Method description + * + */ + @Test + public void testGetFromEmpty() + { + StoreObject obj = store.get("test"); + + assertNull(obj); + } + + /** + * Method description + * + */ + @Test + public void testPutWithId() + { + StoreObject obj1 = new StoreObject("test-1"); + + store.put("1", obj1); + + StoreObject obj2 = new StoreObject("test-2"); + + store.put("2", obj2); + + assertEquals(obj1, store.get("1")); + assertEquals(obj2, store.get("2")); + } + + /** + * Method description + * + */ + @Test + public void testPutWithoutId() + { + StoreObject obj = new StoreObject("test-1"); + String id = store.put(obj); + + assertNotNull(id); + + assertEquals(obj, store.get(id)); + } + + /** + * Method description + * + */ + @Test + public void testRemove() + { + testPutWithId(); + + store.remove("1"); + assertNull(store.get("1")); + assertNotNull(store.get("2")); + store.remove("2"); + assertNull(store.get("2")); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private DataStore store; +} diff --git a/scm-test/src/main/java/sonia/scm/util/MockUtil.java b/scm-test/src/main/java/sonia/scm/util/MockUtil.java index 3aa41f4523..2a80332244 100644 --- a/scm-test/src/main/java/sonia/scm/util/MockUtil.java +++ b/scm-test/src/main/java/sonia/scm/util/MockUtil.java @@ -41,6 +41,7 @@ import org.apache.shiro.authz.Permission; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.Subject.Builder; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -138,6 +139,20 @@ public final class MockUtil * @return */ public static Subject createUserSubject() + { + return createUserSubject(null); + } + + /** + * Method description + * + * + * + * @param securityManager + * @return + */ + public static Subject createUserSubject( + org.apache.shiro.mgt.SecurityManager securityManager) { SimplePrincipalCollection collection = new SimplePrincipalCollection(); User user = UserTestData.createTrillian(); @@ -145,8 +160,18 @@ public final class MockUtil collection.add(user.getName(), "junit"); collection.add(user, "junit"); - return new Subject.Builder().principals(collection).authenticated( - true).buildSubject(); + Builder builder; + + if (securityManager != null) + { + builder = new Subject.Builder(securityManager); + } + else + { + builder = new Subject.Builder(); + } + + return builder.principals(collection).authenticated(true).buildSubject(); } //~--- get methods ---------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 35cb5192e6..84b27d8277 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -47,7 +47,7 @@ import org.slf4j.LoggerFactory; import sonia.scm.api.rest.UriExtensionsConfig; import sonia.scm.cache.CacheManager; -import sonia.scm.cache.EhCacheManager; +import sonia.scm.cache.GuavaCacheManager; import sonia.scm.config.ScmConfiguration; import sonia.scm.event.ScmEventBus; import sonia.scm.filter.AdminSecurityFilter; @@ -86,13 +86,17 @@ import sonia.scm.resources.ScriptResourceServlet; import sonia.scm.security.CipherHandler; import sonia.scm.security.CipherUtil; import sonia.scm.security.DefaultKeyGenerator; +import sonia.scm.security.DefaultSecuritySystem; import sonia.scm.security.EncryptionHandler; import sonia.scm.security.KeyGenerator; import sonia.scm.security.MessageDigestEncryptionHandler; import sonia.scm.security.SecurityContext; +import sonia.scm.security.SecuritySystem; import sonia.scm.store.BlobStoreFactory; +import sonia.scm.store.ConfigurationEntryStoreFactory; import sonia.scm.store.DataStoreFactory; import sonia.scm.store.FileBlobStoreFactory; +import sonia.scm.store.JAXBConfigurationEntryStoreFactory; import sonia.scm.store.JAXBDataStoreFactory; import sonia.scm.store.JAXBStoreFactory; import sonia.scm.store.ListenableStoreFactory; @@ -142,7 +146,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; -import sonia.scm.cache.GuavaCacheManager; /** * @@ -252,6 +255,8 @@ public class ScmServletModule extends ServletModule // bind core bind(StoreFactory.class, JAXBStoreFactory.class); bind(ListenableStoreFactory.class, JAXBStoreFactory.class); + bind(ConfigurationEntryStoreFactory.class, + JAXBConfigurationEntryStoreFactory.class); bind(DataStoreFactory.class, JAXBDataStoreFactory.class); bind(BlobStoreFactory.class, FileBlobStoreFactory.class); bind(ScmConfiguration.class).toInstance(config); @@ -271,6 +276,7 @@ public class ScmServletModule extends ServletModule bind(AuthenticationManager.class, ChainAuthenticatonManager.class); bind(SecurityContext.class).to(BasicSecurityContext.class); bind(WebSecurityContext.class).to(BasicSecurityContext.class); + bind(SecuritySystem.class).to(DefaultSecuritySystem.class); bind(AdministrationContext.class, DefaultAdministrationContext.class); // bind cache diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/Permission.java b/scm-webapp/src/main/java/sonia/scm/api/rest/Permission.java new file mode 100644 index 0000000000..bf6437e97c --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/Permission.java @@ -0,0 +1,167 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Objects; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Sebastian Sdorra + */ +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class Permission implements Serializable +{ + + /** Field description */ + private static final long serialVersionUID = 4320217034601679261L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public Permission() {} + + /** + * Constructs ... + * + * + * @param id + * @param value + */ + public Permission(String id, String value) + { + this.id = id; + this.value = value; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Permission other = (Permission) obj; + + return Objects.equal(id, other.id) && Objects.equal(value, other.value); + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + return Objects.hashCode(id, value); + } + + /** + * Method description + * + * + * @return + */ + @Override + public String toString() + { + //J- + return Objects.toStringHelper(this) + .add("id", id) + .add("value", value) + .toString(); + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public String getId() + { + return id; + } + + /** + * Method description + * + * + * @return + */ + public String getValue() + { + return value; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private String id; + + /** Field description */ + private String value; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractPermissionResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractPermissionResource.java new file mode 100644 index 0000000000..297145475c --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractPermissionResource.java @@ -0,0 +1,265 @@ +/** + * 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.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; + +import sonia.scm.api.rest.Permission; +import sonia.scm.security.AssignedPermission; +import sonia.scm.security.SecuritySystem; +import sonia.scm.security.StoredAssignedPermission; + +//~--- JDK imports ------------------------------------------------------------ + +import java.net.URI; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +/** + * + * @author Sebastian Sdorra + * @since 1.31 + */ +public abstract class AbstractPermissionResource +{ + + /** + * Constructs ... + * + * + * @param securitySystem + * @param name + */ + protected AbstractPermissionResource(SecuritySystem securitySystem, + String name) + { + this.securitySystem = securitySystem; + this.name = name; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param permission + * + * @return + */ + protected abstract AssignedPermission transformPermission( + Permission permission); + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + protected abstract Predicate getPredicate(); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param uriInfo + * @param permission + * + * @return + */ + @POST + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response add(@Context UriInfo uriInfo, Permission permission) + { + AssignedPermission ap = transformPermission(permission); + StoredAssignedPermission sap = securitySystem.addPermission(ap); + URI uri = uriInfo.getAbsolutePathBuilder().path(sap.getId()).build(); + + return Response.created(uri).build(); + } + + /** + * Method description + * + * + * @param id + * + * @return + */ + @DELETE + @Path("{id}") + public Response delete(@PathParam("id") String id) + { + StoredAssignedPermission sap = getPermission(id); + + securitySystem.deletePermission(sap); + + return Response.noContent().build(); + } + + /** + * Method description + * + * + * @param id + * @param permission + * + * @return + */ + @PUT + @Path("{id}") + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response update(@PathParam("id") String id, Permission permission) + { + StoredAssignedPermission sap = getPermission(id); + + securitySystem.modifyPermission(new StoredAssignedPermission(sap.getId(), + transformPermission(permission))); + + return Response.noContent().build(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param id + * + * @return + */ + @GET + @Path("{id}") + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Permission get(@PathParam("id") String id) + { + StoredAssignedPermission sap = getPermission(id); + + return new Permission(sap.getId(), sap.getPermission()); + } + + /** + * Method description + * + * + * @return + */ + @GET + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public List getAll() + { + return getPermissions(getPredicate()); + } + + /** + * Method description + * + * + * @param id + * + * @return + */ + private StoredAssignedPermission getPermission(String id) + { + StoredAssignedPermission sap = securitySystem.getPermission(id); + + if (sap == null) + { + throw new WebApplicationException(Status.NOT_FOUND); + } + + if (!getPredicate().apply(sap)) + { + throw new WebApplicationException(Status.BAD_REQUEST); + } + + return sap; + } + + /** + * Method description + * + * + * @param predicate + * + * @return + */ + private List getPermissions( + Predicate predicate) + { + List permissions = + securitySystem.getPermissions(predicate); + + return Lists.transform(permissions, + new Function() + { + + @Override + public Permission apply(StoredAssignedPermission mgp) + { + return new Permission(mgp.getId(), mgp.getPermission()); + } + }); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + protected String name; + + /** Field description */ + private SecuritySystem securitySystem; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java index c4e9618329..346c07445e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java @@ -56,6 +56,9 @@ import sonia.scm.ScmState; import sonia.scm.config.ScmConfiguration; import sonia.scm.group.GroupNames; import sonia.scm.repository.RepositoryManager; +import sonia.scm.security.PermissionDescriptor; +import sonia.scm.security.Role; +import sonia.scm.security.SecuritySystem; import sonia.scm.security.Tokens; import sonia.scm.user.User; import sonia.scm.user.UserManager; @@ -64,6 +67,7 @@ import sonia.scm.user.UserManager; import java.util.Collection; import java.util.Collections; +import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -105,16 +109,18 @@ public class AuthenticationResource * @param repositoryManger * @param userManager * @param securityContextProvider + * @param securitySystem */ @Inject public AuthenticationResource(SCMContextProvider contextProvider, ScmConfiguration configuration, RepositoryManager repositoryManger, - UserManager userManager) + UserManager userManager, SecuritySystem securitySystem) { this.contextProvider = contextProvider; this.configuration = configuration; this.repositoryManger = repositoryManger; this.userManager = userManager; + this.securitySystem = securitySystem; } //~--- methods -------------------------------------------------------------- @@ -295,7 +301,8 @@ public class AuthenticationResource */ private ScmState createAnonymousState() { - return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST); + return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST, + Collections.EMPTY_LIST); } /** @@ -314,7 +321,14 @@ public class AuthenticationResource User user = collection.oneByType(User.class); GroupNames groups = collection.oneByType(GroupNames.class); - return createState(user, groups.getCollection()); + List ap = Collections.EMPTY_LIST; + + if (subject.hasRole(Role.ADMIN)) + { + ap = securitySystem.getAvailablePermissions(); + } + + return createState(user, groups.getCollection(), ap); } /** @@ -323,14 +337,16 @@ public class AuthenticationResource * * @param user * @param groups + * @param availablePermissions * * @return */ - private ScmState createState(User user, Collection groups) + private ScmState createState(User user, Collection groups, + List availablePermissions) { return new ScmState(contextProvider, user, groups, repositoryManger.getConfiguredTypes(), userManager.getDefaultType(), - new ScmClientConfig(configuration)); + new ScmClientConfig(configuration), availablePermissions); } //~--- fields --------------------------------------------------------------- @@ -344,6 +360,9 @@ public class AuthenticationResource /** Field description */ private RepositoryManager repositoryManger; + /** Field description */ + private SecuritySystem securitySystem; + /** Field description */ private UserManager userManager; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupPermissionResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupPermissionResource.java new file mode 100644 index 0000000000..6468397f4a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupPermissionResource.java @@ -0,0 +1,135 @@ +/** + * 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.Predicate; + +import sonia.scm.api.rest.Permission; +import sonia.scm.security.AssignedPermission; +import sonia.scm.security.SecuritySystem; + +/** + * + * @author Sebastian Sdorra + */ +public class GroupPermissionResource extends AbstractPermissionResource +{ + + /** + * Constructs ... + * + * + * @param securitySystem + * @param name + */ + public GroupPermissionResource(SecuritySystem securitySystem, String name) + { + super(securitySystem, name); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param permission + * + * @return + */ + @Override + protected AssignedPermission transformPermission(Permission permission) + { + return new AssignedPermission(name, true, permission.getValue()); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + protected Predicate getPredicate() + { + return new GroupPredicate(name); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 13/05/01 + * @author Enter your name here... + */ + private static class GroupPredicate implements Predicate + { + + /** + * Constructs ... + * + * + * @param name + */ + public GroupPredicate(String name) + { + this.name = name; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param input + * + * @return + */ + @Override + public boolean apply(AssignedPermission input) + { + return input.isGroupPermission() && input.getName().equals(name); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private String name; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/SecuritySystemResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/SecuritySystemResource.java new file mode 100644 index 0000000000..6f9e638b14 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/SecuritySystemResource.java @@ -0,0 +1,107 @@ +/** + * 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 org.apache.shiro.SecurityUtils; + +import sonia.scm.security.Role; +import sonia.scm.security.SecuritySystem; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * + * @author Sebastian Sdorra + */ +@Path("security/permission") +public class SecuritySystemResource +{ + + /** + * Constructs ... + * + * + * @param system + */ + @Inject + public SecuritySystemResource(SecuritySystem system) + { + this.system = system; + + // only administrators can use this resource + SecurityUtils.getSubject().checkRole(Role.ADMIN); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param group + * + * @return + */ + @Path("group/{group}") + public GroupPermissionResource getGroupSubResource( + @PathParam("group") String group) + { + return new GroupPermissionResource(system, group); + } + + /** + * Method description + * + * + * @param user + * + * @return + */ + @Path("user/{user}") + public UserPermissionResource getUserSubResource( + @PathParam("user") String user) + { + return new UserPermissionResource(system, user); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private SecuritySystem system; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserPermissionResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserPermissionResource.java new file mode 100644 index 0000000000..da1ae99512 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserPermissionResource.java @@ -0,0 +1,135 @@ +/** + * 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.Predicate; + +import sonia.scm.api.rest.Permission; +import sonia.scm.security.AssignedPermission; +import sonia.scm.security.SecuritySystem; + +/** + * + * @author Sebastian Sdorra + */ +public class UserPermissionResource extends AbstractPermissionResource +{ + + /** + * Constructs ... + * + * + * @param securitySystem + * @param name + */ + public UserPermissionResource(SecuritySystem securitySystem, String name) + { + super(securitySystem, name); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param permission + * + * @return + */ + @Override + protected AssignedPermission transformPermission(Permission permission) + { + return new AssignedPermission(name, permission.getValue()); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + protected Predicate getPredicate() + { + return new UserPredicate(name); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 13/05/01 + * @author Enter your name here... + */ + private static class UserPredicate implements Predicate + { + + /** + * Constructs ... + * + * + * @param name + */ + public UserPredicate(String name) + { + this.name = name; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param input + * + * @return + */ + @Override + public boolean apply(AssignedPermission input) + { + return !input.isGroupPermission() && input.getName().equals(name); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private String name; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/DefaultSecuritySystem.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultSecuritySystem.java new file mode 100644 index 0000000000..2e1986d6d7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultSecuritySystem.java @@ -0,0 +1,501 @@ +/** + * 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.security; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.apache.shiro.SecurityUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.HandlerEvent; +import sonia.scm.event.ScmEventBus; +import sonia.scm.event.Subscriber; +import sonia.scm.group.GroupEvent; +import sonia.scm.store.ConfigurationEntryStore; +import sonia.scm.store.ConfigurationEntryStoreFactory; +import sonia.scm.user.UserEvent; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +import java.net.URL; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Map.Entry; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * TODO add events + * + * @author Sebastian Sdorra + * @since 1.31 + */ +@Singleton +@Subscriber(async = true) +public class DefaultSecuritySystem implements SecuritySystem +{ + + /** Field description */ + private static final String NAME = "security"; + + /** Field description */ + private static final String PERMISSION_DESCRIPTOR = + "META-INF/scm/permissions.xml"; + + /** + * the logger for DefaultSecuritySystem + */ + private static final Logger logger = + LoggerFactory.getLogger(DefaultSecuritySystem.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param storeFactory + */ + @Inject + public DefaultSecuritySystem(ConfigurationEntryStoreFactory storeFactory) + { + store = storeFactory.getStore(AssignedPermission.class, NAME); + readAvailablePermissions(); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param permission + * + * @return + */ + @Override + public StoredAssignedPermission addPermission(AssignedPermission permission) + { + assertIsAdmin(); + + String id = store.put(permission); + + StoredAssignedPermission sap = new StoredAssignedPermission(id, permission); + + //J- + ScmEventBus.getInstance().post( + new StoredAssignedPermissionEvent(HandlerEvent.CREATE, sap) + ); + //J+ + + return sap; + } + + /** + * Method description + * + * + * @param permission + */ + @Override + public void deletePermission(StoredAssignedPermission permission) + { + assertIsAdmin(); + store.remove(permission.getId()); + //J- + ScmEventBus.getInstance().post( + new StoredAssignedPermissionEvent(HandlerEvent.CREATE, permission) + ); + //J+ + } + + /** + * Method description + * + * + * @param id + */ + @Override + public void deletePermission(String id) + { + assertIsAdmin(); + + AssignedPermission ap = store.get(id); + + if (ap != null) + { + deletePermission(new StoredAssignedPermission(id, ap)); + } + } + + /** + * Method description + * + * + * @param event + */ + @Subscribe + public void handleEvent(final UserEvent event) + { + if (event.getEventType() == HandlerEvent.DELETE) + { + deletePermissions(new Predicate() + { + + @Override + public boolean apply(AssignedPermission p) + { + return !p.isGroupPermission() + && event.getItem().getName().equals(p.getName()); + } + }); + } + } + + /** + * Method description + * + * + * @param event + */ + @Subscribe + public void handleEvent(final GroupEvent event) + { + if (event.getEventType() == HandlerEvent.DELETE) + { + deletePermissions(new Predicate() + { + + @Override + public boolean apply(AssignedPermission p) + { + return p.isGroupPermission() + && event.getItem().getName().equals(p.getName()); + } + }); + } + } + + /** + * Method description + * + * + * @param permission + */ + @Override + public void modifyPermission(StoredAssignedPermission permission) + { + assertIsAdmin(); + + synchronized (store) + { + store.remove(permission.getId()); + store.put(permission.getId(), new AssignedPermission(permission)); + } + + //J- + ScmEventBus.getInstance().post( + new StoredAssignedPermissionEvent(HandlerEvent.CREATE, permission) + ); + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public List getAllPermissions() + { + return getPermissions(null); + } + + /** + * Method description + * + * + * @return + */ + @Override + public List getAvailablePermissions() + { + assertIsAdmin(); + + return availablePermissions; + } + + /** + * Method description + * + * + * @param id + * + * @return + */ + @Override + public StoredAssignedPermission getPermission(String id) + { + assertIsAdmin(); + + StoredAssignedPermission sap = null; + AssignedPermission ap = store.get(id); + + if (ap != null) + { + sap = new StoredAssignedPermission(id, ap); + } + + return sap; + } + + /** + * Method description + * + * + * @param predicate + * + * @return + */ + @Override + public List getPermissions( + Predicate predicate) + { + Builder permissions = ImmutableList.builder(); + + for (Entry e : store.getAll().entrySet()) + { + if ((predicate == null) || predicate.apply(e.getValue())) + { + permissions.add(new StoredAssignedPermission(e.getKey(), e.getValue())); + } + } + + return permissions.build(); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + private void assertIsAdmin() + { + SecurityUtils.getSubject().checkRole(Role.ADMIN); + } + + /** + * Method description + * + * + * @param predicate + */ + private void deletePermissions(Predicate predicate) + { + List permissions = getPermissions(predicate); + + for (StoredAssignedPermission permission : permissions) + { + deletePermission(permission); + } + } + + /** + * Method description + * + * + * @param context + * @param descriptorUrl + * + * @return + */ + private List parsePermissionDescriptor( + JAXBContext context, URL descriptorUrl) + { + List descriptors = Collections.EMPTY_LIST; + + try + { + PermissionDescriptors descriptorWrapper = + (PermissionDescriptors) context.createUnmarshaller().unmarshal( + descriptorUrl); + + descriptors = descriptorWrapper.getPermissions(); + + logger.debug("found {} permissions at {}", descriptors.size(), + descriptorUrl); + logger.trace("permissions from {}: {}", descriptorUrl, descriptors); + } + catch (JAXBException ex) + { + logger.error("could not parse permission descriptor", ex); + } + + return descriptors; + } + + /** + * Method description + * + */ + private void readAvailablePermissions() + { + Builder builder = ImmutableList.builder(); + + try + { + JAXBContext context = + JAXBContext.newInstance(PermissionDescriptors.class); + + Enumeration descirptorEnum = + getClassLoader().getResources(PERMISSION_DESCRIPTOR); + + while (descirptorEnum.hasMoreElements()) + { + URL descriptorUrl = descirptorEnum.nextElement(); + + logger.debug("read permission descriptor from {}", descriptorUrl); + + builder.addAll(parsePermissionDescriptor(context, descriptorUrl)); + } + } + catch (IOException ex) + { + logger.error("could not read permission descriptors", ex); + } + catch (JAXBException ex) + { + logger.error( + "could not create jaxb context to read permission descriptors", ex); + } + + availablePermissions = builder.build(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private ClassLoader getClassLoader() + { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if (classLoader == null) + { + logger.warn("could not find context classloader, use default"); + + classLoader = DefaultSecuritySystem.class.getClassLoader(); + } + + return classLoader; + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 13/04/30 + * @author Enter your name here... + */ + @XmlRootElement(name = "permissions") + @XmlAccessorType(XmlAccessType.FIELD) + private static class PermissionDescriptors + { + + /** + * Constructs ... + * + */ + public PermissionDescriptors() {} + + //~--- get methods -------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public List getPermissions() + { + if (permissions == null) + { + permissions = Collections.EMPTY_LIST; + } + + return permissions; + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + @XmlElement(name = "permission") + private List permissions; + } + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final ConfigurationEntryStore store; + + /** Field description */ + private List availablePermissions; +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java b/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java index 2cfa12e64b..94e49d0287 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java +++ b/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java @@ -36,6 +36,7 @@ package sonia.scm.security; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Joiner; +import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; @@ -128,6 +129,7 @@ public class ScmRealm extends AuthorizingRealm * * * @param configuration + * @param securitySystem * @param cacheManager * @param userManager * @param groupManager @@ -139,7 +141,8 @@ public class ScmRealm extends AuthorizingRealm * @param responseProvider */ @Inject - public ScmRealm(ScmConfiguration configuration, CacheManager cacheManager, + public ScmRealm(ScmConfiguration configuration, + SecuritySystem securitySystem, CacheManager cacheManager, UserManager userManager, GroupManager groupManager, RepositoryDAO repositoryDAO, UserDAO userDAO, AuthenticationManager authenticator, RepositoryManager manager, @@ -147,6 +150,7 @@ public class ScmRealm extends AuthorizingRealm Provider responseProvider) { this.configuration = configuration; + this.securitySystem = securitySystem; this.userManager = userManager; this.groupManager = groupManager; this.repositoryDAO = repositoryDAO; @@ -194,6 +198,27 @@ public class ScmRealm extends AuthorizingRealm } } + /** + * Method description + * + * + * @param event + */ + @Subscribe + public void onEvent(StoredAssignedPermissionEvent event) + { + if (event.getEventType().isPost()) + { + if (logger.isDebugEnabled()) + { + logger.debug("clear cache, because permission {} has changed", + event.getPermission().getId()); + } + + cache.clear(); + } + } + /** * Method description * @@ -474,6 +499,51 @@ public class ScmRealm extends AuthorizingRealm } } + /** + * Method description + * + * + * @param user + * @param groups + * + * @return + */ + private List collectGlobalPermissions(final User user, + final GroupNames groups) + { + if (logger.isTraceEnabled()) + { + logger.trace("collect global permissions for user {}", user.getName()); + } + + List permissions = Lists.newArrayList(); + + List globalPermissions = + securitySystem.getPermissions(new Predicate() + { + + @Override + public boolean apply(AssignedPermission input) + { + return isUserPermission(user, groups, input); + } + }); + + for (StoredAssignedPermission gp : globalPermissions) + { + if (logger.isTraceEnabled()) + { + logger.trace("add permission {} for user {}", gp.getPermission(), + user.getName()); + } + + permissions.add(gp.getPermission()); + + } + + return permissions; + } + /** * Method description * @@ -585,7 +655,8 @@ public class ScmRealm extends AuthorizingRealm GroupNames groups) { Set roles = Sets.newHashSet(); - List permissions = null; + List permissions; + List globalPermissions = null; roles.add(Role.USER); @@ -604,12 +675,18 @@ public class ScmRealm extends AuthorizingRealm else { permissions = collectRepositoryPermissions(user, groups); + globalPermissions = collectGlobalPermissions(user, groups); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); info.addObjectPermissions(permissions); + if (globalPermissions != null) + { + info.addStringPermissions(globalPermissions); + } + return info; } @@ -737,7 +814,7 @@ public class ScmRealm extends AuthorizingRealm * @return */ private boolean isUserPermission(User user, GroupNames groups, - Permission perm) + PermissionObject perm) { //J- return (perm.isGroupPermission() && groups.contains(perm.getName())) @@ -768,6 +845,9 @@ public class ScmRealm extends AuthorizingRealm /** Field description */ private Provider responseProvider; + /** Field description */ + private SecuritySystem securitySystem; + /** Field description */ private UserDAO userDAO; diff --git a/scm-webapp/src/main/resources/META-INF/scm/permissions.xml b/scm-webapp/src/main/resources/META-INF/scm/permissions.xml new file mode 100644 index 0000000000..4371b17c5a --- /dev/null +++ b/scm-webapp/src/main/resources/META-INF/scm/permissions.xml @@ -0,0 +1,54 @@ + + + + + + + + All Repository (read) + Read access to all repositories + repository:*:READ + + + + All Repository (write) + Write access to all repositories + repository:*:WRITE + + + + All Repository (owner) + Owner access to all repositories + repository:*:OWNER + + + diff --git a/scm-webapp/src/main/webapp/index.mustache b/scm-webapp/src/main/webapp/index.mustache index 869112096e..32929592a7 100644 --- a/scm-webapp/src/main/webapp/index.mustache +++ b/scm-webapp/src/main/webapp/index.mustache @@ -143,6 +143,10 @@ + + + + diff --git a/scm-webapp/src/main/webapp/resources/js/group/sonia.group.formpanel.js b/scm-webapp/src/main/webapp/resources/js/group/sonia.group.formpanel.js index 5242342050..5fb96830b2 100644 --- a/scm-webapp/src/main/webapp/resources/js/group/sonia.group.formpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/group/sonia.group.formpanel.js @@ -47,7 +47,7 @@ Sonia.group.FormPanel = Ext.extend(Sonia.rest.FormPanel,{ initComponent: function(){ this.addEvents('preCreate', 'created', 'preUpdate', 'updated', 'updateFailed', 'creationFailed'); - Sonia.repository.FormPanel.superclass.initComponent.apply(this, arguments); + Sonia.group.FormPanel.superclass.initComponent.apply(this, arguments); }, update: function(group){ diff --git a/scm-webapp/src/main/webapp/resources/js/group/sonia.group.grid.js b/scm-webapp/src/main/webapp/resources/js/group/sonia.group.grid.js index 17e2f90d9a..c6be5f151a 100644 --- a/scm-webapp/src/main/webapp/resources/js/group/sonia.group.grid.js +++ b/scm-webapp/src/main/webapp/resources/js/group/sonia.group.grid.js @@ -105,7 +105,7 @@ Sonia.group.Grid = Ext.extend(Sonia.rest.Grid, { renderMembers: function(members){ var out = ''; - if ( members != null ){ + if ( members !== null ){ var s = members.length; for ( var i=0; i',{ + id: 'permissionGridHelp', + xtype: 'box', + autoEl: { + tag: 'img', + src: this.helpIcon + } + }] + }] + }; + + Ext.apply(this, Ext.apply(this.initialConfig, config)); + Sonia.security.PermissionsPanel.superclass.initComponent.apply(this, arguments); + }, + + getIdFromResponse: function(response){ + var id = null; + var location = response.getResponseHeader('Location'); + if (location){ + var parts = location.split('/'); + id = parts[parts.length - 1]; + } + return id; + }, + + modifyOrAddPermission: function(store, record){ + var id = record.get('id'); + if ( id ){ + this.modifyPermission(id, record); + } else { + this.addPermission(record); + } + }, + + addPermission: function(record){ + Ext.Ajax.request({ + url: restUrl + this.baseUrl + '.json', + method: 'POST', + jsonData: record.data, + scope: this, + success: function(response){ + var id = this.getIdFromResponse(response); + record.data.id = id; + }, + failure: function(result){ + } + }); + }, + + modifyPermission: function(id, record){ + Ext.Ajax.request({ + url: restUrl + this.baseUrl + '/' + id + '.json', + method: 'PUT', + jsonData: record.data, + scope: this, + success: function(){ + }, + failure: function(result){ + } + }); + }, + + removePermission: function(store, record){ + Ext.Ajax.request({ + url: restUrl + this.baseUrl + '/' + record.get('id') + '.json', + method: 'DELETE', + scope: this, + success: function(){ + + }, + failure: function(result){ + } + }); + }, + + renderPermission: function(value){ + var ap = state.availablePermissions; + for (var i=0; i list = securitySystem.getAvailablePermissions(); + + assertNotNull(list); + assertThat(list.size(), greaterThan(0)); + } + + /** + * Method description + * + */ + @Test + public void testDeletePermission() + { + setAdminSubject(); + + StoredAssignedPermission sap = createPermission("trillian", false, + "repository:*:READ"); + + securitySystem.deletePermission(sap); + + assertNull(securitySystem.getPermission(sap.getId())); + } + + /** + * Method description + * + */ + @Test + public void testGetAllPermissions() + { + setAdminSubject(); + + StoredAssignedPermission trillian = createPermission("trillian", false, + "repository:*:READ"); + StoredAssignedPermission dent = createPermission("dent", false, + "repository:*:READ"); + StoredAssignedPermission marvin = createPermission("marvin", false, + "repository:*:READ"); + + List all = securitySystem.getAllPermissions(); + + assertEquals(3, all.size()); + assertThat(all, containsInAnyOrder(trillian, dent, marvin)); + } + + /** + * Method description + * + */ + @Test + public void testGetPermission() + { + setAdminSubject(); + + StoredAssignedPermission sap = createPermission("trillian", false, + "repository:*:READ"); + + StoredAssignedPermission other = securitySystem.getPermission(sap.getId()); + + assertEquals(sap.getId(), other.getId()); + assertEquals(sap, other); + } + + /** + * Method description + * + */ + @Test + public void testGetPermissionsWithPredicate() + { + setAdminSubject(); + + StoredAssignedPermission trillian = createPermission("trillian", false, + "repository:*:READ"); + StoredAssignedPermission dent = createPermission("dent", false, + "repository:*:READ"); + + createPermission("hitchhiker", true, "repository:*:READ"); + + List filtered = + securitySystem.getPermissions(new Predicate() + { + + @Override + public boolean apply(AssignedPermission input) + { + return !input.isGroupPermission(); + } + }); + + assertEquals(2, filtered.size()); + assertThat(filtered, containsInAnyOrder(trillian, dent)); + } + + /** + * Method description + * + */ + @Test + public void testModifyPermission() + { + setAdminSubject(); + + StoredAssignedPermission sap = createPermission("trillian", false, + "repository:*:READ"); + StoredAssignedPermission modified = + new StoredAssignedPermission(sap.getId(), + new AssignedPermission("trillian", "repository:*:WRITE")); + + securitySystem.modifyPermission(modified); + + sap = securitySystem.getPermission(modified.getId()); + + assertEquals(modified.getId(), sap.getId()); + assertEquals(modified, sap); + } + + /** + * Method description + * + */ + @Test(expected = UnauthorizedException.class) + public void testUnauthorizedAddPermission() + { + setUserSubject(); + createPermission("trillian", false, "repository:*:READ"); + } + + /** + * Method description + * + */ + @Test(expected = UnauthorizedException.class) + public void testUnauthorizedDeletePermission() + { + setAdminSubject(); + + StoredAssignedPermission sap = createPermission("trillian", false, + "repository:*:READ"); + + setUserSubject(); + securitySystem.deletePermission(sap); + } + + /** + * Method description + * + */ + @Test(expected = UnauthorizedException.class) + public void testUnauthorizedGetPermission() + { + setAdminSubject(); + + StoredAssignedPermission sap = createPermission("trillian", false, + "repository:*:READ"); + + setUserSubject(); + securitySystem.getPermission(sap.getId()); + } + + /** + * Method description + * + */ + @Test(expected = UnauthorizedException.class) + public void testUnauthorizedModifyPermission() + { + setAdminSubject(); + + StoredAssignedPermission sap = createPermission("trillian", false, + "repository:*:READ"); + + setUserSubject(); + + securitySystem.modifyPermission(sap); + } + + /** + * Method description + * + * + * @param name + * @param groupPermission + * @param value + * + * @return + */ + private StoredAssignedPermission createPermission(String name, + boolean groupPermission, String value) + { + AssignedPermission ap = new AssignedPermission(name, groupPermission, + value); + StoredAssignedPermission sap = securitySystem.addPermission(ap); + + assertNotNull(sap); + assertNotNull(sap.getId()); + + return sap; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + */ + private void setAdminSubject() + { + setSubject(MockUtil.createAdminSubject()); + } + + /** + * Method description + * + */ + private void setUserSubject() + { + org.apache.shiro.mgt.SecurityManager sm = + new DefaultSecurityManager(new SimpleAccountRealm()); + + setSubject(MockUtil.createUserSubject(sm)); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private DefaultSecuritySystem securitySystem; +} diff --git a/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java b/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java index 21d9f52cd7..443f297d32 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java @@ -35,6 +35,7 @@ package sonia.scm.security; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.inject.Provider; @@ -50,6 +51,8 @@ import org.apache.shiro.subject.PrincipalCollection; import org.junit.Test; +import org.mockito.Mockito; + import sonia.scm.cache.MapCacheManager; import sonia.scm.config.ScmConfiguration; import sonia.scm.group.Group; @@ -76,6 +79,7 @@ import static org.mockito.Mockito.*; //~--- JDK imports ------------------------------------------------------------ import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @@ -464,9 +468,16 @@ public class ScmRealmTest AuthenticationResult.NOT_FOUND ); + SecuritySystem securitySystem = mock(SecuritySystem.class); + when( + securitySystem.getPermissions(Mockito.any()) + ).thenReturn( + Collections.EMPTY_LIST + ); return new ScmRealm( new ScmConfiguration(), + securitySystem, new MapCacheManager(), userManager, groupManager,