Skip to content

Commit 8d84db7

Browse files
committed
Fix multi test annotations, realm direct permissions
1 parent 033594f commit 8d84db7

File tree

19 files changed

+262
-71
lines changed

19 files changed

+262
-71
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Version 3.11.0 (2020-08-06)
22

33
* [brk] Config functions that find available ports now take a port name as argument so they can return the same port for each evaluation (example: `$availableTcpPort()` becomes `$availableTcpPort('web')`).
4+
* [new] Support for configuration of Undertow handlers using `undertow-handlers.conf` file (at the root of the classpath by default).
5+
* [new] Security realms can now return direct user permissions, not attached to any role.
6+
* [chg] Enable a detailed user message by default for internal and security exceptions during REST requests.
7+
* [fix] Fixed test annotations that were not fully detected when repeated: `@ConfigurationProperty`, `@KernelParameter` and `@SystemProperty`.
48

59
# Version 3.10.0 (2020-08-06)
610

rest/core/src/main/java/org/seedstack/seed/rest/internal/exceptionmapper/AuthenticationExceptionMapper.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,41 @@
77
*/
88
package org.seedstack.seed.rest.internal.exceptionmapper;
99

10-
import javax.ws.rs.core.Response;
11-
import javax.ws.rs.ext.ExceptionMapper;
12-
import javax.ws.rs.ext.Provider;
10+
import org.seedstack.seed.Application;
11+
import org.seedstack.seed.rest.RestConfig;
1312
import org.seedstack.seed.security.AuthenticationException;
1413
import org.slf4j.Logger;
1514
import org.slf4j.LoggerFactory;
1615

16+
import javax.inject.Inject;
17+
import javax.ws.rs.core.Response;
18+
import javax.ws.rs.ext.ExceptionMapper;
19+
import javax.ws.rs.ext.Provider;
20+
1721
/**
1822
* Default {@link AuthenticationException} exception mapper which returns an HTTP status 401 (unauthorized).
1923
*/
2024
@Provider
2125
public class AuthenticationExceptionMapper implements ExceptionMapper<AuthenticationException> {
2226
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationExceptionMapper.class);
27+
private final RestConfig.ExceptionMappingConfig exceptionMappingConfig;
28+
29+
@Inject
30+
public AuthenticationExceptionMapper(Application application) {
31+
this.exceptionMappingConfig = application.getConfiguration().get(RestConfig.ExceptionMappingConfig.class);
32+
}
2333

2434
@Override
2535
public Response toResponse(AuthenticationException exception) {
26-
LOGGER.debug(exception.toString(), exception);
27-
return Response.status(Response.Status.UNAUTHORIZED).entity("Unauthorized").build();
36+
if (exceptionMappingConfig.isDetailedLog()) {
37+
LOGGER.debug(exception.toString());
38+
} else {
39+
LOGGER.debug(exception.getMessage());
40+
}
41+
String message = "Unauthorized";
42+
if (exceptionMappingConfig.isDetailedUserMessage()) {
43+
message += ": " + exception.getMessage();
44+
}
45+
return Response.status(Response.Status.UNAUTHORIZED).entity(message).build();
2846
}
2947
}

rest/core/src/main/java/org/seedstack/seed/rest/internal/exceptionmapper/AuthorizationExceptionMapper.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,41 @@
77
*/
88
package org.seedstack.seed.rest.internal.exceptionmapper;
99

10-
import javax.ws.rs.core.Response;
11-
import javax.ws.rs.ext.ExceptionMapper;
12-
import javax.ws.rs.ext.Provider;
10+
import org.seedstack.seed.Application;
11+
import org.seedstack.seed.rest.RestConfig;
1312
import org.seedstack.seed.security.AuthorizationException;
1413
import org.slf4j.Logger;
1514
import org.slf4j.LoggerFactory;
1615

16+
import javax.inject.Inject;
17+
import javax.ws.rs.core.Response;
18+
import javax.ws.rs.ext.ExceptionMapper;
19+
import javax.ws.rs.ext.Provider;
20+
1721
/**
1822
* Default {@link AuthorizationException} exception mapper which returns an HTTP status 403 (forbidden).
1923
*/
2024
@Provider
2125
public class AuthorizationExceptionMapper implements ExceptionMapper<AuthorizationException> {
2226
private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationExceptionMapper.class);
27+
private final RestConfig.ExceptionMappingConfig exceptionMappingConfig;
28+
29+
@Inject
30+
public AuthorizationExceptionMapper(Application application) {
31+
this.exceptionMappingConfig = application.getConfiguration().get(RestConfig.ExceptionMappingConfig.class);
32+
}
2333

2434
@Override
2535
public Response toResponse(AuthorizationException exception) {
26-
LOGGER.debug(exception.toString(), exception);
27-
return Response.status(Response.Status.FORBIDDEN).entity("Forbidden").build();
36+
if (exceptionMappingConfig.isDetailedLog()) {
37+
LOGGER.debug(exception.toString());
38+
} else {
39+
LOGGER.debug(exception.getMessage());
40+
}
41+
String message = "Forbidden";
42+
if (exceptionMappingConfig.isDetailedUserMessage()) {
43+
message += ": " + exception.getMessage();
44+
}
45+
return Response.status(Response.Status.FORBIDDEN).entity(message).build();
2846
}
2947
}

rest/core/src/main/java/org/seedstack/seed/rest/internal/exceptionmapper/InternalErrorExceptionMapper.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,21 @@
77
*/
88
package org.seedstack.seed.rest.internal.exceptionmapper;
99

10-
import java.util.UUID;
11-
import javax.inject.Inject;
12-
import javax.servlet.http.HttpServletRequest;
13-
import javax.ws.rs.core.Context;
14-
import javax.ws.rs.core.Response;
15-
import javax.ws.rs.ext.ExceptionMapper;
16-
import javax.ws.rs.ext.Provider;
1710
import org.seedstack.seed.Application;
1811
import org.seedstack.seed.core.Seed;
1912
import org.seedstack.seed.rest.RestConfig;
2013
import org.seedstack.shed.exception.BaseException;
2114
import org.slf4j.Logger;
2215
import org.slf4j.LoggerFactory;
2316

17+
import javax.inject.Inject;
18+
import javax.servlet.http.HttpServletRequest;
19+
import javax.ws.rs.core.Context;
20+
import javax.ws.rs.core.Response;
21+
import javax.ws.rs.ext.ExceptionMapper;
22+
import javax.ws.rs.ext.Provider;
23+
import java.util.UUID;
24+
2425
/**
2526
* Default exception mapper for an caught exception with no exception mapper associated.
2627
* It returns an HTTP status 500 (internal server error).

rest/specs/src/main/java/org/seedstack/seed/rest/RestConfig.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
*/
88
package org.seedstack.seed.rest;
99

10+
import org.seedstack.coffig.Config;
11+
import org.seedstack.coffig.SingleValue;
12+
1013
import java.util.Collections;
1114
import java.util.HashMap;
1215
import java.util.HashSet;
1316
import java.util.Map;
1417
import java.util.Set;
15-
import org.seedstack.coffig.Config;
16-
import org.seedstack.coffig.SingleValue;
1718

1819
@Config("rest")
1920
public class RestConfig {
@@ -117,7 +118,7 @@ public static class ExceptionMappingConfig {
117118
private boolean all = true;
118119
private boolean validation = true;
119120
private boolean detailedLog = true;
120-
private boolean detailedUserMessage = false;
121+
private boolean detailedUserMessage = true;
121122

122123
public boolean isSecurity() {
123124
return security;

security/core/src/main/java/org/seedstack/seed/security/internal/RealmConfiguration.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,9 @@
1212
import org.seedstack.seed.security.RolePermissionResolver;
1313

1414
class RealmConfiguration {
15-
1615
private final String name;
17-
1816
private final Class<? extends Realm> realmClass;
19-
2017
private Class<? extends RoleMapping> roleMappingClass;
21-
2218
private Class<? extends RolePermissionResolver> rolePermissionResolverClass;
2319

2420
RealmConfiguration(String name, Class<? extends Realm> realmClass) {
@@ -50,4 +46,8 @@ Class<? extends Realm> getRealmClass() {
5046
return realmClass;
5147
}
5248

49+
@Override
50+
public String toString() {
51+
return name;
52+
}
5353
}

security/core/src/main/java/org/seedstack/seed/security/internal/ShiroRealmAdapter.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@
88

99
package org.seedstack.seed.security.internal;
1010

11-
import java.util.ArrayList;
12-
import java.util.Collection;
13-
import java.util.List;
14-
import java.util.Set;
15-
import javax.inject.Inject;
1611
import org.apache.shiro.authc.AuthenticationException;
1712
import org.apache.shiro.authc.AuthenticationInfo;
1813
import org.apache.shiro.authc.AuthenticationToken;
@@ -32,6 +27,12 @@
3227
import org.seedstack.seed.security.internal.realms.AuthenticationTokenWrapper;
3328
import org.seedstack.seed.security.principals.PrincipalProvider;
3429

30+
import javax.inject.Inject;
31+
import java.util.ArrayList;
32+
import java.util.Collection;
33+
import java.util.List;
34+
import java.util.Set;
35+
3536
class ShiroRealmAdapter extends AuthorizingRealm {
3637
private Realm realm;
3738
@Inject
@@ -54,12 +55,21 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal
5455
principalProviders.add((PrincipalProvider<?>) principal);
5556
}
5657
}
57-
List<PrincipalProvider<?>> asList = principals.asList();
58-
for (Role role : realm.getRoleMapping().resolveRoles(realm.getRealmRoles(idPrincipal, asList),
58+
59+
List<PrincipalProvider<?>> principalList = principals.asList();
60+
61+
// Retrieve user roles through
62+
for (Role role : realm.getRoleMapping().resolveRoles(realm.getRealmRoles(idPrincipal, principalList),
5963
principalProviders)) {
6064
role.getPermissions().addAll(realm.getRolePermissionResolver().resolvePermissionsInRole(role));
6165
authzInfo.addRole(role);
6266
}
67+
68+
// Retrieve direct user permissions
69+
for (String permission : realm.getRealmPermissions(idPrincipal, principalList)) {
70+
authzInfo.addPermission(permission);
71+
}
72+
6373
return authzInfo;
6474
}
6575

security/core/src/main/java/org/seedstack/seed/security/internal/authorization/SeedAuthorizationInfo.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
*/
88
package org.seedstack.seed.security.internal.authorization;
99

10-
import java.util.Collection;
11-
import java.util.Collections;
12-
import java.util.HashSet;
13-
import java.util.Set;
1410
import org.apache.shiro.authz.AuthorizationInfo;
1511
import org.apache.shiro.authz.Permission;
1612
import org.seedstack.seed.security.Role;
1713
import org.seedstack.seed.security.Scope;
1814

15+
import java.util.Collection;
16+
import java.util.Collections;
17+
import java.util.HashSet;
18+
import java.util.Set;
19+
1920
/**
2021
* AuthorizationInfo that keeps the Roles and Permissions for SeedStack API.
2122
*/
@@ -67,4 +68,13 @@ public void addRole(Role role) {
6768
}
6869
}
6970
}
71+
72+
/**
73+
* Adds a direct permission to the authorization info.
74+
*
75+
* @param permission the permission to add.
76+
*/
77+
public void addPermission(String permission) {
78+
stringPermissions.add(permission);
79+
}
7080
}

security/specs/src/main/java/org/seedstack/seed/security/Realm.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
package org.seedstack.seed.security;
1010

11+
import org.seedstack.seed.security.principals.PrincipalProvider;
12+
1113
import java.util.Collection;
14+
import java.util.HashSet;
1215
import java.util.Set;
13-
import org.seedstack.seed.security.principals.PrincipalProvider;
1416

1517
/**
1618
* A realm is used to authenticate and retrieve authorization for a user.
@@ -23,6 +25,18 @@ default String name() {
2325
return getClass().getSimpleName();
2426
}
2527

28+
/**
29+
* Get the permissions
30+
*
31+
* @param identityPrincipal principal representing the identity of the user
32+
* @param otherPrincipals other principals
33+
* @return the permissions of the user. Should not return null but empty
34+
* collection.
35+
*/
36+
default Set<String> getRealmPermissions(PrincipalProvider<?> identityPrincipal, Collection<PrincipalProvider<?>> otherPrincipals) {
37+
return new HashSet<>();
38+
}
39+
2640
/**
2741
* Get the roles
2842
*
@@ -31,7 +45,9 @@ default String name() {
3145
* @return the roles of the user. Should not return null but empty
3246
* collection.
3347
*/
34-
Set<String> getRealmRoles(PrincipalProvider<?> identityPrincipal, Collection<PrincipalProvider<?>> otherPrincipals);
48+
default Set<String> getRealmRoles(PrincipalProvider<?> identityPrincipal, Collection<PrincipalProvider<?>> otherPrincipals) {
49+
return new HashSet<>();
50+
}
3551

3652
/**
3753
* Authenticates the user and retrieves its properties in an

testing/core/src/main/java/org/seedstack/seed/testing/internal/ConfigurationPropertiesTestPlugin.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
*/
88
package org.seedstack.seed.testing.internal;
99

10-
import java.util.HashMap;
11-
import java.util.Map;
10+
import org.seedstack.seed.testing.ConfigurationProperties;
1211
import org.seedstack.seed.testing.ConfigurationProperty;
1312
import org.seedstack.seed.testing.spi.TestContext;
1413
import org.seedstack.seed.testing.spi.TestPlugin;
1514
import org.seedstack.shed.reflect.Annotations;
1615

16+
import java.util.HashMap;
17+
import java.util.Map;
18+
1719
public class ConfigurationPropertiesTestPlugin implements TestPlugin {
1820
@Override
1921
public boolean enabled(TestContext testContext) {
@@ -25,13 +27,29 @@ public Map<String, String> configurationProperties(TestContext testContext) {
2527
Map<String, String> configuration = new HashMap<>();
2628

2729
// Configuration properties on the class
30+
Annotations.on(testContext.testClass())
31+
.includingMetaAnnotations()
32+
.findAll(ConfigurationProperties.class)
33+
.forEach(configProperties -> {
34+
for (ConfigurationProperty configProperty : configProperties.value()) {
35+
configuration.put(buildPropertyName(configProperty), configProperty.value());
36+
}
37+
});
2838
Annotations.on(testContext.testClass())
2939
.includingMetaAnnotations()
3040
.findAll(ConfigurationProperty.class)
3141
.forEach(configProperty -> configuration.put(buildPropertyName(configProperty),
3242
configProperty.value()));
3343

3444
// Configuration properties on the method (completing/overriding class configuration properties)
45+
testContext.testMethod().ifPresent(testMethod -> Annotations.on(testMethod)
46+
.includingMetaAnnotations()
47+
.findAll(ConfigurationProperties.class)
48+
.forEach(configProperties -> {
49+
for (ConfigurationProperty configProperty : configProperties.value()) {
50+
configuration.put(buildPropertyName(configProperty), configProperty.value());
51+
}
52+
}));
3553
testContext.testMethod().ifPresent(testMethod -> Annotations.on(testMethod)
3654
.includingMetaAnnotations()
3755
.findAll(ConfigurationProperty.class)

0 commit comments

Comments
 (0)