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

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 2b0cd8d35f1a7a9139a131e8a1532661f2001092
Author: Alex Heneveld <[email protected]>
AuthorDate: Fri Jan 4 17:48:41 2019 +0000

    clean up removing jaas stuff to support oauth
    
    still needs configuration and testing, but getting there
---
 .../filter/BrooklynSecurityProviderFilter.java     | 178 +++++----
 .../brooklyn/rest/filter/GoogleOauthFilter.java    | 266 -------------
 .../brooklyn/rest/resources/LogoutResource.java    |   8 -
 .../rest/security/jaas/BrooklynLoginModule.java    | 423 ---------------------
 .../rest/security/jaas/GoogleOauthLoginModule.java | 391 -------------------
 .../brooklyn/rest/security/jaas/JaasUtils.java     |  48 ---
 .../security/jaas/ManagementContextHolder.java     |  36 --
 .../security/jaas/SecurityProviderHttpSession.java | 120 ------
 .../provider/DelegatingSecurityProvider.java       |  53 ++-
 ...ityProvider.java => OauthSecurityProvider.java} |  46 ++-
 .../rest/security/provider/SecurityProvider.java   |   6 +
 .../main/resources/OSGI-INF/blueprint/service.xml  |   4 -
 .../rest/filter/EntitlementContextFilterTest.java  |  12 +-
 .../security/jaas/BrooklynLoginModuleTest.java     | 195 ----------
 .../rest/security/jaas/TestCallbackHandler.java    |  50 ---
 .../brooklyn/rest/testing/BrooklynRestApiTest.java |   4 +-
 .../rest/testing/BrooklynRestResourceTest.java     |  16 +-
 17 files changed, 218 insertions(+), 1638 deletions(-)

diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/filter/BrooklynSecurityProviderFilter.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/filter/BrooklynSecurityProviderFilter.java
index 264a575..4f960f6 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/filter/BrooklynSecurityProviderFilter.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/filter/BrooklynSecurityProviderFilter.java
@@ -19,23 +19,28 @@
 package org.apache.brooklyn.rest.filter;
 
 import java.io.IOException;
+import java.security.Principal;
+import java.util.function.Function;
 
 import javax.annotation.Priority;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.container.ContainerRequestFilter;
-import javax.ws.rs.container.ContainerResponseContext;
-import javax.ws.rs.container.ContainerResponseFilter;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.ext.ContextResolver;
 import javax.ws.rs.ext.Provider;
 
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.rest.BrooklynWebConfig;
-import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule;
+import org.apache.brooklyn.rest.security.provider.DelegatingSecurityProvider;
 import org.apache.brooklyn.rest.security.provider.SecurityProvider;
+import 
org.apache.brooklyn.rest.security.provider.SecurityProvider.PostAuthenticator;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.auth.BasicUserPrincipal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,13 +53,22 @@ import org.slf4j.LoggerFactory;
  */
 @Provider
 @Priority(100)
-public class BrooklynSecurityProviderFilter implements ContainerRequestFilter, 
ContainerResponseFilter {
+public class BrooklynSecurityProviderFilter implements ContainerRequestFilter {
+
+    /**
+     * 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 final Logger log = 
LoggerFactory.getLogger(BrooklynSecurityProviderFilter.class);
+
     
     @Context
     HttpServletRequest webRequest;
     
-    HttpServletResponse webResponse;
-    
     @Context
     private ContextResolver<ManagementContext> mgmtC;
     
@@ -62,6 +76,40 @@ public class BrooklynSecurityProviderFilter implements 
ContainerRequestFilter, C
         return mgmtC.getContext(ManagementContext.class);
     }
     
+    public static class SimpleSecurityContext implements SecurityContext {
+        final Principal principal;
+        final Function<String,Boolean> roleChecker;
+        final boolean secure;
+        final String authScheme;
+        
+        public SimpleSecurityContext(String username, Function<String, 
Boolean> roleChecker, boolean secure, String authScheme) {
+            this.principal = new BasicUserPrincipal(username);
+            this.roleChecker = roleChecker;
+            this.secure = secure;
+            this.authScheme = authScheme;
+        }
+
+        @Override
+        public Principal getUserPrincipal() {
+            return principal;
+        }
+
+        @Override
+        public boolean isUserInRole(String role) {
+            return roleChecker.apply(role);
+        }
+
+        @Override
+        public boolean isSecure() {
+            return secure;
+        }
+
+        @Override
+        public String getAuthenticationScheme() {
+            return authScheme;
+        }
+    }
+    
     @Override
     public void filter(ContainerRequestContext requestContext) throws 
IOException {
         SecurityProvider provider = getProvider();
@@ -71,41 +119,61 @@ public class BrooklynSecurityProviderFilter implements 
ContainerRequestFilter, C
             return;
         }
         
-        String user=null, pass=null;
-        webResponse.setHeader("WWW-Authenticate", "Basic realm=\"brooklyn\"");
-        webResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
-        throw new RuntimeException("Authentication required.");
-//        if (provider.requiresUserPass()) {
-//            // TODO get basic auth
-//            
-//        }
-//        
-//        if (provider.authenticate(session, user, pass)) {
-//            
-//        } else {
-//            throw new RuntimeException("Authentication failed.");
-//        }
-    }
+        String user = null, pass = null;
+        if (provider.requiresUserPass()) {
+            String authorization = webRequest.getHeader("Authorization");
+            if (authorization != null) {
+                String userpass = new 
String(Base64.decodeBase64(authorization.substring(6)));
+                int idxColon = userpass.indexOf(":");
+                if (idxColon >= 0) {
+                    user = userpass.substring(0, idxColon);
+                    pass = userpass.substring(idxColon + 1);
+                } else {
+                    abort(requestContext, "Invalid authorization string");
+                    return;
+                }
+            } else {
+                abort(requestContext, "Authorization required");
+                return;
+            }
+        }
+        
+        if (session==null) {
+            // only create the session if an auth string is supplied
+            session = webRequest.getSession(true);
+        }
+        
session.setAttribute(BrooklynWebConfig.REMOTE_ADDRESS_SESSION_ATTRIBUTE, 
webRequest.getRemoteAddr());
+        
+        if (provider.authenticate(session, user, pass)) {
+            if (provider instanceof PostAuthenticator) {
+                ((PostAuthenticator)provider).postAuthenticate(requestContext);
+            }
+            if (user != null) {
+                session.setAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE, 
user);
+                if 
(requestContext.getSecurityContext().getUserPrincipal()==null) {
+                    requestContext.setSecurityContext(new 
SimpleSecurityContext(user, (role) -> false, 
+                        webRequest.isSecure(), SecurityContext.BASIC_AUTH));
+                }
+            }
+            return;
+        }
     
-    @Override
-    public void filter(ContainerRequestContext requestContext, 
ContainerResponseContext responseContext) throws IOException {
-        // nothing needs done on the response
+        abort(requestContext, "Authentication failed");
+        return;
     }
     
-    protected SecurityProvider getProvider() {
-        // TODO
-        return null;
+    private void abort(ContainerRequestContext requestContext, String message) 
{
+        requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
+            .type(MediaType.TEXT_PLAIN)
+            .entity(message)
+            .header("WWW-Authenticate", "Basic realm=\"brooklyn\"")
+            .build());
     }
 
-    /**
-     * 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 = 
BrooklynLoginModule.AUTHENTICATED_USER_SESSION_ATTRIBUTE;
-
-    private static final Logger log = 
LoggerFactory.getLogger(BrooklynSecurityProviderFilter.class);
+    protected SecurityProvider getProvider() {
+        // we don't cache here (could, it might be faster) but the delegate 
does use a cache
+        return new DelegatingSecurityProvider(mgmt());
+    }
 
 //    private static ThreadLocal<String> originalRequest = new 
ThreadLocal<String>();
 //
@@ -173,42 +241,6 @@ public class BrooklynSecurityProviderFilter implements 
ContainerRequestFilter, C
 //    }
 //
 //    protected boolean authenticate(HttpServletRequest request) {
-//        HttpSession session = request.getSession();
-//        if (provider.isAuthenticated(session)) {
-//            return true;
-//        }
-//        
session.setAttribute(BrooklynWebConfig.REMOTE_ADDRESS_SESSION_ATTRIBUTE, 
request.getRemoteAddr());
-//        String user = null, pass = null;
-//        String authorization = request.getHeader("Authorization");
-//        if (authorization != null) {
-//            String userpass = new 
String(Base64.decodeBase64(authorization.substring(6)));
-//            int idxColon = userpass.indexOf(":");
-//            if (idxColon >= 0) {
-//                user = userpass.substring(0, idxColon);
-//                pass = userpass.substring(idxColon + 1);
-//            } else {
-//                return false;
-//            }
-//        }
-//        if (provider.authenticate(session, user, pass)) {
-//            if (user != null) {
-//                session.setAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE, 
user);
-//            }
-//            return true;
-//        }
-//
-//        return false;
-//    }
-//
-//    @Override
-//    public void init(FilterConfig config) throws ServletException {
-//        ManagementContext mgmt = 
OsgiCompat.getManagementContext(config.getServletContext());
-//        provider = new DelegatingSecurityProvider(mgmt);
-//    }
-//
-//    @Override
-//    public void destroy() {
-//    }
 //
 //    protected void logout(HttpServletRequest request) {
 //        log.info("REST logging {} out of session {}",
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/filter/GoogleOauthFilter.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/filter/GoogleOauthFilter.java
deleted file mode 100644
index ab15f1a..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/filter/GoogleOauthFilter.java
+++ /dev/null
@@ -1,266 +0,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.
- */
-package org.apache.brooklyn.rest.filter;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Priority;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.container.ContainerRequestContext;
-import javax.ws.rs.container.ContainerRequestFilter;
-import javax.ws.rs.container.ContainerResponseContext;
-import javax.ws.rs.container.ContainerResponseFilter;
-import javax.ws.rs.ext.Provider;
-
-import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.yaml.Yamls;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.entity.ContentType;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Provider
-@Priority(1)
-public class GoogleOauthFilter implements ContainerRequestFilter {
-
-    private static final Logger log = 
LoggerFactory.getLogger(GoogleOauthFilter.class);
-    
-    public static final String SESSION_KEY_CODE = "code";
-
-    public static final String SESSION_KEY_ACCESS_TOKEN = "access_token";
-
-    public static final String PARAM_URI_TOKEN_INFO = "uriTokenInfo";
-    public static final String PARAM_URI_GETTOKEN = "uriGetToken";
-    public static final String PARAM_URI_LOGIN_REDIRECT = "uriLoginRedirect";
-    public static final String PARAM_CLIENT_ID = "clientId";
-    public static final String PARAM_CLIENT_SECRET = "clientSecret";
-    public static final String PARAM_CALLBACK_URI = "callbackUri";
-    public static final String PARAM_AUDIENCE = "audience";
-
-    private String uriGetToken = "https://accounts.google.com/o/oauth2/token";;
-    private String uriTokenInfo = 
"https://www.googleapis.com/oauth2/v1/tokeninfo";;
-    private String uriTokenRedirect = "/";
-    private String clientId = 
"789182012565-burd24h3bc0im74g2qemi7lnihvfqd02.apps.googleusercontent.com";
-    private String clientSecret = "X00v-LfU34U4SfsHqPKMWfQl";
-    private String callbackUri = "http://localhost.io:8081/";;
-    private String audience = "audience";
-
-//    @Override
-//    public void init(FilterConfig filterConfig) throws ServletException {
-//        log.info("GOOGLE FILTER 1");
-//        initializateParams(filterConfig);
-//    }
-//
-//    private void initializateParams(FilterConfig filterConfig) {
-//        Enumeration<String> enums = filterConfig.getInitParameterNames();
-//
-//        while (enums.hasMoreElements()) {
-//            String paramKey = enums.nextElement();
-//            String paramValue = filterConfig.getInitParameter(paramKey);
-//            System.out.println(paramKey + ":" + paramValue);
-//            switch (paramKey) {
-//            case PARAM_URI_TOKEN_INFO:
-//                uriTokenInfo = paramValue;
-//                break;
-//            case PARAM_URI_GETTOKEN:
-//                uriGetToken = paramValue;
-//                break;
-//            case PARAM_URI_LOGIN_REDIRECT:
-//                uriTokenRedirect = paramValue;
-//                break;
-//            case PARAM_CLIENT_ID:
-//                clientId = paramValue;
-//                break;
-//            case PARAM_CLIENT_SECRET:
-//                clientSecret = paramValue;
-//                break;
-//            case PARAM_CALLBACK_URI:
-//                callbackUri = paramValue;
-//                break;
-//            case PARAM_AUDIENCE:
-//                audience = paramValue;
-//                break;
-//            default:
-//                System.out.println("Ignored param: " + paramKey + ":" + 
paramValue);
-//            }
-//        }
-//    }
-//
-//    @Override
-//    public void doFilter(ServletRequest req, ServletResponse resp, 
FilterChain chain)
-//            throws IOException, ServletException {
-//        log.info("GOOGLE FILTER 2");
-//        HttpServletRequest request = (HttpServletRequest) req;
-//        // Redirection from the authenticator server
-//        String code = req.getParameter(SESSION_KEY_CODE);
-//
-//        // Getting token, if exists, from the current session
-//        String token = (String) 
request.getSession().getAttribute(SESSION_KEY_ACCESS_TOKEN);
-//
-//        boolean continueFilterProcessing;
-//        if (code != null && !"".equals(code)) { // in brooklyn, have
-//                                                // Strings.isNonBlank(code)
-//            continueFilterProcessing = getToken(req, resp, chain);
-//        } else if (token == null || "".equals(token)) { // isBlank
-//            continueFilterProcessing = redirectLogin(resp);
-//        } else {
-//            continueFilterProcessing = validateToken(token, resp);
-//        }
-//        if (continueFilterProcessing) {
-//            chain.doFilter(req, resp);
-//        }
-//    }
-//
-//    private boolean validateToken(String token, ServletResponse resp) throws 
ClientProtocolException, IOException {
-//        log.info("GOOGLE FILTER 3");
-//        // System.out.println("########################### Validating token
-//        // ###########################");
-//        HashMap<String, String> params = new HashMap<String, String>();
-//        params.put(SESSION_KEY_ACCESS_TOKEN, token);
-//
-//        String body = post(uriTokenInfo, params);
-//        // System.out.println(body);
-//        Map<?,?> jsonObject = null;
-//
-//        // get the access token from json and request info from Google
-//        try {
-//            jsonObject = (Map<?,?>) Yamls.parseAll(body).iterator().next();
-//            log.info("Parsed '"+body+"' as "+jsonObject);
-//        } catch (Exception e) {
-//            Exceptions.propagateIfFatal(e);
-//            log.info("Unable to parse: '"+body+"'");
-//            throw new RuntimeException("Unable to parse json " + body, e);
-//        }
-//
-//        if (!clientId.equals(jsonObject.get(audience))) {
-//            return redirectLogin(resp);
-//        }
-//        // if (isTokenExpiredOrNearlySo(...) { ... }
-//        return true;
-//    }
-//
-//    private boolean getToken(ServletRequest req, ServletResponse resp, 
FilterChain chain)
-//            throws ClientProtocolException, IOException, ServletException {
-//        String code = req.getParameter(SESSION_KEY_CODE);
-//
-//        // get the access token by post to Google
-//        HashMap<String, String> params = new HashMap<String, String>();
-//        params.put(SESSION_KEY_CODE, code);
-//        params.put("client_id", clientId);
-//        params.put("client_secret", clientSecret);
-//        params.put("redirect_uri", callbackUri);
-//        params.put("grant_type", "authorization_code");
-//
-//        String body = post(uriGetToken, params);
-//
-//        Map<?,?> jsonObject = null;
-//
-//        // get the access token from json and request info from Google
-//        try {
-//            jsonObject = (Map<?,?>) Yamls.parseAll(body).iterator().next();
-//            log.info("Parsed '"+body+"' as "+jsonObject);
-//        } catch (Exception e) {
-//            Exceptions.propagateIfFatal(e);
-//            log.info("Unable to parse: '"+body+"'");
-//            return redirectLogin(resp);
-//        }
-//
-//        // Left token and code in session
-//        String accessToken = (String) 
jsonObject.get(SESSION_KEY_ACCESS_TOKEN);
-//        HttpServletRequest request = (HttpServletRequest) req;
-//        request.getSession().setAttribute(SESSION_KEY_ACCESS_TOKEN, 
accessToken);
-//        request.getSession().setAttribute(SESSION_KEY_CODE, code);
-//
-//        // resp.getWriter().println(json);
-//        return true;
-//    }
-//
-//    // makes a POST request to url with form parameters and returns body as a
-//    // string
-//    public String post(String url, Map<String, String> formParameters) 
throws ClientProtocolException, IOException {
-//        HttpPost request = new HttpPost(url);
-//
-//        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
-//        for (String key : formParameters.keySet()) {
-//            nvps.add(new BasicNameValuePair(key, formParameters.get(key)));
-//        }
-//        request.setEntity(new UrlEncodedFormEntity(nvps));
-//
-//        return execute(request);
-//    }
-//
-//    // makes a GET request to url and returns body as a string
-//    public String get(String url) throws ClientProtocolException, 
IOException {
-//        return execute(new HttpGet(url));
-//    }
-//
-//    // makes request and checks response code for 200
-//    private String execute(HttpRequestBase request) throws 
ClientProtocolException, IOException {
-//        HttpClient httpClient = new DefaultHttpClient();
-//        HttpResponse response = httpClient.execute(request);
-//
-//        HttpEntity entity = response.getEntity();
-//        String body = EntityUtils.toString(entity);
-//
-//        if (response.getStatusLine().getStatusCode() != 200) {
-//            throw new RuntimeException(
-//                    "Expected 200 but got " + 
response.getStatusLine().getStatusCode() + ", with body " + body);
-//        }
-//
-//        return body;
-//    }
-//
-//    private boolean redirectLogin(ServletResponse response) throws 
IOException {
-//        HttpServletResponse res = (HttpServletResponse) response;
-//        res.setContentType(ContentType.APPLICATION_XML.toString());
-//        res.sendRedirect(uriTokenRedirect);
-//        return false;
-//    }
-
-    @Override
-    public void filter(ContainerRequestContext requestContext) throws 
IOException {
-        log.info("GOOGLE FILTER 2");
-        // TODO Auto-generated method stub
-        
-    }
-
-}
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/LogoutResource.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/LogoutResource.java
index e3329d2..5ce65ba 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/LogoutResource.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/LogoutResource.java
@@ -30,15 +30,10 @@ import javax.ws.rs.core.UriInfo;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.core.mgmt.entitlement.WebEntitlementContext;
 import org.apache.brooklyn.rest.api.LogoutApi;
-import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 
-import com.google.common.net.HttpHeaders;
-
 public class LogoutResource extends AbstractBrooklynRestResource implements 
LogoutApi {
     
-    private static final String BASIC_REALM_WEBCONSOLE = "Basic 
realm=\""+BrooklynLoginModule.DEFAULT_ROLE+"\"";
-    
     @Context HttpServletRequest req;
     @Context UriInfo uri;
 
@@ -49,7 +44,6 @@ public class LogoutResource extends 
AbstractBrooklynRestResource implements Logo
         if (ctx==null) {
             return Response.status(Status.BAD_REQUEST)
                 .entity("No user logged in")
-                .header(HttpHeaders.WWW_AUTHENTICATE, BASIC_REALM_WEBCONSOLE)
                 .build();            
         }
         
@@ -64,7 +58,6 @@ public class LogoutResource extends 
AbstractBrooklynRestResource implements Logo
     @Override
     public Response unAuthorize() {
         return Response.status(Status.UNAUTHORIZED)
-            .header(HttpHeaders.WWW_AUTHENTICATE, BASIC_REALM_WEBCONSOLE)
             .build();
     }
 
@@ -77,7 +70,6 @@ public class LogoutResource extends 
AbstractBrooklynRestResource implements Logo
             doLogout();
 
             return Response.status(Status.UNAUTHORIZED)
-                    .header(HttpHeaders.WWW_AUTHENTICATE, 
BASIC_REALM_WEBCONSOLE)
                     .build();
         } else {
             return 
Response.temporaryRedirect(uri.getAbsolutePathBuilder().replacePath("/").build()).build();
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
deleted file mode 100644
index 9374fbe..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModule.java
+++ /dev/null
@@ -1,423 +0,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.
- */
-package org.apache.brooklyn.rest.security.jaas;
-
-import org.apache.brooklyn.api.mgmt.ManagementContext;
-import org.apache.brooklyn.config.StringConfigMap;
-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.HttpConnection;
-import org.eclipse.jetty.server.Request;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.FailedLoginException;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-import javax.servlet.http.HttpSession;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Optional;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-// 
http://docs.oracle.com/javase/7/docs/technotes/guides/security/jaas/JAASLMDevGuide.html
-
-/**
- * <p>
- * JAAS module delegating authentication to the {@link SecurityProvider} 
implementation
- * configured in {@literal brooklyn.properties}, key {@literal 
brooklyn.webconsole.security.provider}.
- * <p>
- * We have also supported configuring this as options in OSGi;
- * this is now deprecated, but see {@link 
#initProviderFromOptions(StringConfigMap)} for more info.
- */
-// Needs an explicit "org.apache.karaf.jaas.config" Import-Package in the 
manifest!
-public class BrooklynLoginModule implements LoginModule {
-    private static final Logger log = 
LoggerFactory.getLogger(BrooklynLoginModule.class);
-
-    /**
-     * 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 BasicPrincipal(String name) {
-            this.name = checkNotNull(name, "name");
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-
-        @Override
-        public int hashCode() {
-            return name.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj instanceof BasicPrincipal) {
-                return name.equals(((BasicPrincipal) obj).name);
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            return getClass().getSimpleName() + "[" + name + "]";
-        }
-    }
-
-    public static class UserPrincipal extends BasicPrincipal {
-        public UserPrincipal(String name) {
-            super(name);
-        }
-    }
-
-    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;
-
-    private HttpSession providerSession;
-
-    private SecurityProvider provider;
-    private Subject subject;
-    private CallbackHandler callbackHandler;
-    private boolean loginSuccess;
-    private boolean commitSuccess;
-    private Collection<Principal> principals;
-
-    public BrooklynLoginModule() {
-    }
-
-    private synchronized static SecurityProvider 
createDefaultSecurityProvider(ManagementContext mgmt) {
-        return new DelegatingSecurityProvider(mgmt);
-    }
-
-    private ManagementContext getManagementContext() {
-        return ManagementContextHolder.getManagementContext();
-    }
-
-    @Override
-    public void initialize(Subject subject, CallbackHandler callbackHandler, 
Map<String, ?> sharedState, Map<String, ?> options) {
-        try {
-            this.subject = subject;
-            this.callbackHandler = callbackHandler;
-            this.options = options;
-    
-            this.bundleContext = (BundleContext) 
options.get(BundleContext.class.getName());
-    
-            loginSuccess = false;
-            commitSuccess = false;
-    
-            initProvider();
-            
-        } catch (Exception e) {
-            log.warn("Unable to initialize BrooklynLoginModule: "+e, e);
-            Exceptions.propagateIfFatal(e);
-        }
-    }
-
-    private void initProvider() {
-        // use existing (shared) provider if there is one, for speed
-        // (note this login module class gets a different instance on each 
request; caching the provider is a big efficiency gain) 
-        StringConfigMap brooklynProperties = 
getManagementContext().getConfig();
-        provider = 
brooklynProperties.getConfig(BrooklynWebConfig.SECURITY_PROVIDER_INSTANCE);
-        if (provider != null) return;
-        provider = 
getManagementContext().getScratchpad().get(BrooklynWebConfig.SECURITY_PROVIDER_INSTANCE);
-        if (provider != null) return;
-
-        initProviderFromOptions(brooklynProperties);
-        if (provider==null) {
-            // no osgi options set, so use the standard properties-based one 
(usual path)
-            provider = createDefaultSecurityProvider(getManagementContext());
-        }
-
-        log.debug("Using security provider " + provider);
-    }
-
-    /**
-     * We have since switching to OSGi also allowed the provider to be 
specified as an option in the
-     * OSGi blueprint. This has never been used AFAIK in the real world but it 
was the only way to specify a bundle. 
-     * Note that only implementations visible from {@literal 
brooklyn-rest-server} are usable by default.
-     * We now support specifying a bundle for the delegate, but this is being 
left in as deprecated.
-     * To use this <b>deprecated</b> configuration with a custom security 
provider, 
-     * add the following configuration to the its bundle in {@literal 
src/main/resources/OSGI-INF/bundle/security-provider.xml}:
-     * <p>
-     * <pre>
-     * {@code
-     * <?xml version="1.0" encoding="UTF-8"?>
-     * <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0";
-     *           xmlns:jaas="http://karaf.apache.org/xmlns/jaas/v1.1.0";
-     *           
xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0";>
-     *
-     *    <jaas:config name="karaf" rank="1">
-     *        <jaas:module 
className="org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule" 
flags="required">
-     *            
brooklyn.webconsole.security.provider.symbolicName=BUNDLE_SYMBOLIC_NAME
-     *            brooklyn.webconsole.security.provider.version=BUNDLE_VERSION
-     *        </jaas:module>
-     *    </jaas:config>
-     *
-     * </blueprint>
-     * }
-     * </pre>
-     * @deprecated since 2019-01, use the brooklyn system properties 
-     * {@link BrooklynWebConfig#SECURITY_PROVIDER_CLASSNAME},
-     * {@link BrooklynWebConfig#SECURITY_PROVIDER_BUNDLE}, and
-     * {@link BrooklynWebConfig#SECURITY_PROVIDER_BUNDLE_VERSION},
-     */
-    protected void initProviderFromOptions(StringConfigMap brooklynProperties) 
{
-        // this uses *options* to determine the security provider to load
-        // (not sure this is ever used)
-        String symbolicName = (String) 
options.get(PROPERTY_BUNDLE_SYMBOLIC_NAME);
-        String version = (String) options.get(PROPERTY_BUNDLE_VERSION);
-        String className = (String) 
options.get(BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME.getName());
-        if (className != null && symbolicName == null) {
-            throw new IllegalStateException("Missing JAAS module property " + 
PROPERTY_BUNDLE_SYMBOLIC_NAME + " pointing at the bundle where to load the 
security provider from.");
-        }
-        if (symbolicName != null) {
-            if (className == null) {
-                className = 
brooklynProperties.getConfig(BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME);
-            }
-            if (className != null) {
-                provider = loadProviderFromBundle(getManagementContext(), 
bundleContext, symbolicName, version, className);
-            }
-        }
-    }
-
-    public static SecurityProvider loadProviderFromBundle(
-            ManagementContext mgmt, BundleContext bundleContext,
-            String symbolicName, String version, String className) {
-        try {
-            Collection<Bundle> bundles = getMatchingBundles(bundleContext, 
symbolicName, version);
-            if (bundles.isEmpty()) {
-                throw new IllegalStateException("No bundle " + symbolicName + 
":" + version + " found");
-            } else if (bundles.size() > 1) {
-                log.warn("Found multiple bundles matching symbolicName " + 
symbolicName + " and version " + version +
-                        " while trying to load security provider " + className 
+ ". Will use first one that loads the class successfully.");
-            }
-            SecurityProvider p = tryLoadClass(mgmt, className, bundles);
-            if (p == null) {
-                throw new ClassNotFoundException("Unable to load class " + 
className + " from bundle " + symbolicName + ":" + version);
-            }
-            return p;
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            throw new IllegalStateException("Can not load or create security 
provider " + className + " for bundle " + symbolicName + ":" + version, e);
-        }
-    }
-
-    private static SecurityProvider tryLoadClass(ManagementContext mgmt, 
String className, Collection<Bundle> bundles)
-            throws NoSuchMethodException, InstantiationException, 
IllegalAccessException, InvocationTargetException {
-        for (Bundle b : bundles) {
-            try {
-                @SuppressWarnings("unchecked")
-                Class<? extends SecurityProvider> securityProviderType = 
(Class<? extends SecurityProvider>) b.loadClass(className);
-                return 
DelegatingSecurityProvider.createSecurityProviderInstance(mgmt, 
securityProviderType);
-            } catch (ClassNotFoundException e) {
-            }
-        }
-        return null;
-    }
-
-    private static Collection<Bundle> getMatchingBundles(BundleContext 
bundleContext, final String symbolicName, final String version) {
-        Collection<Bundle> bundles = new ArrayList<>();
-        for (Bundle b : bundleContext.getBundles()) {
-            if (b.getSymbolicName().equals(symbolicName) &&
-                    (version == null || 
b.getVersion().toString().equals(version))) {
-                bundles.add(b);
-            }
-        }
-        return bundles;
-    }
-
-    @Override
-    public boolean login() throws LoginException {
-        try {
-            log.info("ALEX BLM login - "+callbackHandler+" "+this+" 
"+provider);
-            String user=null, password=null;
-            
-            if (provider.requiresUserPass()) {
-                if (callbackHandler == null) {
-                    loginSuccess = false;
-                    throw new FailedLoginException("Username and password not 
available");
-                }
-        
-                NameCallback cbName = new NameCallback("Username: ");
-                PasswordCallback cbPassword = new PasswordCallback("Password: 
", false);
-        
-                Callback[] callbacks = {cbName, cbPassword};
-        
-                try {
-                    callbackHandler.handle(callbacks);
-                } catch (IOException ioe) {
-                    throw new LoginException(ioe.getMessage());
-                } catch (UnsupportedCallbackException uce) {
-                    throw new LoginException(uce.getMessage() + " not 
available to obtain information from user");
-                }
-                user = cbName.getName();
-                password = new String(cbPassword.getPassword());
-            }
-    
-    
-            Request req = getJettyRequest();
-            if (req != null) {
-                providerSession = req.getSession(false);
-            }
-            log.info("GOT SESSION - "+providerSession);
-            if (providerSession == null) {
-                providerSession = new SecurityProviderHttpSession();
-            }
-            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;
-        } catch (LoginException e) {
-            throw e;
-        } catch (Exception e) {
-            log.warn("Unexpected error during login: "+e, e);
-            throw e;
-        }
-    }
-
-    @Override
-    public boolean commit() throws LoginException {
-        log.info("ALEX BLM BR LOGIN - COMMIT");
-        if (loginSuccess && principals!=null && !principals.isEmpty()) {
-            // for oauth principals aren't set currently; they don't seem to 
be needed
-            
-            if (subject.isReadOnly()) {
-                throw new LoginException("Can't commit read-only subject");
-            }
-            subject.getPrincipals().addAll(principals);
-        }
-
-        commitSuccess = true;
-        return loginSuccess;
-    }
-
-    @Override
-    public boolean abort() throws LoginException {
-        log.info("ALEX BLM abort");
-        if (loginSuccess && commitSuccess) {
-            removePrincipal();
-        }
-        clear();
-        return loginSuccess;
-    }
-
-    @Override
-    public boolean logout() throws LoginException {
-        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;
-    }
-
-    private void removePrincipal() throws LoginException {
-        if (principals==null || principals.isEmpty()) return;
-        if (subject.isReadOnly()) {
-            throw new LoginException("Read-only subject");
-        }
-        subject.getPrincipals().removeAll(principals);
-    }
-
-    private void clear() {
-        subject = null;
-        callbackHandler = null;
-        principals = null;
-    }
-
-    private Request getJettyRequest() {
-        return Optional.ofNullable(HttpConnection.getCurrentConnection())
-                .map(HttpConnection::getHttpChannel)
-                .map(HttpChannel::getRequest)
-                .orElse(null);
-    }
-
-}
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/GoogleOauthLoginModule.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/GoogleOauthLoginModule.java
deleted file mode 100644
index bd1493f..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/GoogleOauthLoginModule.java
+++ /dev/null
@@ -1,391 +0,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.
- */
-package org.apache.brooklyn.rest.security.jaas;
-
-import java.io.IOException;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Random;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-import javax.servlet.ServletException;
-
-import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.text.Strings;
-import org.apache.brooklyn.util.yaml.Yamls;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.HttpConnection;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Response;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-public class GoogleOauthLoginModule implements LoginModule {
-    private static final Logger logger = 
LoggerFactory.getLogger(BrooklynLoginModule.class);
-    private static final String SESSION_KEY_ACCESS_TOKEN = "access_token";
-    private static final String SESSION_KEY_CODE = "code";
-    private static final String TOKEN = "fake_token";
-//    public static final String PARAM_URI_TOKEN_INFO = "uriTokenInfo";
-//    public static final String PARAM_URI_GETTOKEN = "uriGetToken";
-//    public static final String PARAM_URI_LOGIN_REDIRECT = "uriLoginRedirect";
-//    public static final String PARAM_CLIENT_ID = "clientId";
-//    public static final String PARAM_CLIENT_SECRET = "clientSecret";
-//    public static final String PARAM_CALLBACK_URI = "callbackUri";
-//    public static final String PARAM_AUDIENCE = "audience";
-
-    private String uriGetToken = "https://accounts.google.com/o/oauth2/token";;
-    private String uriAuthorize = "https://accounts.google.com/o/oauth2/auth";;
-    private String uriTokenInfo = 
"https://www.googleapis.com/oauth2/v1/tokeninfo";;
-    
-    // or github:
-//    private String uriGetToken = "https://github.com/login/oauth/authorize";;
-//    private String uriAuthorize = "https://github.com/login/oauth/authorize";;
-//    private String uriTokenInfo = 
"https://github.com/login/oauth/access_token";;
-    
-//    private String apiURLBase = "https://api.github.com/";;
-
-    private String uriTokenRedirect = "/";
-    // google
-    private String clientId = 
"789182012565-burd24h3bc0im74g2qemi7lnihvfqd02.apps.googleusercontent.com";
-    private String clientSecret = "X00v-LfU34U4SfsHqPKMWfQl";
-    // github
-//    private String clientId = "7f76b9970d8ac15b30b0";
-//    private String clientSecret = "9e15f8dd651f0b1896a3a582f17fa82f049fc910";
-    private String callbackUri = "http://localhost.io:8081/";;
-    private String audience = "audience";
-
-//    private static final String OAUTH2_TOKEN = 
"org.apache.activemq.jaas.oauth2.token";
-//    private static final String OAUTH2_ROLE = 
"org.apache.activemq.jaas.oauth2.role";
-//    private static final String OAUTH2_URL = 
"org.apache.activemq.jaas.oauth2.oauth2url";
-    private Set<Principal> principals = new HashSet<>();
-    private Subject subject;
-    private CallbackHandler callbackHandler;
-    private boolean debug;
-    private String roleName = "webconsole";
-    private String oauth2URL = uriTokenInfo;
-    private boolean loginSucceeded;
-    private String userName;
-    private boolean commitSuccess;
-
-    private final Request request=getJettyRequest();
-    private final Response response=getJettyResponse();
-
-    @Override
-    public void initialize(Subject subject, CallbackHandler callbackHandler, 
Map<String, ?> sharedState, Map<String, ?> options) {
-        logger.info("ALEX LOGIN MODULE - INIT");
-        this.subject = subject;
-        this.callbackHandler = callbackHandler;
-
-        loginSucceeded = false;
-        commitSuccess = false;
-
-        debug = !"false".equalsIgnoreCase((String) options.get("debug"));
-
-        if (debug) {
-            logger.debug(">>>>>>>>>>>>>>>>Initialized debug=" + debug + " 
guestGroup=" + roleName + " url="
-                    + oauth2URL);
-        }
-
-    }
-
-    @Override
-    public boolean login() throws LoginException {
-        loginSucceeded = true;
-
-        Callback[] callbacks = new Callback[1];
-        callbacks[0] = new NameCallback("User name");
-
-        try {
-            callbackHandler.handle(callbacks);
-        } catch (IOException | UnsupportedCallbackException e) {
-            throw (LoginException) new LoginException().initCause(e);
-        }
-
-        userName = ((NameCallback) callbacks[0]).getName();
-
-        if (null == userName) {
-            loginSucceeded = false;
-        }
-
-        String newUrl = oauth2URL + userName;
-        logger.info("ALEX LOGIN MODULE - LOGIN "+userName+" / "+newUrl);
-        logger.debug("THis is the URL: " + newUrl);
-
-        boolean eligible=false;
-
-        // Redirection from the authenticator server
-        String code = request.getParameter(SESSION_KEY_CODE);
-
-        // Getting token, if exists, from the current session
-        String token = (String) 
request.getSession().getAttribute(SESSION_KEY_ACCESS_TOKEN);
-//        token=TOKEN;
-        try {
-            if (Strings.isNonBlank(code)) {
-                eligible = getToken();
-            } else if (Strings.isEmpty(token)) {
-                    eligible = redirectLogin();
-            } else {
-                eligible = validateToken(token);
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        } catch (ServletException e) {
-            e.printStackTrace();
-        }
-
-        if (eligible) {
-            principals.add(new BrooklynLoginModule.UserPrincipal(userName));
-            principals.add(new BrooklynLoginModule.RolePrincipal(roleName));
-        } else {
-            loginSucceeded = false;
-        }
-
-        if (debug) {
-            logger.debug("Token login " + loginSucceeded);
-        }
-        return loginSucceeded;
-    }
-
-    @Override
-    public boolean commit() throws LoginException {
-        if (loginSucceeded) {
-            if (subject.isReadOnly()) {
-                throw new LoginException("Can't commit read-only subject");
-            }
-            subject.getPrincipals().addAll(principals);
-        }
-
-        commitSuccess = true;
-        return loginSucceeded;
-    }
-
-    @Override
-    public boolean abort() throws LoginException {
-        if (loginSucceeded && commitSuccess) {
-            removePrincipal();
-        }
-        clear();
-        return loginSucceeded;
-    }
-
-    @Override
-    public boolean logout() throws LoginException {
-        if (request != null) {
-//            logger.info("REST logging {} 
out",providerSession.getAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE));
-//            provider.logout(req.getSession());
-//            
req.getSession().removeAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE);
-        } else {
-            logger.error("Request object not available for logout");
-        }
-
-        removePrincipal();
-        clear();
-        return true;
-    }
-    private boolean getToken()
-            throws ClientProtocolException, IOException, ServletException {
-        String code = request.getParameter(SESSION_KEY_CODE);
-
-        // get the access token by post to Google
-        HashMap<String, String> params = new HashMap<String, String>();
-        params.put(SESSION_KEY_CODE, code);
-        params.put("client_id", clientId);
-        params.put("client_secret", clientSecret);
-        params.put("redirect_uri", callbackUri);
-        params.put("grant_type", "authorization_code");
-
-        String body = post(uriGetToken, params);
-
-        Map<?,?> jsonObject = null;
-
-        // get the access token from json and request info from Google
-        try {
-            jsonObject = (Map<?,?>) Yamls.parseAll(body).iterator().next();
-            logger.info("Parsed '"+body+"' as "+jsonObject);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            logger.info("Unable to parse: '"+body+"'");
-            // throw new RuntimeException("Unable to parse json " + body);
-            return redirectLogin();
-        }
-
-        // Left token and code in session
-        String accessToken = (String) jsonObject.get(SESSION_KEY_ACCESS_TOKEN);
-        request.getSession().setAttribute(SESSION_KEY_ACCESS_TOKEN, 
accessToken);
-        request.getSession().setAttribute(SESSION_KEY_CODE, code);
-
-        // resp.getWriter().println(json);
-        return true;
-    }
-    private boolean validateToken(String token) throws 
ClientProtocolException, IOException {
-        // System.out.println("########################### Validating token
-        // ###########################");
-
-        //for debug
-        if(token.equals(TOKEN)){
-            return true;
-        }
-        HashMap<String, String> params = new HashMap<String, String>();
-        params.put(SESSION_KEY_ACCESS_TOKEN, token);
-
-        String body = post(uriTokenInfo, params);
-        // System.out.println(body);
-        Map<?,?> jsonObject = null;
-
-        // get the access token from json and request info from Google
-        try {
-            jsonObject = (Map<?,?>) Yamls.parseAll(body).iterator().next();
-            logger.info("Parsed '"+body+"' as "+jsonObject);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            logger.info("Unable to parse: '"+body+"'");
-            throw new RuntimeException("Unable to parse json " + body, e);
-        }
-
-        if (!clientId.equals(jsonObject.get(audience))) {
-            return redirectLogin();
-        }
-        // if (isTokenExpiredOrNearlySo(...) { ... }
-        return true;
-    }
-
-    // makes a GET request to url and returns body as a string
-    public String get(String url) throws ClientProtocolException, IOException {
-        return execute(new HttpGet(url));
-    }
-    // makes a POST request to url with form parameters and returns body as a
-    // string
-    public String post(String url, Map<String, String> formParameters) throws 
ClientProtocolException, IOException {
-        HttpPost request = new HttpPost(url);
-
-        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
-        for (String key : formParameters.keySet()) {
-            nvps.add(new BasicNameValuePair(key, formParameters.get(key)));
-        }
-        request.setEntity(new UrlEncodedFormEntity(nvps));
-
-        return execute(request);
-    }
-    // makes request and checks response code for 200
-    private String execute(HttpRequestBase request) throws 
ClientProtocolException, IOException {
-        HttpClient httpClient = new DefaultHttpClient();
-        HttpResponse response = httpClient.execute(request);
-
-        HttpEntity entity = response.getEntity();
-        String body = EntityUtils.toString(entity);
-
-        if (response.getStatusLine().getStatusCode() != 200) {
-            throw new RuntimeException(
-                    "Expected 200 but got " + 
response.getStatusLine().getStatusCode() + ", with body " + body);
-        }
-
-        return body;
-    }
-
-    private void removePrincipal() throws LoginException {
-        if (subject.isReadOnly()) {
-            throw new LoginException("Read-only subject");
-        }
-        subject.getPrincipals().removeAll(principals);
-    }
-
-    private void clear() {
-        subject = null;
-        callbackHandler = null;
-        principals = null;
-    }
-
-    private static String createRandomHexString(int length){
-        Random random = new Random();
-        StringBuilder sb = new StringBuilder();
-        while (sb.length() < length) {
-            sb.append(Integer.toHexString(random.nextInt()));
-        }
-        return sb.toString();
-    }
-
-    private boolean redirectLogin() throws IOException {
-        String state=createRandomHexString(16); //should be stored in session
-        StringBuilder oauthUrl = new StringBuilder().append(uriAuthorize)
-                .append("?response_type=").append("code")
-                .append("&client_id=").append(clientId) // the client id from 
the api console registration
-                .append("&redirect_uri=").append(callbackUri) // the servlet 
that github redirects to after
-                // authorization
-                .append("&scope=").append("user public_repo")
-//                .append("&scope=openid%20email") // scope is the api 
permissions we
-                .append("&state=").append(state)
-                .append("&access_type=offline") // here we are asking to 
access to user's data while they are not
-                // signed in
-                .append("&approval_prompt=force"); // this requires them to 
verify which account to use, if they are
-        // already signed in
-
-        // just for look inside
-//        Collection<String> originalHeaders = response.getHeaderNames();
-
-        response.reset();
-//        response.addHeader("Origin", "http://localhost.io:8081";);
-//        response.addHeader("Access-Control-Allow-Origin", "*");
-////        response.addHeader("Access-Control-Request-Method", "GET, POST");
-////        response.addHeader("Access-Control-Request-Headers", "origin, 
x-requested-with");
-        logger.debug(oauthUrl.toString());
-        response.sendRedirect(oauthUrl.toString());
-
-        return false;
-
-    }
-
-    private Request getJettyRequest() {
-        return Optional.ofNullable(HttpConnection.getCurrentConnection())
-                .map(HttpConnection::getHttpChannel)
-                .map(HttpChannel::getRequest)
-                .orElse(null);
-    }
-
-    private Response getJettyResponse() {
-        return Optional.ofNullable(HttpConnection.getCurrentConnection())
-                .map(HttpConnection::getHttpChannel)
-                .map(HttpChannel::getResponse)
-                .orElse(null);
-    }
-}
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
deleted file mode 100644
index 94aba5d..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/JaasUtils.java
+++ /dev/null
@@ -1,48 +0,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.
- */
-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);
-        }
-    }
-
-}
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/ManagementContextHolder.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/ManagementContextHolder.java
deleted file mode 100644
index 84704f1..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/ManagementContextHolder.java
+++ /dev/null
@@ -1,36 +0,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.
- */
-package org.apache.brooklyn.rest.security.jaas;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import org.apache.brooklyn.api.mgmt.ManagementContext;
-
-public class ManagementContextHolder {
-    private static ManagementContext mgmt;
-    public static ManagementContext getManagementContext() {
-        return checkNotNull(mgmt, "Management context not set yet");
-    }
-    public void setManagementContext(ManagementContext mgmt) {
-        setManagementContextStatic(mgmt);
-    }
-    public static void setManagementContextStatic(ManagementContext mgmt) {
-        ManagementContextHolder.mgmt = checkNotNull(mgmt, "mgmt");
-    }
-}
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
deleted file mode 100644
index 98f1e6d..0000000
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/jaas/SecurityProviderHttpSession.java
+++ /dev/null
@@ -1,120 +0,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.
- */
-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 org.apache.brooklyn.util.text.Identifiers;
-
-/** mock session, used only for performing authentication */
-public class SecurityProviderHttpSession implements HttpSession {
-    String id = Identifiers.makeRandomId(5);
-    Map<String, Object> attributes = new ConcurrentHashMap<>();
-
-    @Override
-    public long getCreationTime() {
-        return 0;
-    }
-
-    @Override
-    public String getId() {
-        return id;
-    }
-
-    @Override
-    public long getLastAccessedTime() {
-        return 0;
-    }
-
-    @Override
-    public ServletContext getServletContext() {
-        return null;
-    }
-
-    @Override
-    public void setMaxInactiveInterval(int interval) {
-    }
-
-    @Override
-    public int getMaxInactiveInterval() {
-        return 0;
-    }
-
-    @Override
-    @Deprecated //in interface
-    public javax.servlet.http.HttpSessionContext getSessionContext() {
-        return null;
-    }
-
-    @Override
-    public Object getAttribute(String name) {
-        return attributes.get(name);
-    }
-
-    @Override
-    public Object getValue(String name) {
-        return null;
-    }
-
-    @Override
-    public Enumeration<String> getAttributeNames() {
-        return Collections.enumeration(attributes.keySet());
-    }
-
-    @Override
-    public String[] getValueNames() {
-        return null;
-    }
-
-    @Override
-    public void setAttribute(String name, Object value) {
-        attributes.put(name, value);
-    }
-
-    @Override
-    public void putValue(String name, Object value) {
-    }
-
-    @Override
-    public void removeAttribute(String name) {
-        attributes.remove(name);
-    }
-
-    @Override
-    public void removeValue(String name) {
-    }
-
-    @Override
-    public void invalidate() {
-        id = Identifiers.makeRandomId(5);
-        attributes.clear();
-    }
-
-    @Override
-    public boolean isNew() {
-        return false;
-    }
-
-}
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/DelegatingSecurityProvider.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/DelegatingSecurityProvider.java
index d5bb50b..664c5c1 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/DelegatingSecurityProvider.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/DelegatingSecurityProvider.java
@@ -20,6 +20,8 @@ package org.apache.brooklyn.rest.security.provider;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.concurrent.atomic.AtomicLong;
 
 import javax.servlet.http.HttpSession;
@@ -29,8 +31,9 @@ import org.apache.brooklyn.config.StringConfigMap;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.rest.BrooklynWebConfig;
-import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule;
 import org.apache.brooklyn.util.core.ClassLoaderUtils;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -92,7 +95,7 @@ public class DelegatingSecurityProvider implements 
SecurityProvider {
                 String bundleVersion = 
brooklynProperties.getConfig(BrooklynWebConfig.SECURITY_PROVIDER_BUNDLE_VERSION);
                 log.info("REST using security provider " + className + " from 
" + bundle+":"+bundleVersion);
                 BundleContext bundleContext = 
((ManagementContextInternal)mgmt).getOsgiManager().get().getFramework().getBundleContext();
-                delegate = BrooklynLoginModule.loadProviderFromBundle(mgmt, 
bundleContext, bundle, bundleVersion, className);
+                delegate = loadProviderFromBundle(mgmt, bundleContext, bundle, 
bundleVersion, className);
             } else {
                 log.info("REST using security provider " + className);
                 ClassLoaderUtils clu = new ClassLoaderUtils(this, mgmt);
@@ -111,6 +114,52 @@ public class DelegatingSecurityProvider implements 
SecurityProvider {
         return delegate;
     }
 
+    public static SecurityProvider loadProviderFromBundle(
+        ManagementContext mgmt, BundleContext bundleContext,
+        String symbolicName, String version, String className) {
+        try {
+            Collection<Bundle> bundles = getMatchingBundles(bundleContext, 
symbolicName, version);
+            if (bundles.isEmpty()) {
+                throw new IllegalStateException("No bundle " + symbolicName + 
":" + version + " found");
+            } else if (bundles.size() > 1) {
+                log.warn("Found multiple bundles matching symbolicName " + 
symbolicName + " and version " + version +
+                    " while trying to load security provider " + className + 
". Will use first one that loads the class successfully.");
+            }
+            SecurityProvider p = tryLoadClass(mgmt, className, bundles);
+            if (p == null) {
+                throw new ClassNotFoundException("Unable to load class " + 
className + " from bundle " + symbolicName + ":" + version);
+            }
+            return p;
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            throw new IllegalStateException("Can not load or create security 
provider " + className + " for bundle " + symbolicName + ":" + version, e);
+        }
+    }
+
+    private static SecurityProvider tryLoadClass(ManagementContext mgmt, 
String className, Collection<Bundle> bundles)
+        throws NoSuchMethodException, InstantiationException, 
IllegalAccessException, InvocationTargetException {
+        for (Bundle b : bundles) {
+            try {
+                @SuppressWarnings("unchecked")
+                Class<? extends SecurityProvider> securityProviderType = 
(Class<? extends SecurityProvider>) b.loadClass(className);
+                return 
DelegatingSecurityProvider.createSecurityProviderInstance(mgmt, 
securityProviderType);
+            } catch (ClassNotFoundException e) {
+            }
+        }
+        return null;
+    }
+
+    private static Collection<Bundle> getMatchingBundles(BundleContext 
bundleContext, final String symbolicName, final String version) {
+        Collection<Bundle> bundles = new ArrayList<>();
+        for (Bundle b : bundleContext.getBundles()) {
+            if (b.getSymbolicName().equals(symbolicName) &&
+                (version == null || 
b.getVersion().toString().equals(version))) {
+                bundles.add(b);
+            }
+        }
+        return bundles;
+    }
+
     public static SecurityProvider 
createSecurityProviderInstance(ManagementContext mgmt,
             Class<? extends SecurityProvider> clazz) throws 
NoSuchMethodException, InstantiationException,
                     IllegalAccessException, InvocationTargetException {
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/GoogleOauthSecurityProvider.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/OauthSecurityProvider.java
similarity index 79%
rename from 
rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/GoogleOauthSecurityProvider.java
rename to 
rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/OauthSecurityProvider.java
index 89e4844..b3975f1 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/GoogleOauthSecurityProvider.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/OauthSecurityProvider.java
@@ -27,9 +27,9 @@ import java.util.Optional;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpSession;
+import javax.ws.rs.container.ContainerRequestContext;
 
-import org.apache.brooklyn.rest.filter.GoogleOauthFilter;
-import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule;
+import 
org.apache.brooklyn.rest.filter.BrooklynSecurityProviderFilter.SimpleSecurityContext;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.text.Identifiers;
 import org.apache.brooklyn.util.text.Strings;
@@ -53,12 +53,15 @@ import org.eclipse.jetty.server.Response;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/** provider who allows everyone */
-public class GoogleOauthSecurityProvider implements SecurityProvider {
+/** Configurable OAuth redirect security provider
+ * 
+ *  Redirects all inbound requests to an oath web server unless a session 
token is specified. */
+public class OauthSecurityProvider implements SecurityProvider, 
SecurityProvider.PostAuthenticator {
 
-    public static final Logger LOG = 
LoggerFactory.getLogger(GoogleOauthSecurityProvider.class);
+    public static final Logger LOG = 
LoggerFactory.getLogger(OauthSecurityProvider.class);
 
-    private static final Logger logger = 
LoggerFactory.getLogger(BrooklynLoginModule.class);
+    // TODO replace with LOG
+    private static final Logger logger = 
LoggerFactory.getLogger(OauthSecurityProvider.class);
     private static final String SESSION_KEY_ACCESS_TOKEN = "access_token";
     private static final String SESSION_KEY_CODE = "code";
     private static final String FAKE_TOKEN_FOR_DEBUG = "fake_token";
@@ -70,6 +73,10 @@ public class GoogleOauthSecurityProvider implements 
SecurityProvider {
 //    public static final String PARAM_CALLBACK_URI = "callbackUri";
 //    public static final String PARAM_AUDIENCE = "audience";
 
+    // tempting to use getJettyRequest().getRequestURL().toString();
+    // but some oauth providers require this to be declared
+    private String callbackUri = "http://localhost.io:8081/";;
+    
     private String uriGetToken = "https://accounts.google.com/o/oauth2/token";;
     private String uriAuthorize = "https://accounts.google.com/o/oauth2/auth";;
     private String uriTokenInfo = 
"https://www.googleapis.com/oauth2/v1/tokeninfo";;
@@ -109,7 +116,7 @@ public class GoogleOauthSecurityProvider implements 
SecurityProvider {
     @Override
     public boolean isAuthenticated(HttpSession session) {
         LOG.info("isAuthenticated 1 "+session+" ... "+this);
-        Object token = 
session.getAttribute(GoogleOauthFilter.SESSION_KEY_ACCESS_TOKEN);
+        Object token = session.getAttribute(SESSION_KEY_ACCESS_TOKEN);
         // TODO is it valid?
         return token!=null;
     }
@@ -145,7 +152,7 @@ public class GoogleOauthSecurityProvider implements 
SecurityProvider {
     @Override
     public boolean logout(HttpSession session) {
         LOG.info("logout");
-        session.removeAttribute(GoogleOauthFilter.SESSION_KEY_ACCESS_TOKEN);
+        session.removeAttribute(SESSION_KEY_ACCESS_TOKEN);
         return true;
     }
     
@@ -157,7 +164,6 @@ public class GoogleOauthSecurityProvider implements 
SecurityProvider {
     private boolean getToken() throws ClientProtocolException, IOException, 
ServletException {
         Request request = getJettyRequest();
         String code = request.getParameter(SESSION_KEY_CODE);
-        String callbackUri = request.getRequestURL().toString();
 
         // get the access token by post to Google
         HashMap<String, String> params = new HashMap<String, String>();
@@ -189,10 +195,29 @@ public class GoogleOauthSecurityProvider implements 
SecurityProvider {
 
         // TODO is it valid?
         LOG.debug("Got token/code "+accessToken+"/"+code+" from "+jsonObject);
-        // resp.getWriter().println(json);
+        // eg Got token/code 
+        // 
ya29.GluHBtzZ-R-CaoWMlso6KB6cq3DrbmwX6B3kjMmzWqzU-vO76WjKuNS3Ktog7vt9CJnxSZ63NmqO4p5bg20wl0-M14yO1LuoXNV5JX3qHDmXl2rl-z1LbCPEYJ-o
+        //    /  
4/yADFJRSRCxLgZFcpD_KU2jQiCXBGNHTsw0eGZqZ2t6IJJh2O1oWBnBDx4eWl4ZLCRAFJx3QjPYtl7LF9zj_DNlA
 
+        // from {
+        //   
access_token=ya29.GluHBtzZ-R-CaoWMlso6KB6cq3DrbmwX6B3kjMmzWqzU-vO76WjKuNS3Ktog7vt9CJnxSZ63NmqO4p5bg20wl0-M14yO1LuoXNV5JX3qHDmXl2rl-z1LbCPEYJ-o,
 
+        //   expires_in=3600, 
+        //   refresh_token=1/b2Xk2rCVqKFsbz_xePv1tctvihnLoyo0YHsw4YQWK8M, 
+        //   scope=https://www.googleapis.com/auth/userinfo.email 
https://www.googleapis.com/auth/plus.me, 
+        //   token_type=Bearer, 
+        //   
id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc5NzhhOTEzNDcyNjFhMjkxYmQ3MWRjYWI0YTQ2NGJlN2QyNzk2NjYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXpwIjoiNzg5MTgyMDEyNTY1LWJ1cmQyNGgzYmMwaW03NGcycWVtaTdsbmlodmZxZDAyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiNzg5MTgyMDEyNTY1LWJ1cmQyNGgzYmMwaW03NGcycWVtaTdsbmlodmZxZDAyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTA2MDQyNTE3MjU2MTcxNzYyMTU0IiwiaGQiOiJjbG91ZHNvZnRjb3JwLmNvbSIsImVtYWlsIjoiYWxleC5oZW5ldmVsZEBjbG91ZHN
 [...]
+        // TODO how do we get the user ID back?
         return true;
     }
     
+    @Override
+    public void postAuthenticate(ContainerRequestContext requestContext) {
+        Request request = getJettyRequest();
+        String token = (String) 
request.getSession().getAttribute(SESSION_KEY_ACCESS_TOKEN);
+        LOG.info("TOKEN post authed = "+token);
+        String user = token; // TODO not right - see above
+        requestContext.setSecurityContext(new SimpleSecurityContext(user, 
(role) -> false, request.isSecure(), "brooklyn-oauth"));
+    }
+
     private boolean validateToken(String token) throws 
ClientProtocolException, IOException {
         // TODO for debug
         if(token.equals(FAKE_TOKEN_FOR_DEBUG)){
@@ -263,7 +288,6 @@ public class GoogleOauthSecurityProvider implements 
SecurityProvider {
 
     private boolean redirectLogin() throws IOException {
         String state=Identifiers.makeRandomId(12); //should be stored in 
session
-        String callbackUri = getJettyRequest().getRequestURL().toString();
         StringBuilder oauthUrl = new StringBuilder().append(uriAuthorize)
                 .append("?response_type=").append("code")
                 .append("&client_id=").append(clientId) // the client id from 
the api console registration
diff --git 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/SecurityProvider.java
 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/SecurityProvider.java
index 42575ff..1a16219 100644
--- 
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/SecurityProvider.java
+++ 
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/SecurityProvider.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.rest.security.provider;
 
 import javax.servlet.http.HttpSession;
+import javax.ws.rs.container.ContainerRequestContext;
 
 /**
  * The SecurityProvider is responsible for doing authentication.
@@ -37,4 +38,9 @@ public interface SecurityProvider {
     public boolean authenticate(HttpSession session, String user, String pass);
     public boolean logout(HttpSession session);
     
+    public interface PostAuthenticator {
+        /** Invoked by framework after successful authentication for 
principals to be updated. 
+         * (That needs to happen against the container which is not otherwise 
accessible.) */
+        public void postAuthenticate(ContainerRequestContext requestContext);
+    }
 }
diff --git 
a/rest/rest-resources/src/main/resources/OSGI-INF/blueprint/service.xml 
b/rest/rest-resources/src/main/resources/OSGI-INF/blueprint/service.xml
index dc40b44..f357222 100644
--- a/rest/rest-resources/src/main/resources/OSGI-INF/blueprint/service.xml
+++ b/rest/rest-resources/src/main/resources/OSGI-INF/blueprint/service.xml
@@ -61,10 +61,6 @@ limitations under the License.
 
     <reference id="shutdownHandler" 
interface="org.apache.brooklyn.core.mgmt.ShutdownHandler"/>
 
-    <bean 
class="org.apache.brooklyn.rest.security.jaas.ManagementContextHolder">
-        <property name="managementContext" ref="localManagementContext"/>
-    </bean>
-
     <bean id="accessResourceBean" 
class="org.apache.brooklyn.rest.resources.AccessResource"/>
     <bean id="activityResourceBean" 
class="org.apache.brooklyn.rest.resources.ActivityResource"/>
     <bean id="adjunctResourceBean" 
class="org.apache.brooklyn.rest.resources.AdjunctResource"/>
diff --git 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/filter/EntitlementContextFilterTest.java
 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/filter/EntitlementContextFilterTest.java
index 0d37cc8..3c7748b 100644
--- 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/filter/EntitlementContextFilterTest.java
+++ 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/filter/EntitlementContextFilterTest.java
@@ -29,13 +29,12 @@ import org.apache.brooklyn.core.internal.BrooklynProperties;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.core.mgmt.entitlement.WebEntitlementContext;
 import org.apache.brooklyn.rest.BrooklynWebConfig;
-import org.apache.brooklyn.rest.security.jaas.JaasUtils;
 import 
org.apache.brooklyn.rest.security.provider.ExplicitUsersSecurityProvider;
 import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
-import org.apache.cxf.interceptor.security.JAASLoginInterceptor;
 import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.http.HttpStatus;
+import org.eclipse.jetty.server.session.HashSessionManager;
 import org.testng.annotations.Test;
 
 public class EntitlementContextFilterTest extends BrooklynRestResourceTest {
@@ -58,18 +57,13 @@ public class EntitlementContextFilterTest extends 
BrooklynRestResourceTest {
         props.put(BrooklynWebConfig.PASSWORD_FOR_USER(USER_PASS), USER_PASS);
         
getManagementContext().getScratchpad().put(BrooklynWebConfig.SECURITY_PROVIDER_INSTANCE,
 new ExplicitUsersSecurityProvider(getManagementContext()));
 
+        sf.setProvider(new HashSessionManager());
         super.configureCXF(sf);
-
-        JaasUtils.init(getManagementContext());
-
-        JAASLoginInterceptor jaas = new JAASLoginInterceptor();
-        jaas.setContextName("webconsole");
-        sf.getInInterceptors().add(jaas);
-
     }
 
     @Override
     protected void addBrooklynResources() {
+        addResource(new BrooklynSecurityProviderFilter());
         addResource(new RequestTaggingRsFilter());
         addResource(new EntitlementContextFilter());
         addResource(new EntitlementResource());
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
deleted file mode 100644
index f33e807..0000000
--- 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/BrooklynLoginModuleTest.java
+++ /dev/null
@@ -1,195 +0,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.
- */
-package org.apache.brooklyn.rest.security.jaas;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.util.Map;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.FailedLoginException;
-import javax.security.auth.login.LoginException;
-
-import org.apache.brooklyn.core.internal.BrooklynProperties;
-import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport;
-import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.rest.BrooklynWebConfig;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableMap;
-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);
-    private CallbackHandler BAD_CB_HANDLER = new TestCallbackHandler(
-            ACCEPTED_USER + ".invalid",
-            ACCEPTED_PASSWORD + ".invalid");
-
-    private Subject subject;
-    private Map<String, ?> sharedState;
-    private Map<String, ?> options;
-
-    private BrooklynLoginModule module;
-
-    @Override
-    @BeforeMethod(alwaysRun = true)
-    public void setUp() throws Exception {
-        BrooklynProperties properties = BrooklynProperties.Factory.newEmpty();
-        properties.addFrom(ImmutableMap.of(
-                BrooklynWebConfig.USERS, ACCEPTED_USER,
-                BrooklynWebConfig.PASSWORD_FOR_USER("user"), 
ACCEPTED_PASSWORD));
-        mgmt = 
LocalManagementContextForTests.builder(true).useProperties(properties).build();
-        ManagementContextHolder.setManagementContextStatic(mgmt);
-
-        super.setUp();
-
-        subject = new Subject();
-        sharedState = MutableMap.of();
-        options = ImmutableMap.of();
-
-        module = new BrooklynLoginModule();
-    }
-
-    @Test
-    public void testMissingCallback() throws LoginException {
-        module.initialize(subject, null, sharedState, options);
-        try {
-            module.login();
-            fail("Login is supposed to fail due to missing callback");
-        } catch (FailedLoginException e) {
-            // Expected, ignore
-        }
-        assertFalse(module.commit(), "commit");
-        assertEmptyPrincipals();
-        assertFalse(module.abort(), "abort");
-    }
-
-    @Test
-    public void testFailedLoginCommitAbort() throws LoginException {
-        badLogin();
-        assertFalse(module.commit(), "commit");
-        assertEmptyPrincipals();
-        assertFalse(module.abort(), "abort");
-    }
-
-    @Test
-    public void testFailedLoginCommitAbortReadOnly() throws LoginException {
-        subject.setReadOnly();
-        badLogin();
-        assertFalse(module.commit(), "commit");
-        assertEmptyPrincipals();
-        assertFalse(module.abort(), "abort");
-    }
-
-    @Test
-    public void testFailedLoginAbort() throws LoginException {
-        badLogin();
-        assertFalse(module.abort(), "abort");
-        assertEmptyPrincipals();
-    }
-
-    @Test
-    public void testSuccessfulLoginCommitLogout() throws LoginException {
-        goodLogin();
-        assertTrue(module.commit(), "commit");
-        assertBrooklynPrincipal();
-        assertTrue(module.logout(), "logout");
-        assertEmptyPrincipals();
-    }
-
-    @Test
-    public void testSuccessfulLoginCommitAbort() throws LoginException {
-        goodLogin();
-        assertTrue(module.commit(), "commit");
-        assertBrooklynPrincipal();
-        assertTrue(module.abort(), "logout");
-        assertEmptyPrincipals();
-    }
-
-    @Test
-    public void testSuccessfulLoginCommitAbortReadOnly() throws LoginException 
{
-        subject.setReadOnly();
-        goodLogin();
-        try {
-            module.commit();
-            fail("Commit expected to throw");
-        } catch (LoginException e) {
-            // Expected
-        }
-        assertTrue(module.abort());
-    }
-
-    @Test
-    public void testSuccessfulLoginAbort() throws LoginException {
-        goodLogin();
-        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);
-        assertTrue(module.login(), "login");
-        assertEmptyPrincipals();
-    }
-
-    private void badLogin() throws LoginException {
-        module.initialize(subject, BAD_CB_HANDLER, sharedState, options);
-        try {
-            module.login();
-            fail("Login is supposed to fail due to invalid username+password 
pair");
-        } catch (FailedLoginException e) {
-            // Expected, ignore
-        }
-    }
-
-    private void assertBrooklynPrincipal() {
-        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() {
-        assertEquals(subject.getPrincipals().size(), 0);
-    }
-
-}
diff --git 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/TestCallbackHandler.java
 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/TestCallbackHandler.java
deleted file mode 100644
index 4854196..0000000
--- 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/jaas/TestCallbackHandler.java
+++ /dev/null
@@ -1,50 +0,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.
- */
-package org.apache.brooklyn.rest.security.jaas;
-
-import java.io.IOException;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-public class TestCallbackHandler implements CallbackHandler {
-    private String username;
-    private String password;
-
-    public TestCallbackHandler(String username, String password) {
-        this.username = username;
-        this.password = password;
-    }
-
-    @Override
-    public void handle(Callback[] callbacks)
-            throws IOException, UnsupportedCallbackException {
-        for (Callback cb : callbacks) {
-            if (cb instanceof NameCallback) {
-                ((NameCallback)cb).setName(username);
-            } else if (cb instanceof PasswordCallback) {
-                ((PasswordCallback)cb).setPassword(password.toCharArray());
-            }
-        }
-    }
-
-}
diff --git 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
index 54c9384..de9199e 100644
--- 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
+++ 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
@@ -55,7 +55,9 @@ public abstract class BrooklynRestApiTest {
     
     protected TestShutdownHandler shutdownListener = createShutdownHandler();
     protected final static String ENDPOINT_ADDRESS_LOCAL = "local://";
-    protected final static String ENDPOINT_ADDRESS_HTTP = 
"http://localhost:9998/";;
+    protected final static String ENDPOINT_ADDRESS_HOST = "localhost";
+    protected final static int ENDPOINT_ADDRESS_PORT = 9998;
+    protected final static String ENDPOINT_ADDRESS_HTTP = 
"http://"+ENDPOINT_ADDRESS_HOST+":"+ENDPOINT_ADDRESS_PORT+"/";;
 
     protected Set<Class<?>> resourceClasses;
     protected Set<Object> resourceBeans;
diff --git 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
index 183f3de..0e2ba61 100644
--- 
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
+++ 
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
@@ -47,6 +47,9 @@ import org.apache.cxf.endpoint.Server;
 import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.cxf.jaxrs.utils.ResourceUtils;
+import org.apache.cxf.transport.http_jetty.JettyHTTPServerEngine;
+import org.apache.cxf.transport.http_jetty.JettyHTTPServerEngineFactory;
+import org.eclipse.jetty.server.session.HashSessionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -58,7 +61,8 @@ public abstract class BrooklynRestResourceTest extends 
BrooklynRestApiTest {
 
     private static final Logger log = 
LoggerFactory.getLogger(BrooklynRestResourceTest.class);
 
-    private static Server server;
+    private JettyHTTPServerEngine serverEngine;
+    private Server server;
     protected List<?> clientProviders;
     
     class DefaultTestApp extends javax.ws.rs.core.Application {
@@ -89,6 +93,12 @@ public abstract class BrooklynRestResourceTest extends 
BrooklynRestApiTest {
     protected synchronized void startServer() throws Exception {
         if (server == null) {
             setUpResources();
+            
+            // needed to enable session support
+            serverEngine = new 
JettyHTTPServerEngineFactory().createJettyHTTPServerEngine(
+                ENDPOINT_ADDRESS_HOST, ENDPOINT_ADDRESS_PORT, "http"); 
+            serverEngine.setSessionSupport(true);
+                
             JAXRSServerFactoryBean sf = 
ResourceUtils.createApplication(createRestApp(), true);
             if (clientProviders == null) {
                 clientProviders = sf.getProviders();
@@ -115,6 +125,10 @@ public abstract class BrooklynRestResourceTest extends 
BrooklynRestApiTest {
             server.destroy();
             server = null;
         }
+        if (serverEngine!=null) {
+            serverEngine.shutdown();
+            serverEngine = null;
+        }
     }
 
 

Reply via email to