diff --git a/pom.xml b/pom.xml
index 712e47fde9..652e4ef2c0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -198,6 +198,8 @@
true
${project.build.javaLevel}
${project.build.javaLevel}
+ ${project.test.javaLevel}
+ ${project.test.javaLevel}
${project.build.sourceEncoding}
1.7
+ 1.8
UTF-8
SCM-BSD
diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryPermissions.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryPermissions.java
index 001034f606..a0cf08cdc1 100644
--- a/scm-core/src/main/java/sonia/scm/repository/RepositoryPermissions.java
+++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryPermissions.java
@@ -37,6 +37,7 @@ import sonia.scm.security.PermissionActionCheck;
import sonia.scm.security.PermissionCheck;
/**
+ * Permission checks for repository related permissions.
*
* @author Sebastian Sdorra
* @since 2.0.0
@@ -44,30 +45,31 @@ import sonia.scm.security.PermissionCheck;
public final class RepositoryPermissions
{
- /** Field description */
+ /** create permission */
public static final String ACTION_CREATE = "create";
- /** Field description */
+ /** delete permission */
public static final String ACTION_DELETE = "delete";
- /** Field description */
+ /** modify permission */
public static final String ACTION_MODIFY = "modify";
- /** Field description */
+ /** health check permission implies modify permission */
public static final String ACTION_HC = "hc,".concat(ACTION_MODIFY);
- /** Field description */
+ /** read permission */
public static final String ACTION_READ = "read";
- /** Field description */
+ /** write permission */
public static final String ACTION_WRITE = "write";
- /** Field description */
- public static final String SEPERATOR = ":";
+ /** permission separator */
+ public static final String SEPARATOR = ":";
+ /** permission action separator */
public static final String SEPERATOR_ACTION = ",";
- /** Field description */
+ /** permission main type */
public static final String TYPE = "repository";
//~--- constructors ---------------------------------------------------------
@@ -77,10 +79,9 @@ public final class RepositoryPermissions
//~--- methods --------------------------------------------------------------
/**
- * Method description
+ * Returns permission check for create action.
*
- *
- * @return
+ * @return permission check for create action
*/
public static PermissionCheck create()
{
@@ -88,12 +89,11 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission check for delete action.
*
+ * @param r repository for permission check
*
- * @param r
- *
- * @return
+ * @return permission check for delete action
*/
public static PermissionCheck delete(Repository r)
{
@@ -101,23 +101,21 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission check for delete action.
*
+ * @param id id of repository for permission check
*
- * @param id
- *
- * @return
+ * @return permission check for delete action
*/
public static PermissionCheck delete(String id)
{
- return check(ACTION_DELETE.concat(SEPERATOR).concat(id));
+ return check(ACTION_DELETE.concat(SEPARATOR).concat(id));
}
/**
- * Method description
+ * Returns permission action check for delete action.
*
- *
- * @return
+ * @return permission action check for delete action
*/
public static PermissionActionCheck delete()
{
@@ -125,37 +123,34 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission check for health check action.
*
+ * @param id id of repository for permission check
*
- * @param id
- *
- * @return
+ * @return permission check for health check action
*/
public static PermissionCheck healthCheck(String id)
{
- return check(ACTION_HC.concat(SEPERATOR).concat(id));
+ return check(ACTION_HC.concat(SEPARATOR).concat(id));
}
/**
- * Method description
+ * Returns permission action check for health check action.
*
- *
- * @return
+ * @return permission action check for health check action.
*/
public static PermissionActionCheck healthCheck()
{
return new PermissionActionCheck<>(
- TYPE.concat(SEPERATOR).concat(ACTION_HC));
+ TYPE.concat(SEPARATOR).concat(ACTION_HC));
}
/**
- * Method description
+ * Returns permission check for health check action.
*
+ * @param r repository for permission check
*
- * @param r
- *
- * @return
+ * @return permission check for health check action
*/
public static PermissionCheck healthCheck(Repository r)
{
@@ -163,12 +158,11 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission action check for modify action.
*
+ * @param r repository for permission check
*
- * @param r
- *
- * @return
+ * @return permission action check for modify action
*/
public static PermissionCheck modify(Repository r)
{
@@ -176,23 +170,22 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission action check for modify action.
*
*
- * @param id
+ * @param id id of repository for permission check
*
- * @return
+ * @return permission action check for modify action
*/
public static PermissionCheck modify(String id)
{
- return check(ACTION_MODIFY.concat(SEPERATOR).concat(id));
+ return check(ACTION_MODIFY.concat(SEPARATOR).concat(id));
}
/**
- * Method description
+ * Returns permission action check for modify action.
*
- *
- * @return
+ * @return permission action check for modify action
*/
public static PermissionActionCheck modify()
{
@@ -200,25 +193,23 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission check for read action.
*
+ * @param id id of repository for permission check
*
- * @param id
- *
- * @return
+ * @return permission check for read action
*/
public static PermissionCheck read(String id)
{
- return check(ACTION_READ.concat(SEPERATOR).concat(id));
+ return check(ACTION_READ.concat(SEPARATOR).concat(id));
}
/**
- * Method description
+ * Returns permission check for read action.
*
+ * @param r repository for permission check
*
- * @param r
- *
- * @return
+ * @return permission check for read action
*/
public static PermissionCheck read(Repository r)
{
@@ -226,10 +217,9 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission action check for read action.
*
- *
- * @return
+ * @return permission action check for read action
*/
public static PermissionActionCheck read()
{
@@ -237,25 +227,23 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Returns permission check for write action.
*
+ * @param id id of repository for permission check
*
- * @param id
- *
- * @return
+ * @return permission check for write action
*/
public static PermissionCheck write(String id)
{
- return check(ACTION_WRITE.concat(SEPERATOR).concat(id));
+ return check(ACTION_WRITE.concat(SEPARATOR).concat(id));
}
/**
- * Method description
+ * Returns permission check for write action.
*
+ * @param r repository for permission check
*
- * @param r
- *
- * @return
+ * @return permission check for write action
*/
public static PermissionCheck write(Repository r)
{
@@ -263,10 +251,9 @@ public final class RepositoryPermissions
}
/**
- * Method description
+ * Return permission action check for write action.
*
- *
- * @return
+ * @return permission action check for write action
*/
public static PermissionActionCheck write()
{
@@ -275,11 +262,11 @@ public final class RepositoryPermissions
private static PermissionActionCheck actionCheck(String action)
{
- return new PermissionActionCheck<>(TYPE.concat(SEPERATOR).concat(action));
+ return new PermissionActionCheck<>(TYPE.concat(SEPARATOR).concat(action));
}
private static PermissionCheck check(String permission)
{
- return new PermissionCheck(TYPE.concat(SEPERATOR).concat(permission));
+ return new PermissionCheck(TYPE.concat(SEPARATOR).concat(permission));
}
}
diff --git a/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionsTest.java b/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionsTest.java
new file mode 100644
index 0000000000..4457941c52
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionsTest.java
@@ -0,0 +1,224 @@
+/**
+ * Copyright (c) 2014, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+package sonia.scm.repository;
+
+import com.github.sdorra.shiro.ShiroRule;
+import com.github.sdorra.shiro.SubjectAware;
+import java.util.function.Supplier;
+import org.apache.shiro.authz.UnauthorizedException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.runners.MockitoJUnitRunner;
+import sonia.scm.security.PermissionActionCheck;
+import sonia.scm.security.PermissionCheck;
+
+/**
+ * Unit tests for {@link RepositoryPermissions}.
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SubjectAware(configuration = "classpath:sonia/scm/repository/repository-permissions.ini")
+public class RepositoryPermissionsTest {
+
+ @Mock
+ private Repository repository;
+
+ /**
+ * Shiro rule.
+ */
+ @Rule
+ public ShiroRule shiro = new ShiroRule();
+
+ /**
+ * Test permission checks with id successful.
+ */
+ @Test
+ @SubjectAware(username = "permitted", password = "secret")
+ public void testPermissionCheckWithId() {
+ assertPermissionCheckId(RepositoryPermissions::read);
+ assertPermissionCheckId(RepositoryPermissions::write);
+ assertPermissionCheckId(RepositoryPermissions::delete);
+ assertPermissionCheckId(RepositoryPermissions::modify);
+ assertPermissionCheckId(RepositoryPermissions::healthCheck);
+ }
+
+ /**
+ * Test permission checks with id successful.
+ */
+ @Test
+ @SubjectAware(username = "permitted", password = "secret")
+ public void testPermissionActionCheck() {
+ assertPermissionActionCheck(RepositoryPermissions::read);
+ assertPermissionActionCheck(RepositoryPermissions::write);
+ assertPermissionActionCheck(RepositoryPermissions::delete);
+ assertPermissionActionCheck(RepositoryPermissions::modify);
+ assertPermissionActionCheck(RepositoryPermissions::healthCheck);
+ }
+
+ @Test
+ @SubjectAware(username = "notpermitted", password = "secret")
+ public void testPermissionActionCheckWithoutRequired() {
+ assertFailedPermissionActionCheck(RepositoryPermissions::read);
+ assertFailedPermissionActionCheck(RepositoryPermissions::write);
+ assertFailedPermissionActionCheck(RepositoryPermissions::delete);
+ assertFailedPermissionActionCheck(RepositoryPermissions::modify);
+ assertFailedPermissionActionCheck(RepositoryPermissions::healthCheck);
+ }
+
+ /**
+ * Test permission checks with repository successful.
+ */
+ @Test
+ @SubjectAware(username = "permitted", password = "secret")
+ public void testPermissionCheckWithRepository() {
+ assertPermissionCheckRepository(RepositoryPermissions::read);
+ assertPermissionCheckRepository(RepositoryPermissions::write);
+ assertPermissionCheckRepository(RepositoryPermissions::delete);
+ assertPermissionCheckRepository(RepositoryPermissions::modify);
+ assertPermissionCheckRepository(RepositoryPermissions::healthCheck);
+ }
+
+ /**
+ * Test permission checks with id and without the required permissions.
+ */
+ @Test
+ @SubjectAware(username = "notpermitted", password = "secret")
+ public void testPermissionCheckWithIdAndWithoutRequiredPermissions() {
+ assertFailedPermissionCheckId(RepositoryPermissions::read);
+ assertFailedPermissionCheckId(RepositoryPermissions::write);
+ assertFailedPermissionCheckId(RepositoryPermissions::delete);
+ assertFailedPermissionCheckId(RepositoryPermissions::modify);
+ assertFailedPermissionCheckId(RepositoryPermissions::healthCheck);
+ }
+
+ /**
+ * Tests permission checks with repository and without the required permissions.
+ */
+ @Test
+ @SubjectAware(username = "notpermitted", password = "secret")
+ public void testPermissionCheckWithRepositoryAndWithoutRequiredPermissions() {
+ assertFailedPermissionCheckRepository(RepositoryPermissions::read);
+ assertFailedPermissionCheckRepository(RepositoryPermissions::write);
+ assertFailedPermissionCheckRepository(RepositoryPermissions::delete);
+ assertFailedPermissionCheckRepository(RepositoryPermissions::modify);
+ assertFailedPermissionCheckRepository(RepositoryPermissions::healthCheck);
+ }
+
+ private void assertFailedPermissionCheckRepository(RepositoryPermissionChecker checker) {
+ when(repository.getId()).thenReturn("abc");
+ assertFailedPermissionCheck(checker.get(repository));
+ }
+
+ private void assertFailedPermissionCheckId(RepositoryPermissionChecker checker) {
+ assertFailedPermissionCheck(checker.get("abc"));
+ }
+
+ private void assertFailedPermissionCheck(PermissionCheck check) {
+ assertNotNull(check);
+ assertFalse(check.isPermitted());
+ boolean fail = false;
+ try {
+ check.check();
+ }
+ catch (UnauthorizedException ex) {
+ fail = true;
+ }
+ assertTrue(fail);
+ }
+
+ private void assertPermissionCheckRepository(RepositoryPermissionChecker checker) {
+ when(repository.getId()).thenReturn("abc");
+ assertPermissionCheck(checker.get(repository));
+ }
+
+ private void assertPermissionCheckId(RepositoryPermissionChecker checker) {
+ assertPermissionCheck(checker.get("abc"));
+ }
+
+ private void assertPermissionCheck(PermissionCheck check) {
+ assertNotNull(check);
+ assertTrue(check.isPermitted());
+ check.check();
+ }
+
+ private void assertPermissionActionCheck(Supplier> supplier) {
+ assertPermissionActionCheck(supplier.get());
+ }
+
+ private void assertFailedPermissionActionCheck(Supplier> supplier) {
+ assertFailedPermissionActionCheck(supplier.get());
+ }
+
+ private void assertFailedPermissionActionCheck(PermissionActionCheck check) {
+ assertNotNull(check);
+ assertFalse(check.isPermitted("abc"));
+ boolean fail = false;
+ try {
+ check.check("abc");
+ }
+ catch (UnauthorizedException ex) {
+ fail = true;
+ }
+ assertTrue(fail);
+ fail = false;
+ when(repository.getId()).thenReturn("abc");
+ assertFalse(check.isPermitted(repository));
+ try {
+ check.check(repository);
+ }
+ catch (UnauthorizedException ex) {
+ fail = true;
+ }
+ assertTrue(fail);
+ }
+
+ private void assertPermissionActionCheck(PermissionActionCheck check) {
+ assertNotNull(check);
+ assertTrue(check.isPermitted("abc"));
+ check.check("abc");
+ when(repository.getId()).thenReturn("abc");
+ assertTrue(check.isPermitted(repository));
+ check.check(repository);
+ }
+
+ @FunctionalInterface
+ private static interface RepositoryPermissionChecker {
+
+ public PermissionCheck get(T type);
+
+ }
+
+}
diff --git a/scm-core/src/test/resources/sonia/scm/repository/repository-permissions.ini b/scm-core/src/test/resources/sonia/scm/repository/repository-permissions.ini
new file mode 100644
index 0000000000..e148e48f14
--- /dev/null
+++ b/scm-core/src/test/resources/sonia/scm/repository/repository-permissions.ini
@@ -0,0 +1,6 @@
+[users]
+notpermitted = secret
+permitted = secret, permitted
+
+[roles]
+permitted = repository:*:abc
\ No newline at end of file