brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [13/50] brooklyn-server git commit: [BROOKLYN-183] Configure Jetty to use JAAS authentication
Date Wed, 30 Mar 2016 13:59:50 GMT
[BROOKLYN-183] Configure Jetty to use JAAS authentication

* Implement an OSGi fragment so PAX-WEB code can load Jetty classes dynamically
* Default jetty.xml for karaf, registering the BrooklynLoginModule JAAS implementation
* Register JaasLoginService programatically in classical launcher
* Add support for roles in BrooklynLoginModule
* Register a default jaas.conf for classical launcher if one not already registered externally
* Configure Karaf Jetty to listen on port 8081


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/1de6a8a5
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/1de6a8a5
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/1de6a8a5

Branch: refs/heads/master
Commit: 1de6a8a5249377b86d3a7ee92480bfefa56a49f1
Parents: e1e7b20
Author: Svetoslav Neykov <svetoslav.neykov@cloudsoftcorp.com>
Authored: Sun Feb 28 15:03:29 2016 +0200
Committer: Svetoslav Neykov <svetoslav.neykov@cloudsoftcorp.com>
Committed: Thu Mar 17 15:25:45 2016 +0200

----------------------------------------------------------------------
 .../main/resources/etc/org.ops4j.pax.web.cfg    | 19 ++++
 karaf/features/src/main/feature/feature.xml     |  4 +-
 .../security/CustomSecurityProviderTest.java    | 14 ++-
 .../security/StockSecurityProviderTest.java     |  2 +-
 .../src/test/resources/custom-security-bp.xml   |  1 +
 karaf/jetty-config/pom.xml                      | 57 ++++++++++++
 karaf/jetty-config/src/main/resources/jetty.xml | 63 +++++++++++++
 karaf/pom.xml                                   |  1 +
 .../brooklyn/launcher/BrooklynWebServer.java    | 12 +++
 parent/pom.xml                                  |  5 ++
 rest/rest-resources/pom.xml                     |  9 ++
 .../rest/security/jaas/BrooklynLoginModule.java | 95 +++++++++++++++++---
 .../brooklyn/rest/security/jaas/JaasUtils.java  | 48 ++++++++++
 .../jaas/SecurityProviderHttpSession.java       | 17 +++-
 .../rest-resources/src/main/resources/jaas.conf | 21 +++++
 .../security/jaas/BrooklynLoginModuleTest.java  | 21 ++++-
 rest/rest-server-jersey/pom.xml                 | 32 +++++++
 rest/rest-server/pom.xml                        |  4 +
 .../brooklyn/rest/BrooklynRestApiLauncher.java  | 14 ++-
 19 files changed, 414 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.web.cfg
----------------------------------------------------------------------
diff --git a/karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.web.cfg b/karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.web.cfg
new file mode 100644
index 0000000..39947eb
--- /dev/null
+++ b/karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.web.cfg
@@ -0,0 +1,19 @@
+#
+#    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.
+#
+# TODO use the PortService - ${port:8081,8200}
+org.osgi.service.http.port=8081
+

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/features/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/karaf/features/src/main/feature/feature.xml b/karaf/features/src/main/feature/feature.xml
index c049f47..c017bac 100644
--- a/karaf/features/src/main/feature/feature.xml
+++ b/karaf/features/src/main/feature/feature.xml
@@ -164,7 +164,7 @@
 
         <feature>brooklyn-camp-base</feature>
         <feature>brooklyn-utils-rest-swagger</feature>
-        <feature>jetty</feature> <!-- TODO: pax-jetty??? -->
+        <feature>pax-jetty</feature>
     </feature>
 
     <feature name="brooklyn-rest-resources" version="${project.version}" description="Brooklyn
REST Resources">
@@ -188,6 +188,8 @@
     </feature>
 
     <feature name="brooklyn-jsgui" version="${project.version}" description="Brooklyn
REST JavaScript Web GUI">
+        <feature>pax-jetty</feature> <!-- jaas bundle -->
+        <bundle>mvn:org.apache.brooklyn/brooklyn-karaf-jetty-config/${project.version}</bundle>
         <bundle>mvn:org.apache.brooklyn/brooklyn-jsgui/${project.version}/war</bundle>
         <feature>war</feature>
     </feature>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java
----------------------------------------------------------------------
diff --git a/karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java
b/karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java
index e68cd52..a436ac4 100644
--- a/karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java
+++ b/karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java
@@ -20,11 +20,13 @@ package org.apache.brooklyn.security;
 
 import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
 import static org.ops4j.pax.exam.CoreOptions.streamBundle;
 
 import java.io.IOException;
 
 import javax.inject.Inject;
+import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.NameCallback;
@@ -38,6 +40,7 @@ import javax.security.auth.login.LoginException;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
 import org.apache.brooklyn.rest.BrooklynWebConfig;
+import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.test.IntegrationTest;
 import org.apache.karaf.features.BootFinished;
@@ -53,6 +56,8 @@ import org.ops4j.pax.exam.spi.reactors.PerClass;
 import org.ops4j.pax.tinybundles.core.TinyBundles;
 import org.osgi.framework.Constants;
 
+import com.google.common.collect.ImmutableSet;
+
 @RunWith(PaxExam.class)
 @ExamReactorStrategy(PerClass.class)
 @Category(IntegrationTest.class)
@@ -104,8 +109,13 @@ public class CustomSecurityProviderTest {
     @Test
     public void checkLoginSucceeds() throws LoginException {
         assertRealmRegisteredEventually(WEBCONSOLE_REALM);
-        LoginContext lc = doLogin("custom", "password");
-        assertNotNull(lc.getSubject());
+        String user = "custom";
+        LoginContext lc = doLogin(user, "password");
+        Subject subject = lc.getSubject();
+        assertNotNull(subject);
+        assertEquals(subject.getPrincipals(), ImmutableSet.of(
+                new BrooklynLoginModule.UserPrincipal(user),
+                new BrooklynLoginModule.RolePrincipal("users")));
     }
 
     private LoginContext doLogin(final String username, final String password) throws LoginException
{

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java
----------------------------------------------------------------------
diff --git a/karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java
b/karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java
index 0e4b41e..9be9e7d 100644
--- a/karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java
+++ b/karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java
@@ -137,7 +137,7 @@ public class StockSecurityProviderTest {
     private void assertResponseEquals(CloseableHttpClient httpclient, int code) throws IOException,
ClientProtocolException {
         // TODO get this dynamically (from CXF service?)
         // TODO port is static, should make it dynamic
-        HttpGet httpGet = new HttpGet("http://localhost:8181/v1/server/ha/state");
+        HttpGet httpGet = new HttpGet("http://localhost:8081/v1/server/ha/state");
         try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
             assertEquals(code, response.getStatusLine().getStatusCode());
         }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/itest/src/test/resources/custom-security-bp.xml
----------------------------------------------------------------------
diff --git a/karaf/itest/src/test/resources/custom-security-bp.xml b/karaf/itest/src/test/resources/custom-security-bp.xml
index c2b71dd..ace4454 100644
--- a/karaf/itest/src/test/resources/custom-security-bp.xml
+++ b/karaf/itest/src/test/resources/custom-security-bp.xml
@@ -34,6 +34,7 @@ limitations under the License.
                      flags="required">
             brooklyn.webconsole.security.provider.symbolicName=org.apache.brooklyn.test.security
             brooklyn.webconsole.security.provider.version=1.0.0
+            brooklyn.webconsole.security.provider.role=users
         </jaas:module>
     </jaas:config>
 </blueprint>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/jetty-config/pom.xml
----------------------------------------------------------------------
diff --git a/karaf/jetty-config/pom.xml b/karaf/jetty-config/pom.xml
new file mode 100644
index 0000000..1446693
--- /dev/null
+++ b/karaf/jetty-config/pom.xml
@@ -0,0 +1,57 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+<!--
+
+	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.
+
+-->
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.brooklyn</groupId>
+    <artifactId>brooklyn-karaf</artifactId>
+    <version>0.9.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>brooklyn-karaf-jetty-config</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Jetty config fragment</name>
+  <description>
+    An OSGi fragment to extend the jetty classpath (Import-Packaaes) so it is
+    able to load all classes in the included jetty.xml.
+  </description>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <!-- TODO re-use pluginManagement to brooklyn-parent -->
+        <version>2.5.4</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Fragment-Host>org.ops4j.pax.web.pax-web-jetty</Fragment-Host>
+            <Import-Package>
+                org.eclipse.jetty.jaas,
+                org.apache.brooklyn.rest.security.jaas,
+                *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/jetty-config/src/main/resources/jetty.xml
----------------------------------------------------------------------
diff --git a/karaf/jetty-config/src/main/resources/jetty.xml b/karaf/jetty-config/src/main/resources/jetty.xml
new file mode 100644
index 0000000..8957ea2
--- /dev/null
+++ b/karaf/jetty-config/src/main/resources/jetty.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+    <Call name="addBean">
+        <Arg>
+            <New class="org.eclipse.jetty.jaas.JAASLoginService">
+                <Set name="name">karaf</Set>
+                <Set name="loginModuleName">karaf</Set>
+                <Set name="roleClassNames">
+                    <Array type="java.lang.String">
+                        <Item>org.apache.karaf.jaas.boot.principal.RolePrincipal</Item>
+                    </Array>
+                </Set>
+            </New>
+        </Arg>
+    </Call>
+    <Call name="addBean">
+        <Arg>
+            <New class="org.eclipse.jetty.jaas.JAASLoginService">
+                <Set name="name">default</Set>
+                <Set name="loginModuleName">karaf</Set>
+                <Set name="roleClassNames">
+                    <Array type="java.lang.String">
+                        <Item>org.apache.karaf.jaas.boot.principal.RolePrincipal</Item>
+                    </Array>
+                </Set>
+            </New>
+        </Arg>
+    </Call>
+    <Call name="addBean">
+        <Arg>
+            <New class="org.eclipse.jetty.jaas.JAASLoginService">
+                <Set name="name">webconsole</Set>
+                <Set name="loginModuleName">webconsole</Set>
+                <Set name="roleClassNames">
+                    <Array type="java.lang.String">
+                        <Item>org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule$RolePrincipal</Item>
+                    </Array>
+                </Set>
+            </New>
+        </Arg>
+    </Call>
+
+</Configure>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/karaf/pom.xml
----------------------------------------------------------------------
diff --git a/karaf/pom.xml b/karaf/pom.xml
index f095135..dd7a51e 100644
--- a/karaf/pom.xml
+++ b/karaf/pom.xml
@@ -55,6 +55,7 @@
 
   <modules>
     <module>init</module>
+    <module>jetty-config</module>
     <module>features</module>
     <module>apache-brooklyn</module>
     <module>commands</module>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
----------------------------------------------------------------------
diff --git a/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java b/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
index 8b49ca9..2a87ce8 100644
--- a/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
+++ b/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
@@ -54,6 +54,9 @@ import org.apache.brooklyn.rest.filter.HaMasterCheckFilter;
 import org.apache.brooklyn.rest.filter.LoggingFilter;
 import org.apache.brooklyn.rest.filter.NoCacheFilter;
 import org.apache.brooklyn.rest.filter.RequestTaggingFilter;
+import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule;
+import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule.RolePrincipal;
+import org.apache.brooklyn.rest.security.jaas.JaasUtils;
 import org.apache.brooklyn.rest.util.ManagementContextProvider;
 import org.apache.brooklyn.rest.util.ShutdownHandler;
 import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
@@ -212,6 +215,7 @@ public class BrooklynWebServer {
             log.warn("Ignoring unknown flags " + leftovers);
         
         webappTempDir = BrooklynServerPaths.getBrooklynWebTmpDir(managementContext);
+        JaasUtils.init(managementContext);
     }
 
     public BrooklynWebServer(ManagementContext managementContext, int port) {
@@ -359,6 +363,14 @@ public class BrooklynWebServer {
         threadPool.setName("brooklyn-jetty-server-"+actualPort+"-"+threadPool.getName());
 
         server = new Server(threadPool);
+
+        // Can be moved to jetty-web.xml inside wars or a global jetty.xml.
+        JAASLoginService loginService = new JAASLoginService();
+        loginService.setName("webconsole");
+        loginService.setLoginModuleName("webconsole");
+        loginService.setRoleClassNames(new String[] {RolePrincipal.class.getName()});
+        server.addBean(loginService);
+
         final ServerConnector connector;
 
         if (getHttpsEnabled()) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 5e721a3..6034751 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -157,6 +157,11 @@
             </dependency>
             <dependency>
                 <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-jaas</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
                 <artifactId>jetty-webapp</artifactId>
                 <version>${jetty.version}</version>
             </dependency>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-resources/pom.xml
----------------------------------------------------------------------
diff --git a/rest/rest-resources/pom.xml b/rest/rest-resources/pom.xml
index 089ccd8..e28776c 100644
--- a/rest/rest-resources/pom.xml
+++ b/rest/rest-resources/pom.xml
@@ -109,6 +109,15 @@
             <artifactId>jackson-jaxrs-json-provider</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-jaas</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.brooklyn</groupId>
             <artifactId>brooklyn-test-support</artifactId>
             <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModule.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModule.java
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModule.java
index df99135..75ddd32 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModule.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModule.java
@@ -44,6 +44,9 @@ import org.apache.brooklyn.rest.BrooklynWebConfig;
 import org.apache.brooklyn.rest.security.provider.DelegatingSecurityProvider;
 import org.apache.brooklyn.rest.security.provider.SecurityProvider;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.text.Strings;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.Request;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
@@ -83,9 +86,17 @@ import org.slf4j.LoggerFactory;
 public class BrooklynLoginModule implements LoginModule {
     private static final Logger log = LoggerFactory.getLogger(BrooklynLoginModule.class);
 
-    private static class BrooklynPrincipal implements Principal {
+    /**
+     * The session attribute set for authenticated users; for reference
+     * (but should not be relied up to confirm authentication, as
+     * the providers may impose additional criteria such as timeouts,
+     * or a null user (no login) may be permitted)
+     */
+    public static final String AUTHENTICATED_USER_SESSION_ATTRIBUTE = "brooklyn.user";
+
+    private static class BasicPrincipal implements Principal {
         private String name;
-        public BrooklynPrincipal(String name) {
+        public BasicPrincipal(String name) {
             this.name = checkNotNull(name, "name");
         }
         @Override
@@ -98,19 +109,32 @@ public class BrooklynLoginModule implements LoginModule {
         }
         @Override
         public boolean equals(Object obj) {
-            if (obj instanceof BrooklynPrincipal) {
-                return name.equals(((BrooklynPrincipal)obj).name);
+            if (obj instanceof BasicPrincipal) {
+                return name.equals(((BasicPrincipal)obj).name);
             }
             return false;
         }
         @Override
         public String toString() {
-            return "BrooklynPrincipal[" +name + "]";
+            return getClass().getSimpleName() + "[" +name + "]";
+        }
+    }
+    public static class UserPrincipal extends BasicPrincipal {
+        public UserPrincipal(String name) {
+            super(name);
         }
     }
-    private static final Principal DEFAULT_PRINCIPAL = new BrooklynPrincipal("brooklyn");
+    public static class RolePrincipal extends BasicPrincipal {
+        public RolePrincipal(String name) {
+            super(name);
+        }
+    }
+
     public static final String PROPERTY_BUNDLE_SYMBOLIC_NAME = BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME.getName()
+ ".symbolicName";
     public static final String PROPERTY_BUNDLE_VERSION = BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME.getName()
+ ".version";
+    /** SecurityProvider doesn't know about roles, just attach one by default. Use the one
specified here or DEFAULT_ROLE */
+    public static final String PROPERTY_ROLE = BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME.getName()
+ ".role";
+    public static final String DEFAULT_ROLE = "webconsole";
 
     private Map<String, ?> options;
     private BundleContext bundleContext;
@@ -123,6 +147,7 @@ public class BrooklynLoginModule implements LoginModule {
     private CallbackHandler callbackHandler;
     private boolean loginSuccess;
     private boolean commitSuccess;
+    private Collection<Principal> principals;
 
     public BrooklynLoginModule() {
     }
@@ -246,11 +271,34 @@ public class BrooklynLoginModule implements LoginModule {
         String password = new String(cbPassword.getPassword());
 
         providerSession = new SecurityProviderHttpSession();
+
+        Request req = getJettyRequest();
+        if (req != null) {
+            String remoteAddr = req.getRemoteAddr();
+            providerSession.setAttribute(BrooklynWebConfig.REMOTE_ADDRESS_SESSION_ATTRIBUTE,
remoteAddr);
+        }
+
         if (!provider.authenticate(providerSession, user, password)) {
             loginSuccess = false;
             throw new FailedLoginException("Incorrect username or password");
         }
 
+        if (user != null) {
+            providerSession.setAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE, user);
+        }
+
+        principals = new ArrayList<>(2);
+        principals.add(new UserPrincipal(user));
+        // Could introduce a new interface SecurityRoleAware, implemented by
+        // the SecurityProviders, returning the roles a user has assigned.
+        // For now a static role is good enough.
+        String role = (String) options.get(PROPERTY_ROLE);
+        if (role == null) {
+            role = DEFAULT_ROLE;
+        }
+        if (Strings.isNonEmpty(role)) {
+            principals.add(new RolePrincipal(role));
+        }
         loginSuccess = true;
         return true;
     }
@@ -261,7 +309,7 @@ public class BrooklynLoginModule implements LoginModule {
             if (subject.isReadOnly()) {
                 throw new LoginException("Can't commit read-only subject");
             }
-            subject.getPrincipals().add(DEFAULT_PRINCIPAL);
+            subject.getPrincipals().addAll(principals);
         }
 
         commitSuccess = true;
@@ -273,16 +321,24 @@ public class BrooklynLoginModule implements LoginModule {
         if (loginSuccess && commitSuccess) {
             removePrincipal();
         }
+        clear();
         return loginSuccess;
     }
 
     @Override
     public boolean logout() throws LoginException {
-        removePrincipal();
-
-        subject = null;
-        callbackHandler = null;
+        Request req = getJettyRequest();
+        if (req != null) {
+            log.info("REST logging {} out",
+                    providerSession.getAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE));
+            provider.logout(req.getSession());
+            req.getSession().removeAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE);
+        } else {
+            log.error("Request object not available for logout");
+        }
 
+        removePrincipal();
+        clear();
         return true;
     }
 
@@ -290,7 +346,22 @@ public class BrooklynLoginModule implements LoginModule {
         if (subject.isReadOnly()) {
             throw new LoginException("Read-only subject");
         }
-        subject.getPrincipals().remove(DEFAULT_PRINCIPAL);
+        subject.getPrincipals().removeAll(principals);
+    }
+
+    private void clear() {
+        subject = null;
+        callbackHandler = null;
+        principals = null;
+    }
+
+    private Request getJettyRequest() {
+        HttpChannel<?> channel = HttpChannel.getCurrentHttpChannel();
+        if (channel != null) {
+             return channel.getRequest();
+        } else {
+            return null;
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/JaasUtils.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/JaasUtils.java
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/JaasUtils.java
new file mode 100644
index 0000000..94aba5d
--- /dev/null
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/JaasUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.brooklyn.rest.security.jaas;
+
+import java.net.URL;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JaasUtils {
+    private static final Logger log = LoggerFactory.getLogger(JaasUtils.class);
+
+    private static final String JAAS_CONFIG = "java.security.auth.login.config";
+
+    public static void init(ManagementContext mgmt) {
+        ManagementContextHolder.setManagementContextStatic(mgmt);
+        String config = System.getProperty(JAAS_CONFIG);
+        if (config == null) {
+            URL configUrl = JaasUtils.class.getResource("/jaas.conf");
+            if (configUrl != null) {
+                log.debug("Using classpath JAAS config from " + configUrl.toExternalForm());
+                System.setProperty(JAAS_CONFIG, configUrl.toExternalForm());
+            } else {
+                log.error("Can't find " + JAAS_CONFIG + " on classpath. Web server authentication
will fail.");
+            }
+        } else {
+            log.debug("Using externally configured JAAS at " + config);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/SecurityProviderHttpSession.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/SecurityProviderHttpSession.java
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/SecurityProviderHttpSession.java
index bf6812c..5224319 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/SecurityProviderHttpSession.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/SecurityProviderHttpSession.java
@@ -18,13 +18,20 @@
  */
 package org.apache.brooklyn.rest.security.jaas;
 
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpSessionContext;
 
+import org.apache.brooklyn.util.text.Identifiers;
+
 public class SecurityProviderHttpSession implements HttpSession {
+    String id = Identifiers.makeRandomId(5);
+    Map<String, Object> attributes = new ConcurrentHashMap<>();
 
     @Override
     public long getCreationTime() {
@@ -33,7 +40,7 @@ public class SecurityProviderHttpSession implements HttpSession {
 
     @Override
     public String getId() {
-        return null;
+        return id;
     }
 
     @Override
@@ -62,7 +69,7 @@ public class SecurityProviderHttpSession implements HttpSession {
 
     @Override
     public Object getAttribute(String name) {
-        return null;
+        return attributes.get(name);
     }
 
     @Override
@@ -72,7 +79,7 @@ public class SecurityProviderHttpSession implements HttpSession {
 
     @Override
     public Enumeration<String> getAttributeNames() {
-        return null;
+        return Collections.enumeration(attributes.keySet());
     }
 
     @Override
@@ -82,6 +89,7 @@ public class SecurityProviderHttpSession implements HttpSession {
 
     @Override
     public void setAttribute(String name, Object value) {
+        attributes.put(name, value);
     }
 
     @Override
@@ -90,6 +98,7 @@ public class SecurityProviderHttpSession implements HttpSession {
 
     @Override
     public void removeAttribute(String name) {
+        attributes.remove(name);
     }
 
     @Override
@@ -98,6 +107,8 @@ public class SecurityProviderHttpSession implements HttpSession {
 
     @Override
     public void invalidate() {
+        id = Identifiers.makeRandomId(5);
+        attributes.clear();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-resources/src/main/resources/jaas.conf
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/resources/jaas.conf b/rest/rest-resources/src/main/resources/jaas.conf
new file mode 100644
index 0000000..bb18334
--- /dev/null
+++ b/rest/rest-resources/src/main/resources/jaas.conf
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+webconsole {
+   org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule required;
+};

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModuleTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModuleTest.java
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModuleTest.java
index 39310aa..f33e807 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModuleTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModuleTest.java
@@ -23,7 +23,6 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
-import java.security.Principal;
 import java.util.Map;
 
 import javax.security.auth.Subject;
@@ -40,12 +39,13 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.ImmutableSet;
 
 // http://docs.oracle.com/javase/7/docs/technotes/guides/security/jaas/JAASLMDevGuide.html
 public class BrooklynLoginModuleTest extends BrooklynMgmtUnitTestSupport {
     private static final String ACCEPTED_USER = "user";
     private static final String ACCEPTED_PASSWORD = "password";
+    private static final String DEFAULT_ROLE = "webconsole";
     private CallbackHandler GOOD_CB_HANDLER = new TestCallbackHandler(
             ACCEPTED_USER,
             ACCEPTED_PASSWORD);
@@ -153,6 +153,15 @@ public class BrooklynLoginModuleTest extends BrooklynMgmtUnitTestSupport
{
         assertTrue(module.abort(), "abort");
         assertEmptyPrincipals();
     }
+    
+    @Test
+    public void testCustomRole() throws LoginException {
+        String role = "users";
+        options = ImmutableMap.<String, Object>of(BrooklynLoginModule.PROPERTY_ROLE,
role);
+        goodLogin();
+        assertTrue(module.commit(), "commit");
+        assertBrooklynPrincipal(role);
+    }
 
     private void goodLogin() throws LoginException {
         module.initialize(subject, GOOD_CB_HANDLER, sharedState, options);
@@ -171,8 +180,12 @@ public class BrooklynLoginModuleTest extends BrooklynMgmtUnitTestSupport
{
     }
 
     private void assertBrooklynPrincipal() {
-        Principal principal = Iterables.getOnlyElement(subject.getPrincipals());
-        assertEquals(principal.getName(), "brooklyn");
+        assertBrooklynPrincipal(DEFAULT_ROLE);
+    }
+    private void assertBrooklynPrincipal(String role) {
+        assertEquals(subject.getPrincipals(), ImmutableSet.of(
+                new BrooklynLoginModule.UserPrincipal(ACCEPTED_USER),
+                new BrooklynLoginModule.RolePrincipal(role)));
     }
 
     private void assertEmptyPrincipals() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-server-jersey/pom.xml
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/pom.xml b/rest/rest-server-jersey/pom.xml
index a102681..6a92dfb 100644
--- a/rest/rest-server-jersey/pom.xml
+++ b/rest/rest-server-jersey/pom.xml
@@ -162,6 +162,10 @@
         </dependency>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-jaas</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-server</artifactId>
         </dependency>
         <dependency>
@@ -267,6 +271,22 @@
                         </configuration>
                     </execution>
                     <execution>
+                        <id>copy-rest-resources</id>
+                        <phase>generate-resources</phase>
+                        <goals><goal>copy-resources</goal></goals>
+                        <configuration>
+                          <outputDirectory>target/generated-resources/rest-deps</outputDirectory>
+                          <resources>
+                            <resource>
+                              <directory>../rest-resources/src/main/resources</directory>
+                              <includes>
+                                <include>**/jaas.conf</include>
+                              </includes>
+                            </resource>
+                          </resources>
+                        </configuration>
+                    </execution>
+                    <execution>
                         <id>copy-rest-test-resources</id>
                         <phase>generate-test-resources</phase>
                         <goals><goal>copy-resources</goal></goals>
@@ -300,6 +320,18 @@
                     </execution>
                     <execution>
                         <id>rest-resources</id>
+                        <phase>generate-resources</phase>
+                        <goals><goal>add-resource</goal></goals>
+                        <configuration>
+                          <resources>
+                            <resource>
+                                <directory>target/generated-resources/rest-deps</directory>
+                            </resource>
+                          </resources>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>rest-test-resources</id>
                         <phase>generate-test-resources</phase>
                         <goals><goal>add-test-resource</goal></goals>
                         <configuration>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-server/pom.xml
----------------------------------------------------------------------
diff --git a/rest/rest-server/pom.xml b/rest/rest-server/pom.xml
index 94d4895..481b65c 100644
--- a/rest/rest-server/pom.xml
+++ b/rest/rest-server/pom.xml
@@ -113,6 +113,10 @@
             <artifactId>jetty-servlet</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-jaas</artifactId>
+        </dependency>
+        <dependency>
             <groupId>javax.ws.rs</groupId>
             <artifactId>javax.ws.rs-api</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1de6a8a5/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
index d9f0f1a..1dc9bd5 100644
--- a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -245,7 +245,6 @@ public class BrooklynRestApiLauncher {
     /** NB: not fully supported; use one of the other {@link StartMode}s */
     private ContextHandler webXmlContextHandler(ManagementContext mgmt) {
         RestApiSetup.initSwagger();
-        // TODO add security to web.xml
         WebAppContext context;
         if (findMatchingFile("src/main/webapp")!=null) {
             // running in source mode; need to use special classpath
@@ -290,7 +289,9 @@ public class BrooklynRestApiLauncher {
     @Deprecated
     public static Server startServer(ContextHandler context, String summary, InetSocketAddress
bindLocation) {
         Server server = new Server(bindLocation);
-        
+
+        initJaas(server);
+
         server.setHandler(context);
         try {
             server.start();
@@ -303,6 +304,15 @@ public class BrooklynRestApiLauncher {
         return server;
     }
 
+    // TODO Why parallel code for server init here and in BrooklynWebServer?
+    private static void initJaas(Server server) {
+        JAASLoginService loginService = new JAASLoginService();
+        loginService.setName("webconsole");
+        loginService.setLoginModuleName("webconsole");
+        loginService.setRoleClassNames(new String[] {RolePrincipal.class.getName()});
+        server.addBean(loginService);
+    }
+
     public static BrooklynRestApiLauncher launcher() {
         return new BrooklynRestApiLauncher();
     }


Mime
View raw message