This is an automated email from the ASF dual-hosted git repository. guangning pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/pulsar-manager.git
The following commit(s) were added to refs/heads/master by this push: new ce023dc Change default port (#316) ce023dc is described below commit ce023dca54a549fc30367c312bdf4367dfe6fcc2 Author: Guangning <guangn...@apache.org> AuthorDate: Tue Jul 7 17:33:50 2020 +0800 Change default port (#316) --- README.md | 3 ++ build.gradle | 2 ++ front-end/src/api/tokens.js | 7 ++++ front-end/src/store/modules/user.js | 2 ++ front-end/src/utils/csrfToken.js | 28 ++++++++++++++++ front-end/src/utils/request.js | 2 ++ front-end/src/views/login/index.vue | 8 +++++ .../manager/controller/CsrfTokenController.java | 39 ++++++++++++++++++++++ .../interceptor/AdminHandlerInterceptor.java | 12 +++---- .../manager/interceptor/WebAppConfigurer.java | 1 + .../pulsar/manager/security/SecurityConfig.java | 33 ++++++++++++++++++ .../pulsar/manager/zuul/EnvironmentForward.java | 11 +++--- src/main/resources/application.properties | 2 +- 13 files changed, 137 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index bd45c64..04792db 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,10 @@ If you are deploying Pulsar Manager 0.1.0 using the released container, you can If you are deploying Pulsar Manager using the latest code, you can create a super-user using the following command. Then you can use the super user credentials to log in the Pulsar Manager UI. ```$xslt + CSRF_TOKEN=$(curl http://backend-service:7750/pulsar-manager/csrf-token) curl \ + -H 'X-XSRF-TOKEN: $CSRF_TOKEN' \ + -H 'Cookie: XSRF-TOKEN=$CSRF_TOKEN;' \ -H "Content-Type: application/json" \ -X PUT http://backend-service:7750/pulsar-manager/users/superuser \ -d '{"name": "admin", "password": "apachepulsar", "description": "test", "email": "usern...@test.org"}' diff --git a/build.gradle b/build.gradle index b856ed3..786039c 100644 --- a/build.gradle +++ b/build.gradle @@ -142,6 +142,8 @@ dependencies { compile group: 'org.glassfish.jersey.core', name: 'jersey-client', version: jerseyVersion compile group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: jerseyVersion compile group: 'org.glassfish.jersey.media', name: 'jersey-media-json-jackson', version: jerseyVersion + compile group: 'org.springframework.boot', name: 'spring-boot-starter-security' + compile group: 'org.springframework.security', name: 'spring-security-config' compileOnly group: 'org.projectlombok', name: 'lombok', version: lombokVersion compileOnly group: 'org.springframework.boot', name: 'spring-boot-devtools', version: springBootVersion testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion diff --git a/front-end/src/api/tokens.js b/front-end/src/api/tokens.js index 4844cf2..0e7af41 100644 --- a/front-end/src/api/tokens.js +++ b/front-end/src/api/tokens.js @@ -56,3 +56,10 @@ export function getToken(role) { method: 'get' }) } + +export function getCsrfToken() { + return request({ + url: SPRING_BASE_URL + '/csrf-token', + method: 'get' + }) +} diff --git a/front-end/src/store/modules/user.js b/front-end/src/store/modules/user.js index 0bae227..b2acd3c 100644 --- a/front-end/src/store/modules/user.js +++ b/front-end/src/store/modules/user.js @@ -15,6 +15,7 @@ import { loginByUsername, logout } from '@/api/login' import { getToken, setToken, removeToken } from '@/utils/auth' import { setName, removeName } from '@/utils/username' import { removeEnvironment } from '@/utils/environment' +import { removeCsrfToken } from '@/utils/csrfToken' import { Message } from 'element-ui' import { setTenant, removeTenant } from '../../utils/tenant' import { getUserInfo } from '@/api/users' @@ -104,6 +105,7 @@ const user = { commit('SET_TOKEN', '') commit('SET_ROLES', []) removeToken() + removeCsrfToken() removeName() removeTenant() removeEnvironment() diff --git a/front-end/src/utils/csrfToken.js b/front-end/src/utils/csrfToken.js new file mode 100644 index 0000000..d9014cf --- /dev/null +++ b/front-end/src/utils/csrfToken.js @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import Cookies from 'js-cookie' + +const csrfToken = 'XSRF-TOKEN' + +export function getCsrfToken() { + return Cookies.get(csrfToken) +} + +export function setCsrfToken(csrfToken) { + return Cookies.set(csrfToken, csrfToken) +} + +export function removeCsrfToken() { + return Cookies.remove(csrfToken) +} diff --git a/front-end/src/utils/request.js b/front-end/src/utils/request.js index 1f971ee..1a55e73 100644 --- a/front-end/src/utils/request.js +++ b/front-end/src/utils/request.js @@ -20,6 +20,7 @@ import { getName } from '@/utils/username' import { getEnvironment } from '@/utils/environment' import { getTenant } from '@/utils/tenant' import router from '../router' +import { getCsrfToken } from '@/utils/csrfToken' // create an axios instance const service = axios.create({ @@ -37,6 +38,7 @@ service.interceptors.request.use( config.headers['username'] = getName() config.headers['tenant'] = getTenant() config.headers['environment'] = getEnvironment() + config.headers['X-XSRF-TOKEN'] = getCsrfToken() return config }, error => { diff --git a/front-end/src/views/login/index.vue b/front-end/src/views/login/index.vue index f94bb63..f4b79c3 100644 --- a/front-end/src/views/login/index.vue +++ b/front-end/src/views/login/index.vue @@ -72,6 +72,8 @@ <script> import LangSelect from '@/components/LangSelect' import SocialSign from './socialsignin' +import { getCsrfToken } from '@/api/tokens' +import { setCsrfToken } from '@/utils/csrfToken' export default { name: 'Login', @@ -118,6 +120,7 @@ export default { }, created() { // window.addEventListener('hashchange', this.afterQRScan) + this.fetchCsrfToken() }, destroyed() { window.removeEventListener('hashchange', this.afterQRScan) @@ -172,6 +175,11 @@ export default { // this.$router.push({ path: '/' }) // }) // } + }, + fetchCsrfToken() { + getCsrfToken().then(response => { + setCsrfToken(response.headers['x-csrf-token']) + }) } } } diff --git a/src/main/java/org/apache/pulsar/manager/controller/CsrfTokenController.java b/src/main/java/org/apache/pulsar/manager/controller/CsrfTokenController.java new file mode 100644 index 0000000..535b3dc --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/controller/CsrfTokenController.java @@ -0,0 +1,39 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pulsar.manager.controller; + +import io.swagger.annotations.Api; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; + +@RestController +@RequestMapping(value = "/pulsar-manager") +@Api(description = "Generate csrf token for per page.") +public class CsrfTokenController { + + @RequestMapping(value="/csrf-token", method= RequestMethod.GET) + public ResponseEntity<String> getCsrfToken(HttpServletRequest request) { + CsrfToken token = (CsrfToken)request.getAttribute(CsrfToken.class.getName()); + HttpHeaders headers = new HttpHeaders(); + headers.add("X-Csrf-Token", token.getToken()); + return new ResponseEntity<> (token.getToken(), headers, HttpStatus.OK); + } + +} diff --git a/src/main/java/org/apache/pulsar/manager/interceptor/AdminHandlerInterceptor.java b/src/main/java/org/apache/pulsar/manager/interceptor/AdminHandlerInterceptor.java index 433e5d0..0d630d0 100644 --- a/src/main/java/org/apache/pulsar/manager/interceptor/AdminHandlerInterceptor.java +++ b/src/main/java/org/apache/pulsar/manager/interceptor/AdminHandlerInterceptor.java @@ -15,7 +15,6 @@ package org.apache.pulsar.manager.interceptor; import com.google.common.collect.Maps; import com.google.gson.Gson; -import org.apache.commons.lang.StringUtils; import org.apache.pulsar.manager.entity.EnvironmentEntity; import org.apache.pulsar.manager.entity.EnvironmentsRepository; import org.apache.pulsar.manager.entity.UserInfoEntity; @@ -27,13 +26,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; import java.util.Map; import java.util.Optional; @@ -64,8 +61,9 @@ public class AdminHandlerInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // allow frontend requests, in case of front-end running on the same process of backend - if (request.getRequestURI().startsWith("/ui") - || request.getRequestURI().startsWith("/static")) { + + if (request.getServletPath().startsWith("/ui") + || request.getServletPath().startsWith("/static")) { return true; } String token = request.getHeader("token"); @@ -95,11 +93,11 @@ public class AdminHandlerInterceptor extends HandlerInterceptorAdapter { return false; } } - String requestUri = request.getRequestURI(); + String requestUri = request.getServletPath(); if (!requestUri.equals("/pulsar-manager/users/userInfo")) { String environment = request.getHeader("environment"); Optional<EnvironmentEntity> environmentEntityOptional = environmentsRepository.findByName(environment); - if (!request.getRequestURI().startsWith("/pulsar-manager/environments") && !environmentEntityOptional.isPresent()) { + if (!request.getServletPath().startsWith("/pulsar-manager/environments") && !environmentEntityOptional.isPresent()) { map.put("message", "Currently there is no active environment, please set one"); response.setStatus(400); response.getWriter().append(gson.toJson(map)); diff --git a/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java b/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java index 0c97479..a70a2a5 100644 --- a/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java +++ b/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java @@ -35,6 +35,7 @@ public class WebAppConfigurer implements WebMvcConfigurer { registry.addInterceptor(adminHandlerInterceptor).addPathPatterns("/**") .excludePathPatterns("/pulsar-manager/login") .excludePathPatterns("/pulsar-manager/users/superuser") + .excludePathPatterns("/pulsar-manager/csrf-token") .excludePathPatterns("/pulsar-manager/third-party-login/**") // static front-end resources .excludePathPatterns("/ui") diff --git a/src/main/java/org/apache/pulsar/manager/security/SecurityConfig.java b/src/main/java/org/apache/pulsar/manager/security/SecurityConfig.java new file mode 100644 index 0000000..a2f476d --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/security/SecurityConfig.java @@ -0,0 +1,33 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pulsar.manager.security; + +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; + +/** + * Security Config. + */ +@SuppressWarnings("unchecked") +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + public void configure(HttpSecurity http) throws Exception { + http.csrf() + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());; + } +} diff --git a/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java b/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java index d3336c0..da0d8cc 100644 --- a/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java +++ b/src/main/java/org/apache/pulsar/manager/zuul/EnvironmentForward.java @@ -83,7 +83,8 @@ public class EnvironmentForward extends ZuulFilter { HttpServletRequest request = ctx.getRequest(); String redirect = request.getParameter("redirect"); - String requestUri = request.getRequestURI(); + String requestUri = request.getServletPath(); + request.getServletPath(); String token = request.getHeader("token"); if (!rolesService.isSuperUser(token)) { @@ -129,17 +130,17 @@ public class EnvironmentForward extends ZuulFilter { } private Object forwardRequest(RequestContext ctx, HttpServletRequest request, String serviceUrl) { - ctx.put(REQUEST_URI_KEY, request.getRequestURI()); + ctx.put(REQUEST_URI_KEY, request.getServletPath()); try { Map<String, String> authHeader = pulsarAdminService.getAuthHeader(serviceUrl); authHeader.entrySet().forEach(entry -> ctx.addZuulRequestHeader(entry.getKey(), entry.getValue())); ctx.setRouteHost(new URL(serviceUrl)); - pulsarEvent.parsePulsarEvent(request.getRequestURI(), request); + pulsarEvent.parsePulsarEvent(request.getServletPath(), request); log.info("Forward request to {} @ path {}", - serviceUrl, request.getRequestURI()); + serviceUrl, request.getServletPath()); } catch (MalformedURLException e) { log.error("Route forward to {} path {} error: {}", - serviceUrl, request.getRequestURI(), e.getMessage()); + serviceUrl, request.getServletPath(), e.getMessage()); } return null; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 46e0462..31a07b1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -13,7 +13,7 @@ # spring.cloud.refresh.refreshable=none -server.port=8080 +server.port=7750 # configuration log logging.path=