Skip to content

Commit ba28876

Browse files
committed
[breaking] Transformed Permissions utility class into a PermissionFactory component, thus allowing to potentially override hasRole() implementation if needed
1 parent 0cfcc00 commit ba28876

File tree

14 files changed

+85
-62
lines changed

14 files changed

+85
-62
lines changed

restx-admin/src/main/java/restx/admin/AdminModule.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public String getName() {
7373
}
7474

7575
@Provides
76-
public RestxFilter adminRoleFilter() {
76+
public RestxFilter adminRoleFilter(final PermissionFactory permissionFactory) {
7777
return new RestxFilter() {
7878
final Pattern privatePath = Pattern.compile("^/@/(?!(ui|webjars)/).*$");
7979

@@ -87,7 +87,7 @@ public Optional<RestxHandlerMatch> match(RestxRequest req) {
8787
public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx) throws IOException {
8888
final RestxSession current = RestxSession.current();
8989
if (current.getPrincipal().isPresent() &&
90-
Permissions.hasRole(RESTX_ADMIN_ROLE).has(current.getPrincipal().get(), Collections.<String, String>emptyMap()).isPresent()) {
90+
permissionFactory.hasRole(RESTX_ADMIN_ROLE).has(current.getPrincipal().get(), Collections.<String, String>emptyMap()).isPresent()) {
9191
ctx.nextHandlerMatch().handle(req, resp, ctx);
9292
} else {
9393
throw new WebException(HttpStatus.UNAUTHORIZED);

restx-admin/src/main/java/restx/exceptions/ErrorDescriptorsRoute.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package restx.exceptions;
22

3-
import static restx.security.Permissions.hasRole;
4-
5-
63
import com.fasterxml.jackson.databind.ObjectWriter;
74
import com.google.common.base.Optional;
85
import com.google.common.collect.ImmutableCollection;
@@ -15,6 +12,7 @@
1512
import restx.factory.Component;
1613
import restx.jackson.FrontObjectMapperFactory;
1714
import restx.jackson.StdJsonProducerEntityRoute;
15+
import restx.security.PermissionFactory;
1816
import restx.security.RestxSecurityManager;
1917

2018
import javax.inject.Named;
@@ -31,12 +29,15 @@ public class ErrorDescriptorsRoute extends StdJsonProducerEntityRoute {
3129

3230
private final ImmutableMap<String, ErrorDescriptor> errorDescriptors;
3331
private final RestxSecurityManager securityManager;
32+
private PermissionFactory permissionFactory;
3433

3534
public ErrorDescriptorsRoute(Iterable<ErrorDescriptor> errorDescriptors,
3635
@Named(FrontObjectMapperFactory.WRITER_NAME) ObjectWriter objectWriter,
37-
RestxSecurityManager securityManager) {
36+
RestxSecurityManager securityManager,
37+
PermissionFactory permissionFactory) {
3838

3939
super("ErrorDescriptorsRoute", ImmutableCollection.class, objectWriter, new StdRestxRequestMatcher("GET", "/@/errors/descriptors"));
40+
this.permissionFactory = permissionFactory;
4041
Map<String, ErrorDescriptor> map = Maps.newLinkedHashMap();
4142
for (ErrorDescriptor errorDescriptor : errorDescriptors) {
4243
if (map.containsKey(errorDescriptor.getErrorCode())) {
@@ -50,7 +51,7 @@ public ErrorDescriptorsRoute(Iterable<ErrorDescriptor> errorDescriptors,
5051

5152
@Override
5253
protected Optional<?> doRoute(RestxRequest restxRequest, RestxRequestMatch match, Object i) throws IOException {
53-
securityManager.check(restxRequest, match, hasRole(AdminModule.RESTX_ADMIN_ROLE));
54+
securityManager.check(restxRequest, match, permissionFactory.hasRole(AdminModule.RESTX_ADMIN_ROLE));
5455
return Optional.of(errorDescriptors.values());
5556
}
5657
}

restx-apidocs/src/main/java/restx/apidocs/ApiDeclarationRoute.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import restx.factory.NamedComponent;
1414
import restx.jackson.FrontObjectMapperFactory;
1515
import restx.jackson.StdJsonProducerEntityRoute;
16+
import restx.security.PermissionFactory;
1617
import restx.security.RestxSecurityManager;
1718

1819
import javax.inject.Inject;
@@ -21,7 +22,6 @@
2122
import java.util.*;
2223

2324
import static restx.apidocs.ApiDocsIndexRoute.getRouterApiPath;
24-
import static restx.security.Permissions.hasRole;
2525

2626
/**
2727
* Serves the swagger api declaration of one router, which looks like that:
@@ -45,18 +45,20 @@
4545
public class ApiDeclarationRoute extends StdJsonProducerEntityRoute {
4646
private final Factory factory;
4747
private final RestxSecurityManager securityManager;
48+
private PermissionFactory permissionFactory;
4849

4950
@Inject
5051
public ApiDeclarationRoute(@Named(FrontObjectMapperFactory.WRITER_NAME) ObjectWriter writer,
51-
Factory factory, RestxSecurityManager securityManager) {
52+
Factory factory, RestxSecurityManager securityManager, PermissionFactory permissionFactory) {
5253
super("ApiDeclarationRoute", Map.class, writer, new StdRestxRequestMatcher("GET", "/@/api-docs/{router}"));
5354
this.factory = factory;
5455
this.securityManager = securityManager;
56+
this.permissionFactory = permissionFactory;
5557
}
5658

5759
@Override
5860
protected Optional<?> doRoute(RestxRequest restxRequest, RestxRequestMatch match, Object body) throws IOException {
59-
securityManager.check(restxRequest, match, hasRole(AdminModule.RESTX_ADMIN_ROLE));
61+
securityManager.check(restxRequest, match, permissionFactory.hasRole(AdminModule.RESTX_ADMIN_ROLE));
6062
String routerName = match.getPathParam("router");
6163
Optional<NamedComponent<RestxRouter>> router = getRouterByName(factory, routerName);
6264

restx-apidocs/src/main/java/restx/apidocs/ApiDocsIndexRoute.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package restx.apidocs;
22

3-
import static restx.security.Permissions.hasRole;
4-
5-
63
import com.fasterxml.jackson.databind.ObjectWriter;
74
import com.google.common.base.CaseFormat;
85
import com.google.common.base.Optional;
@@ -15,6 +12,7 @@
1512
import restx.factory.NamedComponent;
1613
import restx.jackson.FrontObjectMapperFactory;
1714
import restx.jackson.StdJsonProducerEntityRoute;
15+
import restx.security.PermissionFactory;
1816
import restx.security.RestxSecurityManager;
1917

2018
import javax.inject.Inject;
@@ -46,20 +44,23 @@
4644
public class ApiDocsIndexRoute extends StdJsonProducerEntityRoute {
4745
private final Factory factory;
4846
private final RestxSecurityManager securityManager;
47+
private PermissionFactory permissionFactory;
4948

5049
@Inject
5150
public ApiDocsIndexRoute(@Named(FrontObjectMapperFactory.WRITER_NAME) ObjectWriter writer,
5251
Factory factory,
53-
RestxSecurityManager securityManager) {
52+
RestxSecurityManager securityManager,
53+
PermissionFactory permissionFactory) {
5454

5555
super("ApiDocsIndexRoute", Map.class, writer, new StdRestxRequestMatcher("GET", "/@/api-docs"));
5656
this.factory = factory;
5757
this.securityManager = securityManager;
58+
this.permissionFactory = permissionFactory;
5859
}
5960

6061
@Override
6162
protected Optional<?> doRoute(RestxRequest restxRequest, RestxRequestMatch match, Object i) throws IOException {
62-
securityManager.check(restxRequest, match, hasRole(AdminModule.RESTX_ADMIN_ROLE));
63+
securityManager.check(restxRequest, match, permissionFactory.hasRole(AdminModule.RESTX_ADMIN_ROLE));
6364
return Optional.of(ImmutableMap.builder()
6465
.put("apiVersion", "0.1") // TODO
6566
.put("swaggerVersion", "1.1")

restx-core-annotation-processor/src/main/java/restx/annotations/processor/RestxAnnotationProcessor.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -112,32 +112,32 @@ private String buildPermission(ResourceMethodAnnotation annotation, TypeElement
112112
String permission;
113113
PermitAll permitAll = annotation.methodElem.getAnnotation(PermitAll.class);
114114
if (permitAll != null) {
115-
permission = "open()";
115+
permission = "pf.open()";
116116
} else {
117117
RolesAllowed rolesAllowed = annotation.methodElem.getAnnotation(RolesAllowed.class);
118118
if (rolesAllowed != null) {
119119
List<String> roles = new ArrayList<>();
120120
for (String role : rolesAllowed.value()) {
121121
for(String wildcardedRole : generateWildcardRolesFor(role)){
122-
roles.add("hasRole(\"" + wildcardedRole + "\")");
122+
roles.add("pf.hasRole(\"" + wildcardedRole + "\")");
123123
}
124124
}
125125
switch (roles.size()) {
126126
case 0:
127-
permission = "isAuthenticated()";
127+
permission = "pf.isAuthenticated()";
128128
break;
129129
case 1:
130130
permission = roles.get(0);
131131
break;
132132
default:
133-
permission = "anyOf(" + Joiner.on(", ").join(roles) + ")";
133+
permission = "pf.anyOf(" + Joiner.on(", ").join(roles) + ")";
134134
}
135135
} else {
136136
permitAll = typeElem.getAnnotation(PermitAll.class);
137137
if (permitAll != null) {
138-
permission = "open()";
138+
permission = "pf.open()";
139139
} else {
140-
permission = "isAuthenticated()";
140+
permission = "pf.isAuthenticated()";
141141
}
142142
}
143143
}

restx-core-annotation-processor/src/main/resources/restx/annotations/processor/RestxRouter.mustache

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import restx.entity.*;
1212
import restx.http.*;
1313
import restx.factory.*;
1414
import restx.security.*;
15-
import static restx.security.Permissions.*;
15+
import restx.security.PermissionFactory;
1616
import restx.description.*;
1717
import restx.converters.MainStringConverter;
1818
import static restx.common.MorePreconditions.checkPresent;
@@ -32,6 +32,7 @@ public class {{router}} extends RestxRouter {
3232
final EntityRequestBodyReaderRegistry readerRegistry,
3333
final EntityResponseWriterRegistry writerRegistry,
3434
final MainStringConverter converter,
35+
final PermissionFactory pf,
3536
final Optional<Validator> validator,
3637
final RestxSecurityManager securityManager) {
3738
super(

restx-core/src/main/java/restx/security/Permissions.java restx-core/src/main/java/restx/security/PermissionFactory.java

+16-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package restx.security;
22

33
import com.google.common.base.Optional;
4+
import restx.factory.Component;
45

56
import java.util.Arrays;
67
import java.util.Map;
@@ -11,7 +12,8 @@
1112
* Provides a set of useful permissions, including the OPEN permission which is the only one that can allow access
1213
* to a resource without being authenticated.
1314
*/
14-
public class Permissions {
15+
@Component
16+
public class PermissionFactory {
1517
private static final Pattern ROLE_PARAM_INTERPOLATOR_REGEX = Pattern.compile("\\{(.+?)\\}");
1618

1719
private static final Permission OPEN = new Permission() {
@@ -40,23 +42,30 @@ public String toString() {
4042
/**
4143
* This is the only permission that can allow access to a resource without being authenticated.
4244
*/
43-
public static Permission open() {
45+
public Permission open() {
4446
return OPEN;
4547
}
4648

4749
/**
4850
* This is the most basic permission which is true as soon as a principal is authenticated.
4951
*/
50-
public static Permission isAuthenticated() {
52+
public Permission isAuthenticated() {
5153
return IS_AUTHENTICATED;
5254
}
5355

56+
public boolean isOpen(Permission permission) {
57+
return permission == open();
58+
}
59+
60+
public boolean isIsAuthenticated(Permission permission) {
61+
return permission == isAuthenticated();
62+
}
5463

5564
/**
5665
* This permission is true as soon as the principal has the given role
5766
* @param role the role to check
5867
*/
59-
public static Permission hasRole(final String role) {
68+
public Permission hasRole(final String role) {
6069
return new Permission() {
6170
public final String TO_STRING = "HAS_ROLE[" + role + "]";
6271

@@ -81,7 +90,7 @@ public String toString() {
8190
};
8291
}
8392

84-
protected static String interpolateRole(String role, Map<String, String> roleInterpolationMap) {
93+
protected String interpolateRole(String role, Map<String, String> roleInterpolationMap) {
8594
Matcher matcher = ROLE_PARAM_INTERPOLATOR_REGEX.matcher(role);
8695
StringBuffer interpolatedRole = new StringBuffer();
8796
while(matcher.find()){
@@ -99,7 +108,7 @@ protected static String interpolateRole(String role, Map<String, String> roleInt
99108
/**
100109
* A compound permission which is true if any of the underlying permissions is true
101110
*/
102-
public static Permission anyOf(final Permission... permissions) {
111+
public Permission anyOf(final Permission... permissions) {
103112
return new Permission() {
104113
@Override
105114
public Optional<? extends Permission> has(RestxPrincipal principal, Map<String, String> roleInterpolationMap) {
@@ -123,7 +132,7 @@ public String toString() {
123132
/**
124133
* A compound permission which is true if all underlying permissions are true
125134
*/
126-
public static Permission allOf(final Permission... permissions) {
135+
public Permission allOf(final Permission... permissions) {
127136
return new Permission() {
128137
@Override
129138
public Optional<? extends Permission> has(RestxPrincipal principal, Map<String, String> roleInterpolationMap) {

restx-core/src/main/java/restx/security/RestxSessionCookieFilter.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,22 @@ public class RestxSessionCookieFilter implements RestxRouteFilter, RestxHandler
5151
private final RestxSession.Definition sessionDefinition;
5252
private final ObjectMapper mapper;
5353
private final Signer signer;
54-
private final RestxSessionCookieDescriptor restxSessionCookieDescriptor;
54+
private final PermissionFactory permissionFactory;
55+
private final RestxSessionCookieDescriptor restxSessionCookieDescriptor;
5556
private final RestxSession emptySession;
5657

5758
public RestxSessionCookieFilter(
5859
RestxSession.Definition sessionDefinition,
5960
@Named(FrontObjectMapperFactory.MAPPER_NAME) ObjectMapper mapper,
6061
@Named(COOKIE_SIGNER_NAME) Signer signer,
62+
PermissionFactory permissionFactory,
6163
RestxSessionCookieDescriptor restxSessionCookieDescriptor) {
6264

6365
this.sessionDefinition = sessionDefinition;
6466
this.mapper = mapper;
6567
this.signer = signer;
66-
this.restxSessionCookieDescriptor = restxSessionCookieDescriptor;
68+
this.permissionFactory = permissionFactory;
69+
this.restxSessionCookieDescriptor = restxSessionCookieDescriptor;
6770
this.emptySession = new RestxSession(sessionDefinition, ImmutableMap.<String, String>of(),
6871
Optional.<RestxPrincipal>absent(), Duration.ZERO);
6972
}
@@ -124,7 +127,7 @@ public RestxSession buildContextFromRequest(RestxRequest req) throws IOException
124127
Optional<RestxPrincipal> principalOptional = RestxSession.getValue(
125128
sessionDefinition, RestxPrincipal.class, RestxPrincipal.SESSION_DEF_KEY, principalName);
126129
if (principalOptional.isPresent()
127-
&& Permissions.hasRole("restx-admin").has(principalOptional.get(), Collections.<String,String>emptyMap()).isPresent()) {
130+
&& permissionFactory.hasRole("restx-admin").has(principalOptional.get(), Collections.<String,String>emptyMap()).isPresent()) {
128131
Optional<String> su = req.getHeader("RestxSu");
129132
if (su.isPresent() && !Strings.isNullOrEmpty(su.get())) {
130133
try {

restx-core/src/main/java/restx/security/StdRestxSecurityManager.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@
2424
public class StdRestxSecurityManager implements RestxSecurityManager {
2525
private static final Logger logger = LoggerFactory.getLogger(StdRestxSecurityManager.class);
2626

27+
protected final PermissionFactory permissionFactory;
28+
29+
public StdRestxSecurityManager(PermissionFactory permissionFactory) {
30+
this.permissionFactory = permissionFactory;
31+
}
32+
2733
@Override
2834
public void check(RestxRequest request, RestxRequestMatch requestMatch, Permission permission) {
29-
if (permission == Permissions.open()) {
35+
if (permissionFactory.isOpen(permission)) {
3036
return;
3137
}
3238

restx-factory-admin/src/main/java/restx/factory/FactoryDumpRoute.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package restx.factory;
22

3-
import static restx.security.Permissions.hasRole;
4-
5-
63
import restx.*;
74
import restx.admin.AdminModule;
5+
import restx.security.PermissionFactory;
86
import restx.security.RestxSecurityManager;
97

108
import javax.inject.Inject;
@@ -14,17 +12,19 @@
1412
public class FactoryDumpRoute extends StdRoute {
1513
private final Factory factory;
1614
private final RestxSecurityManager securityManager;
15+
private PermissionFactory permissionFactory;
1716

1817
@Inject
19-
public FactoryDumpRoute(Factory factory, RestxSecurityManager securityManager) {
18+
public FactoryDumpRoute(Factory factory, RestxSecurityManager securityManager, PermissionFactory permissionFactory) {
2019
super("FactoryRoute", new StdRestxRequestMatcher("GET", "/@/factory"));
2120
this.factory = factory;
2221
this.securityManager = securityManager;
22+
this.permissionFactory = permissionFactory;
2323
}
2424

2525
@Override
2626
public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx) throws IOException {
27-
securityManager.check(req, match, hasRole(AdminModule.RESTX_ADMIN_ROLE));
27+
securityManager.check(req, match, permissionFactory.hasRole(AdminModule.RESTX_ADMIN_ROLE));
2828
resp.setContentType("text/plain");
2929
resp.getWriter().println(factory.dump());
3030
}

0 commit comments

Comments
 (0)