Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Version 3.3.2 (2017-09-30)

* [new] A `@RequiresCrudPermissions` annotation allows to add permission checks based on the detected CRUD action of the called method.
* [new] SPI `CrudActionResolver` has been added to security to allow for resolving the CRUD action of a particular method.
* [new] A JAX-RS implementation of `CrudActionResolver` detects the CRUD action based upon the JAX-RS annotations.

# Version 3.3.1 (2017-09-06)

* [new] Configuration dump (`config` tool) now dumps inner properties for maps, collections, arrays and complex objects.
Expand Down
2 changes: 1 addition & 1 deletion cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.seedstack.seed</groupId>
<artifactId>seed</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
</parent>

<artifactId>seed-cli</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.seedstack.seed</groupId>
<artifactId>seed</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
</parent>

<artifactId>seed-core</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<groupId>org.seedstack.seed</groupId>
<artifactId>seed</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
<packaging>pom</packaging>

<properties>
Expand Down
2 changes: 1 addition & 1 deletion rest/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.seedstack.seed</groupId>
<artifactId>seed-rest</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
</parent>

<artifactId>seed-rest-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.rest.internal;

import org.seedstack.seed.security.CrudAction;
import org.seedstack.seed.security.spi.CrudActionResolver;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

class RestCrudActionResolver implements CrudActionResolver {
private final Map<Class<? extends Annotation>, CrudAction> annotationMap;

RestCrudActionResolver() {
Map<Class<? extends Annotation>, CrudAction> map = new HashMap<>();
map.put(javax.ws.rs.DELETE.class, CrudAction.DELETE);
map.put(javax.ws.rs.GET.class, CrudAction.READ);
map.put(javax.ws.rs.HEAD.class, CrudAction.READ);
map.put(javax.ws.rs.OPTIONS.class, CrudAction.READ);
map.put(javax.ws.rs.POST.class, CrudAction.CREATE);
map.put(javax.ws.rs.PUT.class, CrudAction.UPDATE);
annotationMap = Collections.unmodifiableMap(map);
}

@Override
public Optional<CrudAction> resolve(Method method) {
return Arrays.stream(method.getAnnotations())
.map(Annotation::annotationType)
.map(x -> annotationMap.getOrDefault(x, null))
.filter(Objects::nonNull)
.findFirst();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.rest.internal;

import org.junit.Before;
import org.junit.Test;
import org.seedstack.seed.security.CrudAction;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;

import static org.assertj.core.api.Assertions.assertThat;

public class RestCrudActionResolverTest {
private RestCrudActionResolver resolverUnderTest;

@Before
public void setup() throws Exception {
resolverUnderTest = new RestCrudActionResolver();
}

@Test
public void test_that_resolves_to_the_right_verb() throws Exception {
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("delete"))).isPresent().contains(CrudAction.DELETE);
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("get"))).isPresent().contains(CrudAction.READ);
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("head"))).isPresent().contains(CrudAction.READ);
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("options"))).isPresent().contains(CrudAction.READ);
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("post"))).isPresent().contains(CrudAction.CREATE);
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("put"))).isPresent().contains(CrudAction.UPDATE);
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("none"))).isNotPresent();
assertThat(resolverUnderTest.resolve(Fixture.class.getMethod("random"))).isNotPresent();
}

// Test Fixture
public static class Fixture {

@DELETE
public void delete() {

}

@GET
public void get() {

}

@Deprecated
public void random() {

}

@HEAD
public void head() {
}

@OPTIONS
public void options() {

}

@POST
public void post() {

}

@PUT
public void put() {

}

public void none() {

}

}

}
2 changes: 1 addition & 1 deletion rest/jersey2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.seedstack.seed</groupId>
<artifactId>seed-rest</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
</parent>

<artifactId>seed-rest-jersey2</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion rest/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.seedstack.seed</groupId>
<artifactId>seed</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
</parent>

<artifactId>seed-rest</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion rest/specs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.seedstack.seed</groupId>
<artifactId>seed-rest</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
</parent>

<artifactId>seed-rest-specs</artifactId>
Expand Down
3 changes: 1 addition & 2 deletions security/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.seedstack.seed</groupId>
<artifactId>seed-security</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.3.2-SNAPSHOT</version>
</parent>

<artifactId>seed-security-core</artifactId>
Expand Down Expand Up @@ -72,7 +72,6 @@
<version>${javax.el.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.security.internal;

import org.apache.shiro.SecurityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.seedstack.seed.it.SeedITRunner;
import org.seedstack.seed.security.AuthorizationException;
import org.seedstack.seed.security.SecuritySupport;
import org.seedstack.seed.security.WithUser;
import org.seedstack.seed.security.internal.fixtures.AnnotatedCrudClass4Security;
import org.seedstack.seed.security.internal.fixtures.AnnotatedCrudMethods4Security;

import javax.inject.Inject;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

@RunWith(SeedITRunner.class)
public class CrudSecurityIT {

@Inject
private AnnotatedCrudClass4Security annotatedClass;

@Inject
private AnnotatedCrudMethods4Security annotatedMethods;

@Inject
private SecuritySupport securitySupport;

@Test
@WithUser(id = "Obiwan", password = "yodarulez")
public void Obiwan_should_not_be_able_to_interpret_anything() {
assertThatThrownBy(() -> annotatedClass.read()).isInstanceOf(AuthorizationException.class);
assertThatThrownBy(() -> annotatedMethods.read()).isInstanceOf(AuthorizationException.class);

}

// RESTBOT
@Test
@WithUser(id = "R2D2", password = "beep")
public void r2d2_should_be_able_to_be_a_restbot() {
// Delete jabba!
assertThat(SecurityUtils.getSubject().isPermitted("jabba:delete")).isTrue();
assertThat(securitySupport.isPermitted("jabba:delete")).isTrue();

// Update c3p0!
assertThat(SecurityUtils.getSubject().isPermitted("c3p0:update")).isTrue();
assertThat(securitySupport.isPermitted("c3p0:update")).isTrue();

// Create X-Wing
assertThat(SecurityUtils.getSubject().isPermitted("xwing:create")).isTrue();
assertThat(securitySupport.isPermitted("xwing:create")).isTrue();

// Read chewaka
assertThat(SecurityUtils.getSubject().isPermitted("chewaka:read")).isTrue();
assertThat(securitySupport.isPermitted("chewaka:read")).isTrue();

// is a restbot
assertThat(SecurityUtils.getSubject().hasRole("restbot")).isTrue();
assertThat(securitySupport.hasRole("restbot")).isTrue();
}

@Test
@WithUser(id = "R2D2", password = "beep")
public void r2d2_should_be_able_to_call_any_kind_of_method() {

assertThat(annotatedClass.create()).isTrue();
assertThat(annotatedClass.read()).isTrue();
assertThat(annotatedClass.update()).isTrue();
assertThat(annotatedClass.delete()).isTrue();

assertThat(annotatedMethods.create()).isTrue();
assertThat(annotatedMethods.read()).isTrue();
assertThat(annotatedMethods.update()).isTrue();
assertThat(annotatedMethods.delete()).isTrue();

}

// INTERPRETER
@Test
@WithUser(id = "C3P0", password = "ewokgod")
public void c3p0_should_only_be_able_to_read_rest_as_interpreter() {
// Read ewoks
assertThat(SecurityUtils.getSubject().isPermitted("ewok:read")).isTrue();
assertThat(securitySupport.isPermitted("ewok:read")).isTrue();

// Should not be able to update itself
assertThat(SecurityUtils.getSubject().isPermitted("c3p0:update")).isFalse();
assertThat(securitySupport.isPermitted("c3p0:update")).isFalse();

// Is an interpreter
assertThat(SecurityUtils.getSubject().hasRole("interpreter")).isTrue();
assertThat(securitySupport.hasRole("interpreter")).isTrue();
}

@Test
@WithUser(id = "C3P0", password = "ewokgod")
public void c3p0_should_be_able_to_read_data() {

assertThatThrownBy(() -> annotatedMethods.create()).isInstanceOf(AuthorizationException.class);
assertThatThrownBy(() -> annotatedMethods.update()).isInstanceOf(AuthorizationException.class);
assertThatThrownBy(() -> annotatedMethods.delete()).isInstanceOf(AuthorizationException.class);
assertThat(annotatedMethods.read()).isTrue();

assertThatThrownBy(() -> annotatedClass.create()).isInstanceOf(AuthorizationException.class);
assertThatThrownBy(() -> annotatedClass.update()).isInstanceOf(AuthorizationException.class);
assertThatThrownBy(() -> annotatedClass.delete()).isInstanceOf(AuthorizationException.class);
assertThat(annotatedClass.read()).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/
package org.seedstack.seed.security.internal;

import com.google.inject.AbstractModule;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
Expand All @@ -16,7 +15,6 @@
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.seedstack.seed.Install;
import org.seedstack.seed.it.SeedITRunner;
import org.seedstack.seed.security.AuthorizationException;
import org.seedstack.seed.security.SecuritySupport;
Expand Down Expand Up @@ -114,14 +112,4 @@ public void Anakin_should_not_be_able_to_teach() {
public void Anakin_should_have_customized_principal() {
Assertions.assertThat(Principals.getSimplePrincipalByName(securitySupport.getOtherPrincipals(), "foo").getValue()).isEqualTo("bar");
}

@Install
public static class securityTestModule extends AbstractModule {

@Override
protected void configure() {
bind(AnnotatedClass4Security.class);
}

}
}
Loading