This is an automated email from the ASF dual-hosted git repository.
jlmonteiro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee.git
The following commit(s) were added to refs/heads/master by this push:
new 23df129 Finish tomee-security to implement fully Jakarta Security
API. Some other fixes included. Test cases, etc
new 40fd970 Merge pull request #786 from jeanouii/tomee-security_end
23df129 is described below
commit 23df129e2cf6fa52c6827ad66a13eec572b7073c
Author: Jean-Louis Monteiro <[email protected]>
AuthorDate: Tue Apr 20 11:24:04 2021 +0200
Finish tomee-security to implement fully Jakarta Security API. Some other
fixes included. Test cases, etc
---
.../assembler/classic/JaccPermissionsBuilder.java | 13 +-
.../openejb/assembler/classic/PolicyContext.java | 9 +-
.../core/security/AbstractSecurityService.java | 83 ++++-
.../apache/openejb/core/security/JaccProvider.java | 2 +
.../core/security/jacc/BasicJaccProvider.java | 15 +-
.../openejb/ri/sp/PseudoSecurityService.java | 11 +
.../org/apache/openejb/spi/SecurityService.java | 5 +
.../authenticator/jaspic/CallbackHandlerImpl.java | 14 +-
.../java/org/apache/tomee/catalina/Contexts.java | 10 +
.../tomee/catalina/TomcatSecurityService.java | 50 ++-
.../apache/tomee/catalina/TomcatWebAppBuilder.java | 17 +
.../tomee/catalina/security/HTTPMethods.java | 163 ++++++++++
...rityConstaintsToJaccPermissionsTransformer.java | 352 +++++++++++++++++++++
.../apache/tomee/catalina/security/URLPattern.java | 289 +++++++++++++++++
.../tomee/catalina/security/URLPatternCheck.java | 28 ++
.../tomee/catalina/security/UncheckedItem.java | 64 ++++
.../tomee/security/TomEESecurityContext.java | 22 +-
.../tomee/security/cdi/TomEESecurityExtension.java | 8 +-
.../identitystore/TomEEIdentityStoreHandler.java | 17 +
.../provider/TomEESecurityServerAuthConfig.java | 2 +-
.../TomEESecurityServletContainerInitializer.java | 39 ++-
.../servlet/JaccPermissionServletTest.java | 141 +++++++++
.../src/test/resources/conf/tomcat-users.xml | 4 +
23 files changed, 1326 insertions(+), 32 deletions(-)
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JaccPermissionsBuilder.java
b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JaccPermissionsBuilder.java
index f52b6de..14a20d0 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JaccPermissionsBuilder.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/JaccPermissionsBuilder.java
@@ -33,6 +33,7 @@ import javax.security.jacc.PolicyContextException;
import java.lang.reflect.Method;
import java.security.Permission;
import java.security.PermissionCollection;
+import java.security.Permissions;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
@@ -55,10 +56,12 @@ public class JaccPermissionsBuilder {
return;
}
+ final String contextID = policyContext.getContextID();
try {
final PolicyConfigurationFactory factory =
PolicyConfigurationFactory.getPolicyConfigurationFactory();
- final PolicyConfiguration policy =
factory.getPolicyConfiguration(policyContext.getContextID(), false);
+ // final boolean needsCommit = factory.inService(contextID);
+ final PolicyConfiguration policy =
factory.getPolicyConfiguration(contextID, false);
policy.addToExcludedPolicy(policyContext.getExcludedPermissions());
@@ -68,11 +71,15 @@ public class JaccPermissionsBuilder {
policy.addToRole(entry.getKey(), entry.getValue());
}
- policy.commit();
+ // not sure if this is required or not
+ // if (needsCommit) {
+ policy.commit();
+ // }
} catch (final ClassNotFoundException e) {
throw new OpenEJBException("PolicyConfigurationFactory class not
found", e);
+
} catch (final PolicyContextException e) {
- throw new OpenEJBException("JACC PolicyConfiguration failed:
ContextId=" + policyContext.getContextID(), e);
+ throw new OpenEJBException("JACC PolicyConfiguration failed:
ContextId=" + contextID, e);
}
}
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/PolicyContext.java
b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/PolicyContext.java
index a6407ce..3b0dd2d 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/PolicyContext.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/PolicyContext.java
@@ -17,13 +17,11 @@
package org.apache.openejb.assembler.classic;
+import java.security.Permission;
import java.security.PermissionCollection;
import java.util.HashMap;
import java.util.Map;
-/**
- * @version $Rev$ $Date$
- */
public class PolicyContext {
private final PermissionCollection excludedPermissions = new
DelegatePermissionCollection();
@@ -47,6 +45,11 @@ public class PolicyContext {
return rolePermissions;
}
+ public void addRole(final String name, final Permission permission) {
+ rolePermissions.computeIfAbsent(name, (k) -> new
DelegatePermissionCollection());
+ rolePermissions.get(name).add(permission);
+ }
+
public String getContextID() {
return contextId;
}
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
index e86a2b0..399096b 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
@@ -29,20 +29,27 @@ import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.spi.CallerPrincipal;
import org.apache.openejb.spi.SecurityService;
import org.apache.openejb.util.JavaSecurityManagers;
+import org.apache.openejb.util.LogCategory;
+import org.apache.openejb.util.Logger;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
import javax.security.jacc.EJBMethodPermission;
import javax.security.jacc.PolicyConfigurationFactory;
+import javax.security.jacc.PolicyContext;
+import javax.security.jacc.PolicyContextException;
+import javax.security.jacc.PolicyContextHandler;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.security.AccessControlException;
import java.security.AccessController;
+import java.security.CodeSource;
import java.security.Policy;
import java.security.Principal;
import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -54,12 +61,22 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import static java.util.Arrays.asList;
+
/**
* This security service chooses a UUID as its token as this can be serialized
* to clients, is mostly secure, and can be deserialized in a client vm without
* addition openejb-core classes.
*/
-public abstract class AbstractSecurityService implements DestroyableResource,
SecurityService<UUID>, ThreadContextListener,
BasicPolicyConfiguration.RoleResolver {
+public abstract class AbstractSecurityService implements DestroyableResource,
SecurityService<UUID>, ThreadContextListener,
+
BasicPolicyConfiguration.RoleResolver, PolicyContextHandler {
+
+ private static final Logger LOGGER =
Logger.getInstance(LogCategory.OPENEJB_SECURITY,
"org.apache.openejb.util.resources");
+
+ protected static final String KEY_SUBJECT =
"javax.security.auth.Subject.container";
+ protected static final String KEY_REQUEST =
"javax.servlet.http.HttpServletRequest";
+ protected static final Set<String> KEYS = new
HashSet<>(asList(KEY_REQUEST, KEY_SUBJECT));
+
private static final Map<Object, Identity> identities = new
ConcurrentHashMap<Object, Identity>();
protected static final ThreadLocal<Identity> clientIdentity = new
ThreadLocal<Identity>();
protected String defaultUser = "guest";
@@ -81,7 +98,19 @@ public abstract class AbstractSecurityService implements
DestroyableResource, Se
// set the default subject and the default context
updateSecurityContext();
+ // we can now add the role resolver for Jacc to convert into strings
SystemInstance.get().setComponent(BasicPolicyConfiguration.RoleResolver.class,
this);
+
+ // and finally we can register ourself as a PolicyContextHandler
+ // we can register policy handlers and the role mapper
+ try {
+ for (String key : getKeys()) {
+ PolicyContext.registerHandler(key, this, false);
+ }
+ } catch (final PolicyContextException e) {
+ // best would probably to fail start if something wrong happens
+ LOGGER.warning("Can't register PolicyContextHandler", e);
+ }
}
@Override
@@ -278,6 +307,40 @@ public abstract class AbstractSecurityService implements
DestroyableResource, Se
return false;
}
+ protected Subject getSubject() {
+ final ThreadContext threadContext = ThreadContext.getThreadContext();
+ if (threadContext == null) {
+ final Identity id = clientIdentity.get();
+ if (id != null) {
+ return id.getSubject();
+ }
+ return new Subject();
+ }
+
+ final SecurityContext securityContext =
threadContext.get(SecurityContext.class);
+ if (securityContext == null) { // unlikely
+ return new Subject();
+ }
+ return securityContext.subject;
+ }
+
+ @Override
+ public <P extends Principal> Set<P> getPrincipalsByType(final Class<P>
pType) {
+ if (pType == null) {
+ throw new IllegalArgumentException("Principal type can't be null");
+ }
+ return getSubject().getPrincipals(pType);
+ }
+
+ @Override
+ public ProtectionDomain getProtectionDomain() {
+ return new ProtectionDomain(
+ new CodeSource(null, (java.security.cert.Certificate[]) null),
+ null, null,
+ getSubject().getPrincipals().toArray(new Principal[0])
+ );
+ }
+
@Override
public Principal getCallerPrincipal() {
final ThreadContext threadContext = ThreadContext.getThreadContext();
@@ -416,6 +479,24 @@ public abstract class AbstractSecurityService implements
DestroyableResource, Se
return defaultContext;
}
+ @Override
+ public boolean supports(final String key) throws PolicyContextException {
+ return KEY_SUBJECT.equals(key);
+ }
+
+ @Override
+ public String[] getKeys() throws PolicyContextException {
+ return new String[] {KEY_SUBJECT};
+ }
+
+ @Override
+ public Object getContext(final String key, final Object data) throws
PolicyContextException {
+ if (KEY_SUBJECT.equals(key)) {
+ return getSubject();
+ }
+ throw new PolicyContextException("Handler does not support key: " +
key);
+ }
+
public static final class ProvidedSecurityContext {
public final SecurityContext context;
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/JaccProvider.java
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/JaccProvider.java
index 2eca8b4..50a13fe 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/JaccProvider.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/JaccProvider.java
@@ -136,4 +136,6 @@ public abstract class JaccProvider {
public abstract void refresh();
public abstract boolean implies(ProtectionDomain domain, Permission
permission);
+
+ public abstract boolean hasAccessToWebResource(final String resource,
final String... methods);
}
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
index 3c733ae..9b53637 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
@@ -18,6 +18,8 @@
package org.apache.openejb.core.security.jacc;
import org.apache.openejb.core.security.JaccProvider;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.spi.SecurityService;
import javax.security.jacc.EJBMethodPermission;
import javax.security.jacc.EJBRoleRefPermission;
@@ -40,6 +42,7 @@ import java.util.Set;
* @version $Rev$ $Date$
*/
public class BasicJaccProvider extends JaccProvider {
+
private static final Set<Class> JACC_PERMISSIONS = new HashSet<Class>() {
{
add(EJBMethodPermission.class);
@@ -112,6 +115,16 @@ public class BasicJaccProvider extends JaccProvider {
}
}
- return systemPolicy != null ? systemPolicy.implies(domain, permission)
: false;
+ return systemPolicy != null && systemPolicy.implies(domain,
permission);
+ }
+
+ public boolean hasAccessToWebResource(final String resource, final
String... methods) {
+ final SecurityService securityService =
SystemInstance.get().getComponent(SecurityService.class);
+ if (securityService != null) {
+ return implies(securityService.getProtectionDomain(), new
WebResourcePermission(resource, methods));
+ }
+ return false;
+
}
+
}
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/ri/sp/PseudoSecurityService.java
b/container/openejb-core/src/main/java/org/apache/openejb/ri/sp/PseudoSecurityService.java
index a83b8dd..205f139 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/ri/sp/PseudoSecurityService.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/ri/sp/PseudoSecurityService.java
@@ -24,6 +24,7 @@ import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.security.Principal;
+import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.Properties;
import java.util.Set;
@@ -85,4 +86,14 @@ public class PseudoSecurityService implements
SecurityService {
public void onLogout(final HttpServletRequest request) {
// no-op
}
+
+ @Override
+ public Set getPrincipalsByType(final Class pType) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public ProtectionDomain getProtectionDomain() {
+ return null;
+ }
}
\ No newline at end of file
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/spi/SecurityService.java
b/container/openejb-core/src/main/java/org/apache/openejb/spi/SecurityService.java
index e1ed185..1b85d6a 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/spi/SecurityService.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/spi/SecurityService.java
@@ -23,6 +23,8 @@ import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.security.Principal;
+import java.security.ProtectionDomain;
+import java.util.Set;
/**
* The generic value T is any serializable token of the SecurityService
@@ -69,6 +71,9 @@ public interface SecurityService<T> extends Service {
*/
Principal getCallerPrincipal();
+ <P extends Principal> Set<P> getPrincipalsByType(final Class<P> pType);
+ ProtectionDomain getProtectionDomain();
+
/**
* Active
*/
diff --git
a/tomee/apache-tomee/src/patch/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java
b/tomee/apache-tomee/src/patch/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java
index 45d6bf7..d785d4e 100644
---
a/tomee/apache-tomee/src/patch/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java
+++
b/tomee/apache-tomee/src/patch/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java
@@ -52,8 +52,7 @@ import java.util.List;
*/
public class CallbackHandlerImpl implements CallbackHandler, Contained {
- private static final StringManager sm = StringManager.getManager(
-
org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl.class);
+ private static final StringManager sm =
StringManager.getManager(org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl.class);
private final Log log =
LogFactory.getLog(org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl.class);
// must not be static
private Container container;
@@ -85,16 +84,16 @@ public class CallbackHandlerImpl implements
CallbackHandler, Contained {
log.warn(sm.getString("callbackHandlerImpl.containerMissing",
callback.getClass().getName()));
} else if (container.getRealm() == null) {
log.warn(sm.getString("callbackHandlerImpl.realmMissing",
- callback.getClass().getName(),
container.getName()));
+ callback.getClass().getName(),
container.getName()));
} else {
PasswordValidationCallback pvc =
(PasswordValidationCallback) callback;
principal =
container.getRealm().authenticate(pvc.getUsername(),
- String.valueOf(pvc.getPassword()));
+
String.valueOf(pvc.getPassword()));
subject = pvc.getSubject();
}
} else {
log.error(sm.getString("callbackHandlerImpl.jaspicCallbackMissing",
- callback.getClass().getName()));
+ callback.getClass().getName()));
}
}
@@ -119,6 +118,11 @@ public class CallbackHandlerImpl implements
CallbackHandler, Contained {
}
subject.getPrivateCredentials().add(new
GenericPrincipal(mergeName, null, mergeRoles, mergePrincipal));
+
+ // may come from CallerPrincipalCallback and we need to being
to get it from the Subject
+ if (principal != null) {
+ subject.getPrincipals().add(principal);
+ }
}
}
}
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/Contexts.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/Contexts.java
index 2208d91..c002279 100644
--- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/Contexts.java
+++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/Contexts.java
@@ -25,10 +25,20 @@ import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.util.ContextName;
+import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
public class Contexts {
+
+ public static String toAppContext(final ServletContext servletContext,
final String contextPath) {
+ return servletContext.getVirtualServerName() + " " + contextPath;
+ }
+
+ public static String toAppContext(final StandardContext standardContext) {
+ return toAppContext(standardContext.getServletContext(),
standardContext.getPath());
+ }
+
public static String getHostname(final StandardContext ctx) {
String hostName = null;
final Container parentHost = ctx.getParent();
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java
index f21261c..058367d 100644
---
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java
+++
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java
@@ -31,6 +31,7 @@ import org.apache.tomee.loader.TomcatHelper;
import javax.security.auth.Subject;
import javax.security.auth.login.CredentialNotFoundException;
import javax.security.auth.login.LoginException;
+import javax.security.jacc.PolicyContextException;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.security.Principal;
@@ -119,13 +120,27 @@ public class TomcatSecurityService extends
AbstractSecurityService {
}
private Subject createSubject(final Realm realm, final Principal
principal) {
- final Set<Principal> principals = new HashSet<>();
- if (principal.getClass().isAnnotationPresent(CallerPrincipal.class)) {
- principals.add(principal);
- } else {
- principals.add(new TomcatUser(realm, principal));
+ final Subject subject = new Subject();
+
+ // 1. Add the principal as is
+ subject.getPrincipals().add(principal);
+ subject.getPrincipals().add(new TomcatUser(realm, principal));
+
+ Principal p = principal;
+ if (principal instanceof TomcatUser) { // should never happen
+ p = ((TomcatUser) principal).getTomcatPrincipal();
+ subject.getPrincipals().add(p);
}
- return new Subject(true, principals, new HashSet(), new HashSet());
+
+ if (p instanceof GenericPrincipal) {
+ final GenericPrincipal genericPrincipal = (GenericPrincipal) p;
+ subject.getPrincipals().add(genericPrincipal.getUserPrincipal());
+
+ // todo should we create credentials with the roles? groups?
+ subject.getPrivateCredentials().add(p);
+ }
+
+ return subject;
}
@Override
@@ -366,4 +381,27 @@ public class TomcatSecurityService extends
AbstractSecurityService {
return super.getDefaultSecurityContext();
}
+ @Override
+ public boolean supports(final String key) throws PolicyContextException {
+ return KEYS.contains(key);
+ }
+
+ @Override
+ public String[] getKeys() throws PolicyContextException {
+ return KEYS.toArray(new String[0]);
+ }
+
+ @Override
+ public Object getContext(final String key, final Object data) throws
PolicyContextException {
+ switch (key) {
+ case KEY_REQUEST:
+ return OpenEJBSecurityListener.requests.get();
+ case KEY_SUBJECT:
+ // quite obvious as internally we keep track of it
+ // but we could also grab the request and the principals and
build a new Subject with the principals
+ return getSubject();
+ default:
+ throw new PolicyContextException("Handler does not support
key: " + key);
+ }
+ }
}
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java
index f5da4f9..84fc828 100644
---
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java
+++
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java
@@ -73,10 +73,12 @@ import org.apache.openejb.assembler.classic.ConnectorInfo;
import org.apache.openejb.assembler.classic.DeploymentExceptionManager;
import org.apache.openejb.assembler.classic.EjbJarInfo;
import org.apache.openejb.assembler.classic.InjectionBuilder;
+import org.apache.openejb.assembler.classic.JaccPermissionsBuilder;
import org.apache.openejb.assembler.classic.JndiEncBuilder;
import org.apache.openejb.assembler.classic.OpenEjbConfiguration;
import org.apache.openejb.assembler.classic.OpenEjbConfigurationFactory;
import org.apache.openejb.assembler.classic.PersistenceUnitInfo;
+import org.apache.openejb.assembler.classic.PolicyContext;
import org.apache.openejb.assembler.classic.ReloadableEntityManagerFactory;
import org.apache.openejb.assembler.classic.ResourceInfo;
import org.apache.openejb.assembler.classic.ServletInfo;
@@ -122,6 +124,7 @@ import
org.apache.tomcat.util.descriptor.web.ContextTransaction;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.descriptor.web.ResourceBase;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.http.CookieProcessor;
import org.apache.tomcat.util.scan.StandardJarScanFilter;
import org.apache.tomee.catalina.cdi.ServletContextHandler;
@@ -131,6 +134,7 @@ import
org.apache.tomee.catalina.cluster.TomEEClusterListener;
import org.apache.tomee.catalina.environment.Hosts;
import org.apache.tomee.catalina.event.AfterApplicationCreated;
import org.apache.tomee.catalina.routing.RouterValve;
+import
org.apache.tomee.catalina.security.TomcatSecurityConstaintsToJaccPermissionsTransformer;
import org.apache.tomee.common.NamingUtil;
import org.apache.tomee.common.UserTransactionFactory;
import org.apache.tomee.config.TomEESystemConfig;
@@ -145,6 +149,8 @@ import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
+import javax.security.jacc.PolicyConfiguration;
+import javax.security.jacc.PolicyConfigurationFactory;
import javax.servlet.ServletContext;
import javax.servlet.SessionTrackingMode;
import javax.servlet.http.HttpServletRequest;
@@ -597,6 +603,17 @@ public class TomcatWebAppBuilder implements WebAppBuilder,
ContextListener, Pare
} else { // force a normal deployment with lazy building
of AppInfo
deployWar(standardContext, host, null);
}
+
+ // TODO should we copy the information in the appInfo
using the jee object tree or add more to the info tree
+ // this might then move to the assembler after webapp is
deployed so we can read information from info tree
+ // and build up all policy context from there instead of
from Tomcat internal objects
+ final TomcatSecurityConstaintsToJaccPermissionsTransformer
transformer =
+ new
TomcatSecurityConstaintsToJaccPermissionsTransformer(standardContext);
+ final PolicyContext policyContext =
transformer.createResourceAndDataPermissions();
+
+ final JaccPermissionsBuilder jaccPermissionsBuilder = new
JaccPermissionsBuilder();
+ jaccPermissionsBuilder.install(policyContext);
+
}
}
} finally { // cleanup temp var passing
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/HTTPMethods.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/HTTPMethods.java
new file mode 100644
index 0000000..1356e69
--- /dev/null
+++
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/HTTPMethods.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2018 OmniFaces.
+ * Copyright 2003-2011 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.catalina.security;
+
+/**
+ *
+ * @author Guillermo González de Agüero
+ */
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tracks sets of HTTP actions for use while computing permissions during web
+ * deployment.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HTTPMethods {
+
+ private final Set<String> methods = new HashSet<String>();
+
+ private boolean isExcluded = false;
+
+ public HTTPMethods(Set<String> httpMethods, boolean isExcluded) {
+ this.isExcluded = isExcluded;
+ methods.addAll(httpMethods);
+ }
+
+ public HTTPMethods(HTTPMethods httpMethods, boolean complemented) {
+ isExcluded = httpMethods.isExcluded ^ complemented;
+ methods.addAll(httpMethods.methods);
+ }
+
+ /**
+ * Generally speaking, add method is to perform a union action between the
+ * caller and the parameters
+ *
+ * @param httpMethods
+ * @param addedMethodsExcluded
+ */
+ public void add(Set<String> httpMethods, boolean addedMethodsExcluded) {
+ //JACC 3.1.3.2 Combining HTTP Methods
+ //An empty list combines with any other list to yield the empty list.
+ if (isExcluded && httpMethods.isEmpty()) {
+ return;
+ }
+ if (httpMethods.size() == 0) {
+ isExcluded = addedMethodsExcluded;
+ methods.clear();
+ return;
+ }
+ //JACC 3.1.3.2 Combing HTTP Methods
+ //Lists of http-method elements combine to yield a list of http-method
elements containing the union (without duplicates) of the http-method elements
that occur in the individual lists.
+ //Lists of http-method-omission elements combine to yield a list
containing only the http-method-omission elements that occur in all of the
individual lists (i.e., the intersection).
+ //A list of http-method-omission elements combines with a list of
http-method elements to yield the list of http-method-omission elements minus
any elements whose method name occurs in the http-method list
+ if (isExcluded) {
+ if (addedMethodsExcluded) {
+ //ExceptionList + ExceptionList
+ methods.retainAll(httpMethods);
+ } else {
+ //ExceptionList + List
+ methods.removeAll(httpMethods);
+ }
+ } else {
+ if (addedMethodsExcluded) {
+ //List + ExceptionList
+ Set<String> tempHttpMethods = new HashSet<String>(httpMethods);
+ tempHttpMethods.removeAll(methods);
+ methods.clear();
+ methods.addAll(tempHttpMethods);
+ isExcluded = true;
+ } else {
+ //List + List
+ methods.addAll(httpMethods);
+ }
+ }
+ }
+
+ public HTTPMethods add(HTTPMethods httpMethods) {
+ add(httpMethods.methods, httpMethods.isExcluded);
+ return this;
+ }
+
+ /**
+ * Remove methods is only used while we wish to remove those configurations
+ * in role/unchecked constraints, which are also configured in excluded
+ * constraints
+ *
+ * @param httpMethods
+ * @return
+ */
+ public HTTPMethods remove(HTTPMethods httpMethods) {
+ if (isExcluded) {
+ if (httpMethods.isExcluded) {
+ //TODO questionable
+ isExcluded = false;
+ Set<String> toRemove = new HashSet<String>(methods);
+ methods.clear();
+ methods.addAll(httpMethods.methods);
+ methods.removeAll(toRemove);
+ } else {
+ methods.addAll(httpMethods.methods);
+ }
+ } else {
+ if (httpMethods.isExcluded) {
+ methods.retainAll(httpMethods.methods);
+ } else {
+ methods.removeAll(httpMethods.methods);
+ }
+ }
+ if (!isExcluded && methods.isEmpty()) {
+ return null;
+ }
+ return this;
+ }
+
+ public String getHttpMethods() {
+ return getHttpMethodsBuffer(isExcluded).toString();
+ }
+
+ public StringBuilder getHttpMethodsBuffer() {
+ return getHttpMethodsBuffer(isExcluded);
+ }
+
+ public String getComplementedHttpMethods() {
+ return getHttpMethodsBuffer(!isExcluded).toString();
+ }
+
+ private StringBuilder getHttpMethodsBuffer(boolean excluded) {
+ StringBuilder buffer = new StringBuilder();
+ if (excluded) {
+ buffer.append("!");
+ }
+ boolean afterFirst = false;
+ for (String method : methods) {
+ if (afterFirst) {
+ buffer.append(",");
+ } else {
+ afterFirst = true;
+ }
+ buffer.append(method);
+ }
+ return buffer;
+ }
+
+ public boolean isNone() {
+ return !isExcluded && methods.isEmpty();
+ }
+}
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/TomcatSecurityConstaintsToJaccPermissionsTransformer.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/TomcatSecurityConstaintsToJaccPermissionsTransformer.java
new file mode 100644
index 0000000..94d88f8
--- /dev/null
+++
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/TomcatSecurityConstaintsToJaccPermissionsTransformer.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2018 OmniFaces.
+ * Copyright 2003-2011 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.catalina.security;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.StandardContext;
+import org.apache.openejb.assembler.classic.DelegatePermissionCollection;
+import org.apache.openejb.assembler.classic.PolicyContext;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+
+import javax.security.jacc.PolicyContextException;
+import javax.security.jacc.WebResourcePermission;
+import javax.security.jacc.WebRoleRefPermission;
+import javax.security.jacc.WebUserDataPermission;
+import javax.servlet.ServletContext;
+import java.security.PermissionCollection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+
+/**
+ * @author Guillermo González de Agüero
+ */
+public class TomcatSecurityConstaintsToJaccPermissionsTransformer {
+
+ // from context
+ final StandardContext standardContext;
+ private final List<SecurityConstraint> constraints;
+ private final List<String> declaredRoles;
+ private final boolean isDenyUncoveredHttpMethods;
+
+ // final result
+ private final PolicyContext policyContext;
+
+ // computed in various methods
+ private final Set<String> securityRoles = new HashSet<>();
+ private final Map<String, URLPattern> uncheckedPatterns = new HashMap<>();
+ private final Map<UncheckedItem, HTTPMethods> uncheckedResourcePatterns =
new HashMap<>();
+ private final Map<UncheckedItem, HTTPMethods> uncheckedUserPatterns = new
HashMap<>();
+ private final Map<String, URLPattern> excludedPatterns = new HashMap<>();
+ private final Map<String, Map<String, URLPattern>> rolesPatterns = new
HashMap<>();
+ private final Set<URLPattern> allSet = new HashSet<>();
+ private final Map<String, URLPattern> allMap = new HashMap<>();
//uncheckedPatterns union excludedPatterns union rolesPatterns.
+
+ public TomcatSecurityConstaintsToJaccPermissionsTransformer(final
StandardContext standardContext) {
+ this.standardContext = standardContext;
+
+ constraints = new
ArrayList<>(asList(standardContext.findConstraints()));
+ declaredRoles = asList(standardContext.findSecurityRoles());
+ isDenyUncoveredHttpMethods =
standardContext.getDenyUncoveredHttpMethods();
+
+ // todo move all host context id crap into something consistent like
in Tomcat JASPIC`
+ // instead of only using host and context path - consistency and safe
+ final ServletContext servletContext =
standardContext.getServletContext();
+ final String id = servletContext.getVirtualServerName() + " " +
servletContext.getContextPath();
+ policyContext = new PolicyContext(id);
+ }
+
+ public PolicyContext createResourceAndDataPermissions() {
+
+ securityRoles.addAll(declaredRoles);
+
+ // todo, improve this part so roles are extracted in the
constructor and we don't need standard context
+ // it should even receive in the constructor all required
information from TomcatWebAppBuilder so it's more testable
+ // find all role ref permission - probably too wide
+ for (Container container : standardContext.findChildren()) {
+ if (container instanceof Wrapper) {
+ processRoleRefPermissions((Wrapper) container);
+ }
+ }
+
+ addUnmappedJSPPermissions();
+ analyzeSecurityConstraints();
+ removeExcludedDups();
+ buildPermissions();
+
+ // all populated now
+ return policyContext;
+
+ }
+
+ private void analyzeSecurityConstraints() {
+ for (SecurityConstraint securityConstraint : constraints) {
+ Map<String, URLPattern> currentPatterns = null;
+ Set<String> roleNames = null;
+ if (securityConstraint.getAuthConstraint()) {
+ if (securityConstraint.findAuthRoles().length == 0) {
+ currentPatterns = excludedPatterns;
+
+ } else {
+ roleNames = new
HashSet<String>(Arrays.asList(securityConstraint.findAuthRoles()));
+ if (roleNames.remove("*")) {
+ roleNames.addAll(securityRoles);
+ }
+ }
+
+ } else {
+ currentPatterns = uncheckedPatterns;
+ }
+ String transport = securityConstraint.getUserConstraint() == null
? "NONE" : securityConstraint.getUserConstraint();
+
+ boolean isRoleBasedPattern = (currentPatterns == null);
+
+ if (securityConstraint.findCollections() != null) {
+ for (SecurityCollection webResourceCollection :
securityConstraint.findCollections()) {
+ //Calculate HTTP methods list
+ for (String urlPattern :
webResourceCollection.findPatterns()) {
+
+ if (isRoleBasedPattern) {
+ for (String roleName : roleNames) {
+ Map<String, URLPattern> currentRolePatterns =
rolesPatterns.get(roleName);
+ if (currentRolePatterns == null) {
+ currentRolePatterns = new HashMap<>();
+ rolesPatterns.put(roleName,
currentRolePatterns);
+ }
+
+ boolean omission = false;
+ String[] httpMethods =
webResourceCollection.findMethods();
+ if (httpMethods.length == 0) {
+ omission = true;
+ httpMethods =
webResourceCollection.findOmittedMethods();
+ }
+
+ analyzeURLPattern(urlPattern, new
HashSet<>(Arrays.asList(httpMethods)), omission, transport,
currentRolePatterns);
+ }
+
+ } else {
+ boolean omission = false;
+ String[] httpMethods =
webResourceCollection.findMethods();
+ if (httpMethods.length == 0) {
+ omission = true;
+ httpMethods =
webResourceCollection.findOmittedMethods();
+ }
+
+ analyzeURLPattern(urlPattern, new
HashSet<>(Arrays.asList(httpMethods)), omission, transport, currentPatterns);
+ }
+ URLPattern allPattern = allMap.get(urlPattern);
+
+ if (allPattern == null) {
+ boolean omission = false;
+ String[] httpMethods =
webResourceCollection.findMethods();
+ if (httpMethods.length == 0) {
+ omission = true;
+ httpMethods =
webResourceCollection.findOmittedMethods();
+ }
+
+ allPattern = new URLPattern(urlPattern, new
HashSet<>(Arrays.asList(httpMethods)), omission);
+ allSet.add(allPattern);
+ allMap.put(urlPattern, allPattern);
+
+ } else {
+ boolean omission = false;
+ String[] httpMethods =
webResourceCollection.findMethods();
+ if (httpMethods.length == 0) {
+ omission = true;
+ httpMethods =
webResourceCollection.findOmittedMethods();
+ }
+
+ allPattern.addMethods(new
HashSet<>(Arrays.asList(httpMethods)), omission);
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ private void analyzeURLPattern(final String urlPattern,
+ final Set<String> httpMethods,
+ final boolean omission,
+ final String transport,
+ final Map<String, URLPattern> currentPatterns)
{
+
+ URLPattern pattern = currentPatterns.get(urlPattern);
+ if (pattern == null) {
+ pattern = new URLPattern(urlPattern, httpMethods, omission);
+ currentPatterns.put(urlPattern, pattern);
+
+ } else {
+ pattern.addMethods(httpMethods, omission);
+ }
+ pattern.setTransport(transport);
+ }
+
+ private void removeExcludedDups() {
+ for (Map.Entry<String, URLPattern> excluded :
excludedPatterns.entrySet()) {
+ String url = excluded.getKey();
+ URLPattern pattern = excluded.getValue();
+ removeExcluded(url, pattern, uncheckedPatterns);
+ for (Map<String, URLPattern> rolePatterns :
rolesPatterns.values()) {
+ removeExcluded(url, pattern, rolePatterns);
+ }
+ }
+ }
+
+ private void removeExcluded(final String url, final URLPattern pattern,
final Map<String, URLPattern> patterns) {
+ URLPattern testPattern = patterns.get(url);
+ if (testPattern != null) {
+ if (!testPattern.removeMethods(pattern)) {
+ patterns.remove(url);
+ }
+ }
+ }
+
+ private void buildPermissions() {
+
+ for (URLPattern pattern : excludedPatterns.values()) {
+ String name = pattern.getQualifiedPattern(allSet);
+ String actions = pattern.getMethods();
+ policyContext.getExcludedPermissions().add(new
WebResourcePermission(name, actions));
+ policyContext.getExcludedPermissions().add(new
WebUserDataPermission(name, actions));
+ }
+
+ for (Map.Entry<String, Map<String, URLPattern>> entry :
rolesPatterns.entrySet()) {
+ Set<URLPattern> currentRolePatterns = new
HashSet<URLPattern>(entry.getValue().values());
+ for (URLPattern pattern : entry.getValue().values()) {
+ String name = pattern.getQualifiedPattern(currentRolePatterns);
+ String actions = pattern.getMethods();
+ WebResourcePermission permission = new
WebResourcePermission(name, actions);
+ policyContext.addRole(entry.getKey(), permission);
+ HTTPMethods methods = pattern.getHTTPMethods();
+ int transportType = pattern.getTransport();
+ addOrUpdatePattern(uncheckedUserPatterns, name, methods,
transportType);
+ }
+ }
+
+ for (URLPattern pattern : uncheckedPatterns.values()) {
+ String name = pattern.getQualifiedPattern(allSet);
+ HTTPMethods methods = pattern.getHTTPMethods();
+ addOrUpdatePattern(uncheckedResourcePatterns, name, methods,
URLPattern.NA);
+ int transportType = pattern.getTransport();
+ addOrUpdatePattern(uncheckedUserPatterns, name, methods,
transportType);
+ }
+
+ /*
+ * A <code>WebResourcePermission</code> and a
+ * <code>WebUserDataPermission</code> must be instantiated for each
+ * <tt>url-pattern</tt> in the deployment descriptor and the default
+ * pattern "/", that is not combined by the
+ * <tt>web-resource-collection</tt> elements of the deployment
+ * descriptor with ever HTTP method value. The permission objects must
+ * be contructed using the qualified pattern as their name and with
+ * actions defined by the subset of the HTTP methods that do not occur
+ * in combination with the pattern. The resulting permissions that must
+ * be added to the unchecked policy statements by calling the
+ * <code>addToUncheckedPolcy</code> method on the
+ * <code>PolicyConfiguration</code> object.
+ */
+ for (URLPattern pattern : allSet) {
+ String name = pattern.getQualifiedPattern(allSet);
+ HTTPMethods methods = pattern.getComplementedHTTPMethods();
+ if (methods.isNone()) {
+ continue;
+ }
+ addOrUpdatePattern(uncheckedResourcePatterns, name, methods,
URLPattern.NA);
+ addOrUpdatePattern(uncheckedUserPatterns, name, methods,
URLPattern.NA);
+ }
+
+ if (!allMap.containsKey("/")) {
+ URLPattern pattern = new URLPattern("/",
Collections.<String>emptySet(), false);
+ String name = pattern.getQualifiedPattern(allSet);
+ HTTPMethods methods = pattern.getComplementedHTTPMethods();
+ addOrUpdatePattern(uncheckedResourcePatterns, name, methods,
URLPattern.NA);
+ addOrUpdatePattern(uncheckedUserPatterns, name, methods,
URLPattern.NA);
+ }
+
+ //Create the uncheckedPermissions for WebResourcePermissions
+ for (UncheckedItem item : uncheckedResourcePatterns.keySet()) {
+ HTTPMethods methods = uncheckedResourcePatterns.get(item);
+ String actions = URLPattern.getMethodsWithTransport(methods,
item.getTransportType());
+ policyContext.getUncheckedPermissions().add(new
WebResourcePermission(item.getName(), actions));
+ }
+
+ //Create the uncheckedPermissions for WebUserDataPermissions
+ for (UncheckedItem item : uncheckedUserPatterns.keySet()) {
+ HTTPMethods methods = uncheckedUserPatterns.get(item);
+ String actions = URLPattern.getMethodsWithTransport(methods,
item.getTransportType());
+ policyContext.getUncheckedPermissions().add(new
WebUserDataPermission(item.getName(), actions));
+ }
+ }
+
+ private void addOrUpdatePattern(final Map<UncheckedItem, HTTPMethods>
patternMap,
+ final String name,
+ final HTTPMethods actions,
+ final int transportType) {
+
+ final UncheckedItem item = new UncheckedItem(name, transportType);
+ final HTTPMethods existingActions = patternMap.get(item);
+ if (existingActions != null) {
+ patternMap.put(item, existingActions.add(actions));
+
+ } else {
+ patternMap.put(item, new HTTPMethods(actions, false));
+ }
+ }
+
+ protected void processRoleRefPermissions(Wrapper servlet) {
+
+ final String servletName = servlet.getName();
+
+ //WebRoleRefPermissions
+ Set<String> unmappedRoles = new HashSet<>(securityRoles);
+ for (String securityRoleRef : servlet.findSecurityReferences()) {
+ //jacc 3.1.3.2
+ /* The name of the WebRoleRefPermission must be the servlet-name
in whose
+ * context the security-role-ref is defined. The actions of the
WebRoleRefPermission
+ * must be the value of the role-name (that is the reference),
appearing in the security-role-ref.
+ * The deployment tools must call the addToRole method on the
PolicyConfiguration object to add the
+ * WebRoleRefPermission object resulting from the translation to
the role
+ * identified in the role-link appearing in the security-role-ref.
+ */
+
policyContext.addRole(servlet.findSecurityReference(securityRoleRef),
+ new
WebRoleRefPermission(servletName, securityRoleRef));
+ unmappedRoles.remove(securityRoleRef);
+ }
+
+ for (String roleName : unmappedRoles) {
+ policyContext.addRole(roleName, new
WebRoleRefPermission(servletName, roleName));
+ }
+ }
+
+ protected void addUnmappedJSPPermissions() {
+ for (String roleName : securityRoles) {
+ policyContext.addRole(roleName, new WebRoleRefPermission("",
roleName));
+ }
+ }
+
+}
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/URLPattern.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/URLPattern.java
new file mode 100644
index 0000000..e43023c
--- /dev/null
+++
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/URLPattern.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2018 OmniFaces.
+ * Copyright 2003-2011 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.catalina.security;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * @author Guillermo González de Agüero
+ */
+
+/**
+ * Utility class for <code>ModuleConfiguration</code>. This class is used to
generate qualified patterns, HTTP
+ * method sets, complements of HTTP method sets, and HTTP method sets w/
transport restrictions for URL patterns that
+ * are found in the web deployment descriptor.
+ *
+ * @version $Rev$ $Date$
+ */
+public class URLPattern {
+ public final static int NA = 0x00;
+ public final static int INTEGRAL = 0x01;
+ public final static int CONFIDENTIAL = 0x02;
+
+ private final URLPatternCheck type;
+ private final String pattern;
+ private final HTTPMethods httpMethods;
+ private int transport;
+
+ /**
+ * Construct an instance of the utility class for
<code>WebModuleConfiguration</code>.
+ *
+ * @param pat the URL pattern that this instance is to collect information
on
+ * @see "JSR 115, section 3.1.3" Translating Servlet Deployment Descriptors
+ */
+ public URLPattern(String pat, Set<String> methods, boolean
isHttpMethodExcluded) {
+ if (pat == null)
+ throw new IllegalArgumentException("URL pattern cannot be null");
+ if (pat.length() == 0)
+ throw new IllegalArgumentException("URL pattern cannot be empty");
+ if (pat.equals("/") || pat.equals("/*")) {
+ type = DEFAULT;
+ pat = "/";
+ } else if (pat.charAt(0) == '/' && pat.endsWith("/*")) {
+ type = PATH_PREFIX;
+ } else if (pat.charAt(0) == '*') {
+ type = EXTENSION;
+ } else {
+ type = EXACT;
+ }
+ pattern = pat;
+ httpMethods = new HTTPMethods(methods, isHttpMethodExcluded);
+ }
+
+ /**
+ * Get a qualifed URL pattern relative to a particular set of URL
patterns. This algorithm is described in
+ * JSR 115, section 3.1.3.1 "Qualified URL Pattern Names".
+ *
+ * @param patterns the set of possible URL patterns that could be used to
qualify this pattern
+ * @return a qualifed URL pattern
+ */
+ public String getQualifiedPattern(Set<URLPattern> patterns) {
+ if (type == EXACT) {
+ return pattern;
+ } else {
+ HashSet<String> bucket = new HashSet<String>();
+ StringBuilder result = new StringBuilder(pattern);
+
+ // Collect a set of qualifying patterns, depending on the type of
this pattern.
+ for (URLPattern p : patterns) {
+ if (type.check(this, p)) {
+ bucket.add(p.pattern);
+ }
+ }
+ // append the set of qualifying patterns
+ for (String aBucket : bucket) {
+ result.append(':');
+ result.append(aBucket);
+ }
+ return result.toString();
+ }
+ }
+
+ /**
+ * Add a method to the union of HTTP methods associated with this URL
pattern. An empty Set is short hand for
+ * the set of all HTTP methods.
+ *
+ * @param methods the HTTP methods to be added to the set.
+ */
+ public void addMethods(Set<String> methods, boolean isExcluded) {
+ httpMethods.add(methods, isExcluded);
+ }
+
+ public boolean removeMethods(URLPattern other) {
+ return httpMethods.remove(other.getHTTPMethods()) != null;
+ }
+
+ /**
+ * Return the set of HTTP methods that have been associated with this URL
pattern.
+ *
+ * @return a set of HTTP methods
+ */
+ public String getMethods() {
+ return httpMethods.getHttpMethods();
+ }
+
+
+ public String getComplementedMethods() {
+ return httpMethods.getComplementedHttpMethods();
+ }
+
+ public HTTPMethods getHTTPMethods() {
+ return httpMethods;
+ }
+
+ public HTTPMethods getComplementedHTTPMethods() {
+ return new HTTPMethods(httpMethods, true);
+ }
+
+ public String getMethodsWithTransport() {
+ return getMethodsWithTransport(httpMethods, transport);
+ }
+
+ public static String getMethodsWithTransport(HTTPMethods methods, int
transport) {
+ StringBuilder buffer = methods.getHttpMethodsBuffer();
+
+
+ if (transport != NA) {
+ buffer.append(":");
+
+ if (transport != 0x03) {
+ if (transport == INTEGRAL) {
+ buffer.append("INTEGRAL");
+ } else {
+ buffer.append("CONFIDENTIAL");
+ }
+ }
+ }
+
+ return buffer.toString();
+ }
+
+ public void setTransport(String trans) {
+ switch (transport) {
+ case NA: {
+ if ("INTEGRAL".equals(trans)) {
+ transport = INTEGRAL;
+ } else if ("CONFIDENTIAL".equals(trans)) {
+ transport = CONFIDENTIAL;
+ }
+ break;
+ }
+
+ case INTEGRAL: {
+ if ("CONFIDENTIAL".equals(trans)) {
+ transport = CONFIDENTIAL;
+ }
+ break;
+ }
+ }
+ }
+
+ public int getTransport() {
+ return transport;
+ }
+
+ /**
+ * TODO this is kinda weird without an explanation
+ * @param obj object to compare with
+ * @return if this equals obj
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof URLPattern)) return false;
+
+ URLPattern test = (URLPattern) obj;
+
+ return pattern.equals(test.pattern);
+ }
+
+ public int hashCode() {
+ return pattern.hashCode();
+ }
+
+ boolean matches(URLPattern p) {
+ String test = p.pattern;
+
+ // their pattern values are String equivalent
+ if (pattern.equals(test)) return true;
+
+ return type.matches(pattern, test);
+ }
+
+ private final static URLPatternCheck EXACT = new URLPatternCheck() {
+ public boolean check(URLPattern base, URLPattern test) {
+ return matches(base.pattern, test.pattern);
+ }
+
+ public boolean matches(String base, String test) {
+ return base.equals(test);
+ }
+ };
+
+ private final static URLPatternCheck PATH_PREFIX = new URLPatternCheck() {
+ public boolean check(URLPattern base, URLPattern test) {
+ return ((test.type == PATH_PREFIX || test.type == EXACT)
+ && base.matches(test)
+ && !base.equals(test));
+ }
+
+ /**
+ * This pattern is a path-prefix pattern (that is, it starts with "/"
and ends with "/*") and the argument
+ * pattern starts with the substring of this pattern, minus its last 2
characters, and the next character of
+ * the argument pattern, if there is one, is "/"
+ *
+ * @param base the base pattern
+ * @param test the pattern to be tested
+ * @return <code>true</code> if <code>test</code> is matched by
<code>base</code>
+ */
+ public boolean matches(String base, String test) {
+ int length = base.length() - 2;
+ if (length > test.length()) return false;
+
+ for (int i = 0; i < length; i++) {
+ if (base.charAt(i) != test.charAt(i)) return false;
+ }
+
+ if (test.length() == length)
+ return true;
+ else if (test.charAt(length) != '/') return false;
+
+ return true;
+ }
+ };
+
+ private final static URLPatternCheck EXTENSION = new URLPatternCheck() {
+ public boolean check(URLPattern base, URLPattern test) {
+ if (test.type == PATH_PREFIX) return true;
+
+ if (test.type == EXACT) return matches(base.pattern, test.pattern);
+
+ return false;
+ }
+
+ /**
+ * This pattern is an extension pattern (that is, it startswith "*.")
and the argument pattern ends with
+ * this pattern.
+ *
+ * @param base the base pattern
+ * @param test the pattern to be tested
+ * @return <code>true</code> if <code>test</code> is matched by
<code>base</code>
+ */
+ public boolean matches(String base, String test) {
+ return test.endsWith(base.substring(1));
+ }
+ };
+
+ private final static URLPatternCheck DEFAULT = new URLPatternCheck() {
+ public boolean check(URLPattern base, URLPattern test) {
+ return base.matches(test) && !base.equals(test);
+ }
+
+ /**
+ * This pattern is the path-prefix pattern "/*" or the reference
pattern is the special default pattern,
+ * "/", which matches all argument patterns.
+ *
+ * @param base the base pattern
+ * @param test the pattern to be tested
+ * @return <code>true</code> if <code>test</code> is matched by
<code>base</code>
+ * @see "JSR 115"
+ */
+ public boolean matches(String base, String test) {
+ return true;
+ }
+ };
+}
\ No newline at end of file
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/URLPatternCheck.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/URLPatternCheck.java
new file mode 100644
index 0000000..5620270
--- /dev/null
+++
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/URLPatternCheck.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 OmniFaces.
+ * Copyright 2003-2011 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.catalina.security;
+
+/**
+ *
+ * @author Guillermo González de Agüero
+ */
+public abstract class URLPatternCheck {
+
+ public abstract boolean check(URLPattern base, URLPattern test);
+
+ public abstract boolean matches(String base, String test);
+}
\ No newline at end of file
diff --git
a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/UncheckedItem.java
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/UncheckedItem.java
new file mode 100644
index 0000000..fb82bda
--- /dev/null
+++
b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/security/UncheckedItem.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 OmniFaces.
+ * Copyright 2003-2011 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.catalina.security;
+
+/**
+ *
+ * @author Guillermo González de Agüero
+ */
+class UncheckedItem {
+ final static int NA = 0x00;
+ final static int INTEGRAL = 0x01;
+ final static int CONFIDENTIAL = 0x02;
+
+ private int transportType = NA;
+ private String name;
+
+ public UncheckedItem(String name, int transportType) {
+ setName(name);
+ setTransportType(transportType);
+ }
+
+ public boolean equals(Object o) {
+ if (o instanceof UncheckedItem) {
+ UncheckedItem item = (UncheckedItem) o;
+ return item.transportType == transportType &&
item.name.equals(this.name);
+ }
+ return false;
+ }
+
+
+ public int hashCode() {
+ return name.hashCode() + transportType;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getTransportType() {
+ return transportType;
+ }
+
+ public void setTransportType(int transportType) {
+ this.transportType = transportType;
+ }
+}
\ No newline at end of file
diff --git
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/TomEESecurityContext.java
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/TomEESecurityContext.java
index 037467e..4afcc7c 100644
---
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/TomEESecurityContext.java
+++
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/TomEESecurityContext.java
@@ -19,8 +19,11 @@ package org.apache.tomee.security;
import org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl;
import org.apache.catalina.connector.Request;
import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.openejb.core.security.JaccProvider;
+import org.apache.openejb.core.security.jacc.BasicJaccProvider;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.spi.SecurityService;
+import org.apache.openejb.util.JavaSecurityManagers;
import org.apache.tomee.catalina.OpenEJBSecurityListener;
import org.apache.tomee.catalina.TomcatSecurityService;
import org.apache.tomee.security.message.TomEEMessageInfo;
@@ -47,9 +50,12 @@ import java.util.Set;
import static javax.security.auth.message.AuthStatus.SEND_CONTINUE;
import static javax.security.auth.message.AuthStatus.SEND_FAILURE;
import static javax.security.auth.message.AuthStatus.SUCCESS;
+import static org.apache.tomee.catalina.Contexts.toAppContext;
public class TomEESecurityContext implements SecurityContext {
+
private TomcatSecurityService securityService;
+ private JaccProvider jaccProvider;
@PostConstruct
private void init() {
@@ -57,6 +63,7 @@ public class TomEESecurityContext implements SecurityContext {
if (securityService instanceof TomcatSecurityService) {
this.securityService = (TomcatSecurityService) securityService;
}
+ jaccProvider = JaccProvider.get();
}
@Override
@@ -66,8 +73,7 @@ public class TomEESecurityContext implements SecurityContext {
@Override
public <T extends Principal> Set<T> getPrincipalsByType(final Class<T>
pType) {
- // todo
- return Collections.emptySet();
+ return securityService.getPrincipalsByType(pType);
}
@Override
@@ -77,8 +83,7 @@ public class TomEESecurityContext implements SecurityContext {
@Override
public boolean hasAccessToWebResource(final String resource, final
String... methods) {
- // todo
- return false;
+ return jaccProvider.hasAccessToWebResource(resource, methods);
}
@Override
@@ -115,7 +120,7 @@ public class TomEESecurityContext implements
SecurityContext {
}
private ServerAuthContext getServerAuthContext(final HttpServletRequest
request) throws AuthException {
- final String appContext =
request.getServletContext().getVirtualServerName() + " " +
request.getContextPath();
+ final String appContext = toAppContext(request.getServletContext(),
request.getContextPath());
final AuthConfigProvider authConfigProvider =
AuthConfigFactory.getFactory().getConfigProvider("HttpServlet", appContext,
null);
@@ -126,6 +131,7 @@ public class TomEESecurityContext implements
SecurityContext {
}
public static void registerContainerAboutLogin(final Principal principal,
final Set<String> groups) {
+
final SecurityService securityService =
SystemInstance.get().getComponent(SecurityService.class);
if (securityService instanceof TomcatSecurityService) {
final TomcatSecurityService tomcatSecurityService =
(TomcatSecurityService) securityService;
@@ -136,9 +142,15 @@ public class TomEESecurityContext implements
SecurityContext {
null,
groups == null ? Collections.emptyList() : new
ArrayList<>(groups),
principal);
+
+ // todo should it be done in the enterWebApp?
+
JavaSecurityManagers.setContextID(toAppContext(request.getServletContext(),
request.getContextPath()));
+
tomcatSecurityService.enterWebApp(request.getWrapper().getRealm(),
genericPrincipal,
request.getWrapper().getRunAs());
}
}
+
+
}
diff --git
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/cdi/TomEESecurityExtension.java
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/cdi/TomEESecurityExtension.java
index 591523f..c4462c6 100644
---
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/cdi/TomEESecurityExtension.java
+++
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/cdi/TomEESecurityExtension.java
@@ -70,6 +70,8 @@ public class TomEESecurityExtension implements Extension {
private final AtomicReference<Annotated> databaseStore = new
AtomicReference<>();
private final AtomicReference<Annotated> ldapStore = new
AtomicReference<>();
+ private boolean applicationAuthenticationMechanisms = false;
+
void observeBeforeBeanDiscovery(
@Observes final BeforeBeanDiscovery beforeBeanDiscovery,
final BeanManager beanManager) {
@@ -117,6 +119,10 @@ public class TomEESecurityExtension implements Extension {
if (customMechanism.get() == null &&
annotatedType.isAnnotationPresent(CustomFormAuthenticationMechanismDefinition.class))
{
customMechanism.set(annotatedType);
}
+
+ if
(eventIn.getBean().getTypes().contains(HttpAuthenticationMechanism.class)) {
+ applicationAuthenticationMechanisms = true;
+ }
}
void registerAuthenticationMechanism(
@@ -294,7 +300,7 @@ public class TomEESecurityExtension implements Extension {
}
public boolean hasAuthenticationMechanisms() {
- return basicMechanism.get() != null || formMechanism.get() != null ||
customMechanism.get() != null;
+ return basicMechanism.get() != null || formMechanism.get() != null ||
customMechanism.get() != null || applicationAuthenticationMechanisms;
}
private Supplier<LoginToContinue> createFormLoginToContinueSupplier(final
BeanManager beanManager) {
diff --git
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/identitystore/TomEEIdentityStoreHandler.java
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/identitystore/TomEEIdentityStoreHandler.java
index 91e660d..122b277 100644
---
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/identitystore/TomEEIdentityStoreHandler.java
+++
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/identitystore/TomEEIdentityStoreHandler.java
@@ -24,6 +24,8 @@ import javax.security.enterprise.credential.Credential;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStore;
import javax.security.enterprise.identitystore.IdentityStoreHandler;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
@@ -94,10 +96,25 @@ public class TomEEIdentityStoreHandler implements
IdentityStoreHandler {
}
final Set<String> groups = new HashSet<>();
+
+ // Take the groups from the identity store that validated the
credentials only
+ // if it has been set to provide groups.
if (authorizedStore.validationTypes().contains(PROVIDE_GROUPS)) {
groups.addAll(validationResult.getCallerGroups());
}
+ // Ask all stores that were configured for group providing only to get
the groups for the
+ // authenticated caller
+ final CredentialValidationResult finalResult = validationResult; //
compiler didn't like validationResult in the enclosed scope
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ for (IdentityStore authorizationIdentityStore :
authenticationStores) {
+
groups.addAll(authorizationIdentityStore.getCallerGroups(finalResult));
+ }
+ return null;
+ }
+ });
+
final CredentialValidationResult authorizedValidationResult =
validationResult;
final Set<String> additionalGroups =
authorizationStores.stream()
diff --git
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/provider/TomEESecurityServerAuthConfig.java
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/provider/TomEESecurityServerAuthConfig.java
index 5686b68..5e3c75a 100644
---
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/provider/TomEESecurityServerAuthConfig.java
+++
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/provider/TomEESecurityServerAuthConfig.java
@@ -49,7 +49,7 @@ public class TomEESecurityServerAuthConfig implements
ServerAuthConfig {
@Override
public String getAuthContextID(final MessageInfo messageInfo) throws
IllegalArgumentException {
- return null;
+ return appContext; // not sure what's the difference with
getAppContext()
}
@Override
diff --git
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/servlet/TomEESecurityServletContainerInitializer.java
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/servlet/TomEESecurityServletContainerInitializer.java
index 704ee51..10e7ac0 100644
---
a/tomee/tomee-security/src/main/java/org/apache/tomee/security/servlet/TomEESecurityServletContainerInitializer.java
+++
b/tomee/tomee-security/src/main/java/org/apache/tomee/security/servlet/TomEESecurityServletContainerInitializer.java
@@ -24,11 +24,18 @@ import javax.enterprise.inject.spi.CDI;
import javax.security.auth.message.config.AuthConfigFactory;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Set;
-public class TomEESecurityServletContainerInitializer implements
ServletContainerInitializer {
+public class TomEESecurityServletContainerInitializer implements
ServletContainerInitializer, ServletContextListener {
+
+ public static final String CONTEXT_REGISTRATION_ID =
"org.apache.tomee.security.message.registrationId";
+
@Override
public void onStartup(final Set<Class<?>> c, final ServletContext ctx)
throws ServletException {
@@ -49,15 +56,35 @@ public class TomEESecurityServletContainerInitializer
implements ServletContaine
}
if (securityExtension.hasAuthenticationMechanisms()) {
- AuthConfigFactory.getFactory().registerConfigProvider(
- new TomEESecurityAuthConfigProvider(new HashMap(), null), //
todo we can probably do better
- "HttpServlet", //
from AuthenticatorBase.java:1245
- ctx.getVirtualServerName() + " " + ctx.getContextPath(),
// from AuthenticatorBase.java:1178
- "TomEE Security JSR-375");
+ final String registrationId = AccessController.doPrivileged(new
PrivilegedAction<String>() {
+ public String run() {
+ return
AuthConfigFactory.getFactory().registerConfigProvider(
+ new TomEESecurityAuthConfigProvider(new HashMap(),
null), // todo we can probably do better
+ "HttpServlet",
// from AuthenticatorBase.java:1245
+ ctx.getVirtualServerName() + " " +
ctx.getContextPath(), // from AuthenticatorBase.java:1178
+ "TomEE Security JSR-375");
+ }
+ });
+
+ if (registrationId != null) {
+ ctx.setAttribute(CONTEXT_REGISTRATION_ID, registrationId);
+ }
}
}
private BeanManager getBeanManager() throws IllegalStateException {
return CDI.current().getBeanManager();
}
+
+ @Override
+ public void contextDestroyed(final ServletContextEvent sce) {
+ String registrationId = (String)
sce.getServletContext().getAttribute(CONTEXT_REGISTRATION_ID);
+ if (registrationId != null && registrationId.length() > 0) {
+ AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return
AuthConfigFactory.getFactory().removeRegistration(registrationId);
+ }
+ });
+ }
+ }
}
diff --git
a/tomee/tomee-security/src/test/java/org/apache/tomee/security/servlet/JaccPermissionServletTest.java
b/tomee/tomee-security/src/test/java/org/apache/tomee/security/servlet/JaccPermissionServletTest.java
new file mode 100644
index 0000000..a051f64
--- /dev/null
+++
b/tomee/tomee-security/src/test/java/org/apache/tomee/security/servlet/JaccPermissionServletTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.security.servlet;
+
+import org.apache.tomee.security.AbstractTomEESecurityTest;
+import org.apache.tomee.security.cdi.TomcatUserIdentityStoreDefinition;
+import org.apache.tomee.security.client.BasicAuthFilter;
+import org.junit.Test;
+
+import javax.inject.Inject;
+import javax.security.enterprise.SecurityContext;
+import
javax.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.HttpConstraint;
+import javax.servlet.annotation.HttpMethodConstraint;
+import javax.servlet.annotation.ServletSecurity;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JaccPermissionServletTest extends AbstractTomEESecurityTest {
+
+ @Test
+ public void authenticate() throws Exception {
+ final String servlet = getAppUrl() + "/servlet2";
+ {
+ final String username = "tom";
+ final Response response = ClientBuilder.newBuilder().register(new
BasicAuthFilter(username, "secret1")).build()
+ .target(servlet)
+ .request()
+ .get();
+ assertEquals(200, response.getStatus());
+ final String responsePayload = response.readEntity(String.class);
+ System.out.println(responsePayload);
+
+ final StringBuilder sb = new StringBuilder(100);
+ sb.append("context username: ").append(username).append("\n");
+ sb.append("has GET access to /protectedServlet:
true").append("\n");
+ sb.append("has POST access to /protectedServlet: true");
+
+ assertTrue(responsePayload.contains(sb.toString()));
+ }
+ {
+ final String username = "bob";
+ final Response response = ClientBuilder.newBuilder().register(new
BasicAuthFilter(username, "secret3")).build()
+ .target(servlet)
+ .request()
+ .get();
+ assertEquals(200, response.getStatus());
+ final String responsePayload = response.readEntity(String.class);
+ System.out.println(responsePayload);
+
+ final StringBuilder sb = new StringBuilder(100);
+ sb.append("context username: ").append(username).append("\n");
+ sb.append("has GET access to /protectedServlet:
true").append("\n");
+ sb.append("has POST access to /protectedServlet: false");
+
+ assertTrue(responsePayload.contains(sb.toString()));
+ }
+ }
+
+ @WebServlet("/protectedServlet")
+ @ServletSecurity(value = @HttpConstraint(rolesAllowed = "Manager"),
+ httpMethodConstraints = { @HttpMethodConstraint("GET") })
+ @TomcatUserIdentityStoreDefinition
+ @BasicAuthenticationMechanismDefinition
+ public static class ProtectedServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse
response)
+ throws ServletException, IOException {
+
+ response.getWriter().write("This is a servlet \n");
+
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ super.doPost(req, resp);
+ }
+
+ }
+
+ @WebServlet("/servlet2")
+ @TomcatUserIdentityStoreDefinition
+ @BasicAuthenticationMechanismDefinition
+ public static class Servlet2 extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Inject
+ private SecurityContext securityContext;
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse
response)
+ throws ServletException, IOException {
+
+ response.getWriter().write("This is a servlet \n");
+
+ String contextName = null;
+ if (securityContext.getCallerPrincipal() != null) {
+ contextName = securityContext.getCallerPrincipal().getName();
+ }
+
+ response.getWriter().write("context username: " + contextName +
"\n");
+
+ response.getWriter().println("has GET access to /protectedServlet:
"
+ +
securityContext.hasAccessToWebResource("/protectedServlet", "GET"));
+
+ response.getWriter().println("has POST access to
/protectedServlet: "
+ +
securityContext.hasAccessToWebResource("/protectedServlet", "POST"));
+
+ }
+
+ }
+
+}
diff --git a/tomee/tomee-security/src/test/resources/conf/tomcat-users.xml
b/tomee/tomee-security/src/test/resources/conf/tomcat-users.xml
index d7de203..868e874 100644
--- a/tomee/tomee-security/src/test/resources/conf/tomcat-users.xml
+++ b/tomee/tomee-security/src/test/resources/conf/tomcat-users.xml
@@ -17,4 +17,8 @@
<tomcat-users>
<user name="tomcat" password="tomcat" roles="tomcat"/>
<user name="user" password="user" roles="user"/>
+
+ <user name="tom" password="secret1" roles="Administrator,Manager"/>
+ <user name="emma" password="secret2" roles="Administrator,Employee"/>
+ <user name="bob" password="secret3" roles="Administrator"/>
</tomcat-users>