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 5df761c7fcba55949943829b324c62af09bd5f55 Author: Juan Cabrerizo <[email protected]> AuthorDate: Mon Nov 26 11:24:50 2018 +0000 adding oauth server --- launcher/pom.xml | 12 + .../brooklyn/launcher/BrooklynWebServer.java | 11 +- rest/rest-server/pom.xml | 12 + .../org/apache/brooklyn/rest/RestApiSetup.java | 17 ++ .../apache/brooklyn/rest/filter/MyOauthFilter.java | 244 +++++++++++++++++++++ 5 files changed, 288 insertions(+), 8 deletions(-) diff --git a/launcher/pom.xml b/launcher/pom.xml index a18827e..23d6d76 100644 --- a/launcher/pom.xml +++ b/launcher/pom.xml @@ -213,6 +213,18 @@ <classifier>tests</classifier> <scope>test</scope> </dependency> + <dependency> + <groupId>com.googlecode.json-simple</groupId> + <artifactId>json-simple</artifactId> + <version>1.1</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>com.googlecode.json-simple</groupId> + <artifactId>json-simple</artifactId> + <version>1.1</version> + <scope>compile</scope> + </dependency> </dependencies> diff --git a/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java b/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java index 9ead29a..cd0cb00 100644 --- a/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java +++ b/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java @@ -53,14 +53,7 @@ import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocati import org.apache.brooklyn.rest.BrooklynWebConfig; import org.apache.brooklyn.rest.NopSecurityHandler; import org.apache.brooklyn.rest.RestApiSetup; -import org.apache.brooklyn.rest.filter.CorsImplSupplierFilter; -import org.apache.brooklyn.rest.filter.CsrfTokenFilter; -import org.apache.brooklyn.rest.filter.EntitlementContextFilter; -import org.apache.brooklyn.rest.filter.HaHotCheckResourceFilter; -import org.apache.brooklyn.rest.filter.LoggingFilter; -import org.apache.brooklyn.rest.filter.NoCacheFilter; -import org.apache.brooklyn.rest.filter.RequestTaggingFilter; -import org.apache.brooklyn.rest.filter.RequestTaggingRsFilter; +import org.apache.brooklyn.rest.filter.*; import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule; import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule.RolePrincipal; import org.apache.brooklyn.rest.security.jaas.JaasUtils; @@ -501,6 +494,8 @@ public class BrooklynWebServer { RestApiSetup.installServletFilters(context, RequestTaggingFilter.class, LoggingFilter.class); + RestApiSetup.installOauthServletFilters(context, + MyOauthFilter.class); if (securityFilterClazz != null) { RestApiSetup.installServletFilters(context, securityFilterClazz); } diff --git a/rest/rest-server/pom.xml b/rest/rest-server/pom.xml index fca9be4..aa97e00 100644 --- a/rest/rest-server/pom.xml +++ b/rest/rest-server/pom.xml @@ -209,6 +209,18 @@ <artifactId>cxf-rt-rs-client</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>com.googlecode.json-simple</groupId> + <artifactId>json-simple</artifactId> + <version>1.1</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>com.googlecode.json-simple</groupId> + <artifactId>json-simple</artifactId> + <version>1.1</version> + <scope>compile</scope> + </dependency> </dependencies> <build> diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/RestApiSetup.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/RestApiSetup.java index 33bbfd3..d961f7d 100644 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/RestApiSetup.java +++ b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/RestApiSetup.java @@ -26,14 +26,17 @@ import javax.servlet.DispatcherType; import javax.servlet.Filter; import org.apache.brooklyn.rest.apidoc.RestApiResourceScanner; +import org.apache.brooklyn.rest.filter.MyOauthFilter; import org.apache.cxf.BusFactory; import org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet; import org.apache.cxf.transport.common.gzip.GZIPInInterceptor; import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor; +import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import io.swagger.config.ScannerFactory; +import org.eclipse.jetty.webapp.WebAppContext; public class RestApiSetup { @@ -70,4 +73,18 @@ public class RestApiSetup { ScannerFactory.setScanner(new RestApiResourceScanner()); } + public static void installOauthServletFilters(WebAppContext context, Class<MyOauthFilter> myOauthFilterClass) { + FilterHolder fh= context.addFilter(myOauthFilterClass, "/*", EnumSet.allOf(DispatcherType.class)); + setFilterParams(fh); + } + private static void setFilterParams(FilterHolder fh) { + fh.setInitParameter(MyOauthFilter.PARAM_URI_GETTOKEN, "https://accounts.google.com/o/oauth2/token"); + fh.setInitParameter(MyOauthFilter.PARAM_URI_TOKEN_INFO, "https://www.googleapis.com/oauth2/v1/tokeninfo"); + fh.setInitParameter(MyOauthFilter.PARAM_URI_LOGIN_REDIRECT, "/login"); + fh.setInitParameter(MyOauthFilter.PARAM_CLIENT_ID, + "789182012565-burd24h3bc0im74g2qemi7lnihvfqd02.apps.googleusercontent.com"); + fh.setInitParameter(MyOauthFilter.PARAM_CLIENT_SECRET, "X00v-LfU34U4SfsHqPKMWfQl"); + fh.setInitParameter(MyOauthFilter.PARAM_CALLBACK_URI, "http://localhost.io:8080/service/ping"); + fh.setInitParameter(MyOauthFilter.PARAM_AUDIENCE, "audience"); + } } diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/filter/MyOauthFilter.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/filter/MyOauthFilter.java new file mode 100644 index 0000000..4ae2b4a --- /dev/null +++ b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/filter/MyOauthFilter.java @@ -0,0 +1,244 @@ +/* + * 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.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 net.minidev.json.JSONObject; +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.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +public class MyOauthFilter implements Filter { + + private static final String SESSION_KEY_CODE = "code"; + + private static final String SESSION_KEY_ACCESS_TOKEN = "access_token"; + + public static final String PARAM_URI_TOKEN_INFO = "uriTokenInfo"; + private String uriTokenInfo = ""; + public static final String PARAM_URI_GETTOKEN = "uriGetToken"; + private String uriGetToken = ""; + public static final String PARAM_URI_LOGIN_REDIRECT = "uriLoginRedirect"; + private String uriTokenRedirect = ""; + public static final String PARAM_CLIENT_ID = "clientId"; + private String clientId = ""; + public static final String PARAM_CLIENT_SECRET = "clientSecret"; + private String clientSecret = ""; + public static final String PARAM_CALLBACK_URI = "callbackUri"; + private String callbackUri = ""; + public static final String PARAM_AUDIENCE = "audience"; + private String audience = ""; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + 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 { + 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 { + // 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); + JSONObject jsonObject = null; + + // get the access token from json and request info from Google + try { + jsonObject = (JSONObject) new JSONParser().parse(body); + } catch (ParseException e) { + throw new RuntimeException("Unable to parse json " + body); + } + + 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); + + JSONObject jsonObject = null; + + // get the access token from json and request info from Google + try { + jsonObject = (JSONObject) new JSONParser().parse(body); + } catch (ParseException e) { + // throw new RuntimeException("Unable to parse json " + 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 destroy() { + // TODO Auto-generated method stub + } + +}
