/javamelody-core/src/main/java/net/bull/javamelody/JiraMonitoringFilter.java
Java | 366 lines | 267 code | 26 blank | 73 comment | 46 complexity | 8dc38d80f2013e57e975eeb2fe12e9f7 MD5 | raw file
- /*
- * Copyright 2008-2016 by Emeric Vernat
- *
- * This file is part of Java Melody.
- *
- * 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 net.bull.javamelody;
-
- import java.io.IOException;
- import java.security.Principal;
- import java.util.Arrays;
- import java.util.List;
-
- 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.servlet.http.HttpSession;
-
- /**
- * Filter of monitoring JavaMelody for JIRA/Bamboo/Confluence with security check for system administrator.
- * @author Emeric Vernat
- */
- public class JiraMonitoringFilter extends PluginMonitoringFilter {
- private static final boolean PLUGIN_AUTHENTICATION_DISABLED = Boolean
- .parseBoolean(System.getProperty("javamelody.plugin-authentication-disabled"));
- // valeur de com.atlassian.jira.security.Permissions.SYSTEM_ADMIN
- private static final int SYSTEM_ADMIN = 44;
- // valeur de DefaultAuthenticator.LOGGED_IN_KEY
- private static final String LOGGED_IN_KEY = "seraph_defaultauthenticator_user";
- private static final List<String> JIRA_USER_CLASSES = Arrays.asList(
- // since JIRA 6, but exists in JIRA 5.2:
- "com.atlassian.jira.user.ApplicationUser",
- // since JIRA 5:
- "com.atlassian.crowd.embedded.api.User",
- // before JIRA 5:
- "com.opensymphony.user.User");
-
- // initialisation ici et non dans la méthode init, car on ne sait pas très bien
- // quand la méthode init serait appelée dans les systèmes de plugins
- private final boolean jira = isJira();
- private final boolean confluence = isConfluence();
- private final boolean bamboo = isBamboo();
-
- private boolean confluenceGetUserByNameExists = true; // on suppose true au départ
-
- /** {@inheritDoc} */
- @Override
- public void init(FilterConfig config) throws ServletException {
- super.init(config);
-
- if (jira) {
- LOG.debug("JavaMelody is monitoring JIRA");
- } else if (confluence) {
- LOG.debug("JavaMelody is monitoring Confluence");
- } else if (bamboo) {
- LOG.debug("JavaMelody is monitoring Bamboo");
- } else {
- LOG.debug(
- "JavaMelody is monitoring unknown, access to monitoring reports is not secured by JavaMelody");
- }
- if (PLUGIN_AUTHENTICATION_DISABLED) {
- LOG.debug("Authentication for monitoring reports has been disabled");
- }
-
- final String analyticsDisabled = "javamelody.analytics-disabled";
- if (System.getProperty(analyticsDisabled) != null
- || config.getServletContext().getInitParameter(analyticsDisabled) != null) {
- System.setProperty("javamelody.analytics-id", "disabled");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- if (!(request instanceof HttpServletRequest)) {
- super.doFilter(request, response, chain);
- return;
- }
- final HttpServletRequest httpRequest = (HttpServletRequest) request;
- final HttpServletResponse httpResponse = (HttpServletResponse) response;
-
- if (httpRequest.getRequestURI().equals(getMonitoringUrl(httpRequest))
- && hasNotPermission(httpRequest, httpResponse)) {
- return;
- }
-
- putRemoteUserInSession(httpRequest);
-
- super.doFilter(request, response, chain);
- }
-
- private void putRemoteUserInSession(HttpServletRequest httpRequest) {
- final HttpSession session = httpRequest.getSession(false);
- if (session != null
- && session.getAttribute(SessionInformations.SESSION_REMOTE_USER) == null) {
- // si session null, la session n'est pas encore créée (et ne le sera peut-être jamais),
- try {
- final Object user = getUser(session);
- // objet utilisateur, peut être null
- if (user instanceof Principal) {
- final String remoteUser = ((Principal) user).getName();
- session.setAttribute(SessionInformations.SESSION_REMOTE_USER, remoteUser);
- }
- } catch (final Exception e) {
- // tant pis
- return;
- }
- }
- }
-
- private boolean hasNotPermission(HttpServletRequest httpRequest,
- HttpServletResponse httpResponse) throws IOException {
- return !PLUGIN_AUTHENTICATION_DISABLED
- && (jira && !checkJiraAdminPermission(httpRequest, httpResponse)
- || confluence && !checkConfluenceAdminPermission(httpRequest, httpResponse)
- || bamboo && !checkBambooAdminPermission(httpRequest, httpResponse));
- }
-
- private boolean checkJiraAdminPermission(HttpServletRequest httpRequest,
- HttpServletResponse httpResponse) throws IOException {
- // only the administrator can view the monitoring report
- final Object user = getUser(httpRequest);
- if (user == null) {
- // si non authentifié, on redirige vers la page de login en indiquant la page
- // d'origine (sans le contexte) à afficher après le login
- final String destination = getMonitoringUrl(httpRequest)
- .substring(httpRequest.getContextPath().length());
- httpResponse.sendRedirect("login.jsp?os_destination=" + destination);
- return false;
- }
- if (!hasJiraSystemAdminPermission(user)) {
- // si authentifié mais sans la permission system admin, alors Forbidden
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
- return false;
- }
- return true;
- }
-
- private boolean checkConfluenceAdminPermission(HttpServletRequest httpRequest,
- HttpServletResponse httpResponse) throws IOException {
- // only the administrator can view the monitoring report
- final Object user = getUser(httpRequest);
- if (user == null) {
- // si non authentifié, on redirige vers la page de login en indiquant la page
- // d'origine (sans le contexte) à afficher après le login
- final String destination = getMonitoringUrl(httpRequest)
- .substring(httpRequest.getContextPath().length());
- httpResponse.sendRedirect("login.action?os_destination=" + destination);
- return false;
- }
- if (!hasConfluenceAdminPermission(user)) {
- // si authentifié mais sans la permission system admin, alors Forbidden
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
- return false;
- }
- return true;
- }
-
- private boolean checkBambooAdminPermission(HttpServletRequest httpRequest,
- HttpServletResponse httpResponse) throws IOException {
- // only the administrator can view the monitoring report
- final Object user = getUser(httpRequest);
- if (user == null) {
- // si non authentifié, on redirige vers la page de login en indiquant la page
- // d'origine (sans le contexte) à afficher après le login
- final String destination = getMonitoringUrl(httpRequest)
- .substring(httpRequest.getContextPath().length());
- httpResponse.sendRedirect("userlogin!default.action?os_destination=" + destination);
- return false;
- }
- if (!hasBambooAdminPermission(user)) {
- // si authentifié mais sans la permission admin, alors Forbidden
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
- return false;
- }
- return true;
- }
-
- private static boolean hasJiraSystemAdminPermission(Object user) {
- try {
- final Class<?> componentAccessorClass = Class
- .forName("com.atlassian.jira.component.ComponentAccessor");
- // on travaille par réflexion car la compilation normale introduirait une dépendance
- // trop compliquée et trop lourde à télécharger pour maven
- // Note : si getPermissionManager().hasPermission est supprimée,
- // il faudra utiliser getGlobalPermissionManager().hasPermission (Since v6.2.5)
- final Object permissionManager = componentAccessorClass
- .getMethod("getPermissionManager").invoke(null);
- Exception firstException = null;
- // selon la version de JIRA, on essaye les différentes classes possibles du user
- for (final String className : JIRA_USER_CLASSES) {
- try {
- final Class<?> userClass = Class.forName(className);
- final Boolean result = (Boolean) permissionManager.getClass()
- .getMethod("hasPermission", Integer.TYPE, userClass)
- .invoke(permissionManager, SYSTEM_ADMIN, user);
- return result;
- } catch (final Exception e) {
- if (firstException == null) {
- firstException = e;
- }
- continue;
- }
- }
- // aucune classe n'a fonctionné
- throw firstException;
- } catch (final Exception e) {
- throw new IllegalStateException(e);
- }
- // return user != null
- // && com.atlassian.jira.component.ComponentAccessor.getPermissionManager().hasPermission(
- // SYSTEM_ADMIN, (com.opensymphony.user.User) user);
- }
-
- private static boolean hasConfluenceAdminPermission(Object user) {
- try {
- final Class<?> containerManagerClass = Class
- .forName("com.atlassian.spring.container.ContainerManager");
- final Class<?> userClass = Class.forName("com.atlassian.user.User");
- // on travaille par réflexion car la compilation normale introduirait une dépendance
- // trop compliquée et trop lourde à télécharger pour maven
- final Object permissionManager = containerManagerClass
- .getMethod("getComponent", String.class).invoke(null, "permissionManager");
- final Boolean result = (Boolean) permissionManager.getClass()
- .getMethod("isConfluenceAdministrator", userClass)
- .invoke(permissionManager, user);
- return result;
- } catch (final Exception e) {
- throw new IllegalStateException(e);
- }
- // return user != null
- // && com.atlassian.spring.container.ContainerManager.getComponent("permissionManager").
- // isConfluenceAdministrator((com.opensymphony.user.User) user);
- }
-
- private static boolean hasBambooAdminPermission(Object user) {
- try {
- final Class<?> containerManagerClass = Class
- .forName("com.atlassian.spring.container.ContainerManager");
- // on travaille par réflexion car la compilation normale introduirait une dépendance
- // trop compliquée et trop lourde à télécharger pour maven
- final Object bambooPermissionManager = containerManagerClass
- .getMethod("getComponent", String.class)
- .invoke(null, "bambooPermissionManager");
-
- Boolean result;
- try {
- // since Bamboo 3.1 (issue 192):
- result = (Boolean) bambooPermissionManager.getClass()
- .getMethod("isSystemAdmin", String.class)
- .invoke(bambooPermissionManager, user.toString());
- } catch (final NoSuchMethodException e) {
- // before Bamboo 3.1 (issue 192):
- final Class<?> globalApplicationSecureObjectClass = Class
- .forName("com.atlassian.bamboo.security.GlobalApplicationSecureObject");
- final Object globalApplicationSecureObject = globalApplicationSecureObjectClass
- .getField("INSTANCE").get(null);
- result = (Boolean) bambooPermissionManager.getClass()
- .getMethod("hasPermission", String.class, String.class, Object.class)
- .invoke(bambooPermissionManager, user.toString(), "ADMIN",
- globalApplicationSecureObject);
- }
- return result;
- } catch (final Exception e) {
- throw new IllegalStateException(e);
- }
- // return user != null
- // && com.atlassian.spring.container.ContainerManager.getComponent("bambooPermissionManager").
- // hasPermission(username, "ADMIN", GlobalApplicationSecureObject.INSTANCE);
- }
-
- private Object getUser(HttpServletRequest httpRequest) {
- final HttpSession session = httpRequest.getSession(false);
- return getUser(session);
- }
-
- private Object getUser(HttpSession session) {
- // ceci fonctionne dans JIRA et dans Confluence (et Bamboo ?)
- if (session == null) {
- return null;
- }
- Object result = session.getAttribute(LOGGED_IN_KEY);
- if (confluence) {
- if (result != null && "com.atlassian.confluence.user.SessionSafePrincipal"
- .equals(result.getClass().getName())) {
- // since confluence 4.1.4 (or 4.1.?)
- final String userName = result.toString();
- // note: httpRequest.getRemoteUser() null in general
- try {
- final Class<?> containerManagerClass = Class
- .forName("com.atlassian.spring.container.ContainerManager");
- final Object userAccessor = containerManagerClass
- .getMethod("getComponent", String.class).invoke(null, "userAccessor");
- result = userAccessor.getClass().getMethod("getUser", String.class)
- .invoke(userAccessor, userName);
- } catch (final Exception e) {
- throw new IllegalStateException(e);
- }
- } else if (result instanceof Principal && confluenceGetUserByNameExists) {
- // since confluence 5.2 or 5.3
- final String userName = ((Principal) result).getName();
- try {
- final Class<?> containerManagerClass = Class
- .forName("com.atlassian.spring.container.ContainerManager");
- final Object userAccessor = containerManagerClass
- .getMethod("getComponent", String.class).invoke(null, "userAccessor");
- // getUser deprecated, use getUserByName as said in:
- // https://docs.atlassian.com/atlassian-confluence/5.3.1/com/atlassian/confluence/user/UserAccessor.html
- try {
- result = userAccessor.getClass().getMethod("getUserByName", String.class)
- .invoke(userAccessor, userName);
- } catch (final NoSuchMethodException e) {
- // getUserByName does not exist in old Confluence versions (3.5.13 for example)
- confluenceGetUserByNameExists = false;
- }
- } catch (final Exception e) {
- throw new IllegalStateException(e);
- }
- }
- }
- return result;
- }
-
- private static boolean isJira() {
- try {
- Class.forName("com.atlassian.jira.ManagerFactory");
- return true;
- } catch (final ClassNotFoundException e) {
- return false;
- }
- }
-
- private static boolean isConfluence() {
- try {
- Class.forName("com.atlassian.confluence.security.PermissionManager");
- return true;
- } catch (final ClassNotFoundException e) {
- return false;
- }
- }
-
- private static boolean isBamboo() {
- try {
- Class.forName("com.atlassian.bamboo.security.BambooPermissionManager");
- return true;
- } catch (final ClassNotFoundException e) {
- return false;
- }
- }
- }