You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jg...@apache.org on 2019/02/20 21:31:26 UTC

[tomee] branch master updated: Added README.adoc to cdi-realm - TOMEE-2372

This is an automated email from the ASF dual-hosted git repository.

jgallimore 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 28d5099  Added README.adoc to cdi-realm - TOMEE-2372
     new c3f5a55  Merge pull request #392 from BogdanStirbat/TOMEE-2372
28d5099 is described below

commit 28d50995ab1d36d5357be676c634e41c2e33fbfc
Author: Bogdan Stirbat <bo...@gmail.com>
AuthorDate: Sat Feb 9 16:58:03 2019 +0200

    Added README.adoc to cdi-realm - TOMEE-2372
---
 examples/cdi-realm/README.adoc | 181 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 181 insertions(+)

diff --git a/examples/cdi-realm/README.adoc b/examples/cdi-realm/README.adoc
new file mode 100644
index 0000000..df0f8be
--- /dev/null
+++ b/examples/cdi-realm/README.adoc
@@ -0,0 +1,181 @@
+= CDI Realm
+:index-group: Security
+:jbake-type: page
+:jbake-status: published
+
+This example shows how to secure access to a web resource provided by a servlet. For this, we will use realms.
+
+A https://docs.oracle.com/javaee/6/tutorial/doc/bnbxj.html[realm], in JEE world, is a security policy domain defined for a web or application server.
+A realm contains a collection of users, who may or may not be assigned to a group.
+
+A realm, basically, specifies a list of users and roles. I's a "database" of users with associated passwords and possible roles.
+The Servlet Specification doesn't specifies an API for specifying such a list of users and roles for a given application.
+For this reason, Tomcat servlet container defines an interface, `org.apache.catalina.Realm`. More information can be found https://tomcat.apache.org/tomcat-9.0-doc/realm-howto.html[here].
+
+In TomEE application server, the mechanism used by Tomcat to define a realm for a servlet is reused and enhanced. More information can be found https://www.tomitribe.com/blog/tomee-security-episode-1-apache-tomcat-and-apache-tomee-security-under-the-covers[here].
+
+== Example
+
+This example shows a servlet secured using a realm. The secured servlet has a simple functionality, just for ilustrating the concepts explained here:
+
+....
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@WebServlet("/servlet")
+public class SecuredServlet extends HttpServlet {
+    @Override
+    protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        resp.getWriter().write("Servlet!");
+    }
+}
+....
+
+For securing this servlet, we will add the following class:
+
+....
+import javax.enterprise.context.RequestScoped;
+import java.security.Principal;
+
+@RequestScoped // just to show we can be bound to the request but @ApplicationScoped is what makes sense
+public class AuthBean {
+    public Principal authenticate(final String username, String password) {
+        if (("userA".equals(username) || "userB".equals(username)) && "test".equals(password)) {
+            return new Principal() {
+                @Override
+                public String getName() {
+                    return username;
+                }
+
+                @Override
+                public String toString() {
+                    return username;
+                }
+            };
+        }
+        return null;
+    }
+
+    public boolean hasRole(final Principal principal, final String role) {
+        return principal != null && (
+                principal.getName().equals("userA") && (role.equals("admin")
+                        || role.equals("user"))
+                        || principal.getName().equals("userB") && (role.equals("user"))
+        );
+    }
+}
+....
+
+The class defines 2 methods: `authenticate` and `hasRole`.
+Both these methods will be used by a class, `LazyRealm`, implemented in TomEE application server.
+In the file `webapp/META-INF/context.xml` this realm is configured:
+
+....
+<Context preemptiveAuthentication="true">
+  <Valve className="org.apache.catalina.authenticator.BasicAuthenticator" />
+  <Realm className="org.apache.tomee.catalina.realm.LazyRealm"
+         cdi="true" realmClass="org.superbiz.AuthBean"/>
+</Context>
+....
+
+The class `AuthBean` defines a "database" with 2 users: userA (having role admin) and userB (having role user), both having the password test.
+Class `org.apache.tomee.catalina.realm.LazyRealm` will load our `AuthBean` class and will use it to check if a user has access to the content provided by our servlet.
+
+== Tests
+
+....
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.BasicAuthCache;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.openejb.arquillian.common.IO;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.asset.FileAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+@RunWith(Arquillian.class)
+public class AuthBeanTest {
+    @Deployment(testable = false)
+    public static WebArchive createDeployment() {
+        return ShrinkWrap.create(WebArchive.class, "low-typed-realm.war")
+                .addClasses(SecuredServlet.class, AuthBean.class)
+                .addAsManifestResource(new FileAsset(new File("src/main/webapp/META-INF/context.xml")), "context.xml")
+                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
+    }
+
+    @ArquillianResource
+    private URL webapp;
+
+    @Test
+    public void success() throws IOException {
+        assertEquals("200 Servlet!", get("userA", "test"));
+    }
+
+    @Test
+    public void failure() throws IOException {
+        assertThat(get("userA", "oops, wrong password"), startsWith("401"));
+    }
+
+    private String get(final String user, final String password) {
+        final BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
+        basicCredentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, password));
+        final CloseableHttpClient client = HttpClients.custom()
+                .setDefaultCredentialsProvider(basicCredentialsProvider).build();
+
+        final HttpHost httpHost = new HttpHost(webapp.getHost(), webapp.getPort(), webapp.getProtocol());
+        final AuthCache authCache = new BasicAuthCache();
+        final BasicScheme basicAuth = new BasicScheme();
+        authCache.put(httpHost, basicAuth);
+        final HttpClientContext context = HttpClientContext.create();
+        context.setAuthCache(authCache);
+
+        final HttpGet get = new HttpGet(webapp.toExternalForm() + "servlet");
+        CloseableHttpResponse response = null;
+        try {
+            response = client.execute(httpHost, get, context);
+            return response.getStatusLine().getStatusCode() + " " + EntityUtils.toString(response.getEntity());
+        } catch (final IOException e) {
+            throw new IllegalStateException(e);
+        } finally {
+            try {
+                IO.close(response);
+            } catch (final IOException e) {
+                // no-op
+            }
+        }
+    }
+}
+....
+
+The test uses Arquillian to start an application server and load the servlet.
+There are two tests methods: `success`, where our servlet is accessed with the correct username and password, and `failure`, where our servlet is accessed with an incorrect password.
+
+Full example can be found https://github.com/apache/tomee/tree/master/examples/cdi-realm[here].
+It's a maven project, and the test can be run with `mvn clean install` command.
\ No newline at end of file