diff --git a/assemblies/pentaho-solutions/src/main/resources/pentaho-solutions/system/pentahoServices.spring.xml b/assemblies/pentaho-solutions/src/main/resources/pentaho-solutions/system/pentahoServices.spring.xml index 69c6950d8d..09c604803b 100644 --- a/assemblies/pentaho-solutions/src/main/resources/pentaho-solutions/system/pentahoServices.spring.xml +++ b/assemblies/pentaho-solutions/src/main/resources/pentaho-solutions/system/pentahoServices.spring.xml @@ -62,7 +62,7 @@ - @@ -166,4 +166,5 @@ + \ No newline at end of file diff --git a/extensions/src/main/java/org/pentaho/platform/web/http/api/resources/Log4jResource.java b/extensions/src/main/java/org/pentaho/platform/web/http/api/resources/Log4jResource.java index 8120318834..563ac79c68 100644 --- a/extensions/src/main/java/org/pentaho/platform/web/http/api/resources/Log4jResource.java +++ b/extensions/src/main/java/org/pentaho/platform/web/http/api/resources/Log4jResource.java @@ -14,7 +14,7 @@ * See the GNU Lesser General Public License for more details. * * - * Copyright (c) 2002-2018 Hitachi Vantara. All rights reserved. + * Copyright (c) 2002-2019 Hitachi Vantara. All rights reserved. * */ @@ -29,6 +29,11 @@ import org.codehaus.enunciate.jaxrs.ResponseCode; import org.codehaus.enunciate.jaxrs.StatusCodes; import org.owasp.encoder.Encode; +import org.pentaho.platform.api.engine.IAuthorizationPolicy; +import org.pentaho.platform.engine.core.system.PentahoSystem; +import org.pentaho.platform.security.policy.rolebased.actions.AdministerSecurityAction; +import org.pentaho.platform.security.policy.rolebased.actions.RepositoryCreateAction; +import org.pentaho.platform.security.policy.rolebased.actions.RepositoryReadAction; import javax.ws.rs.FormParam; import javax.ws.rs.PUT; @@ -38,6 +43,8 @@ import javax.ws.rs.core.Response; import java.util.Enumeration; +import static javax.ws.rs.core.Response.Status.UNAUTHORIZED; + @Path( "/logconfig" ) public class Log4jResource { @@ -45,59 +52,117 @@ public class Log4jResource { private static final Logger LOGGER = Logger.getLogger( Log4jResource.class ); private static final String CONFIG = "log4j.xml"; + private IAuthorizationPolicy policy; + + public Log4jResource() { + init(); + } + + private void init() { + try { + policy = PentahoSystem.get( IAuthorizationPolicy.class ); + } catch ( Exception ex ) { + LOGGER.warn( "Unable to get IAuthorizationPolicy: " + ex.getMessage() ); + } + } + + /** + * Reloads the log4j.xml file updating the log levels and settings. + * + *

Example Request:
+ * PUT pentaho/api/logconfig/reload + *

+ * + * @return In text/plain the word "Done" if successfully executed. + */ @PUT @Path ( "/reload" ) @StatusCodes( { - @ResponseCode( code = 200, condition = "Successfully reload from configuration." ) + @ResponseCode( code = 200, condition = "Successfully reload from configuration." ), + @ResponseCode( code = 401, condition = "User is not an administrator and is unauthorized to perform action." ) } ) @Produces( { MediaType.TEXT_PLAIN } ) public Response reloadConfiguration() throws Exception { - LOGGER.setLevel( Level.INFO ); - LOGGER.info( "Reloading configuration..." ); - - DOMConfigurator.configure( Loader.getResource( CONFIG ) ); - return Response.ok( "Done" ).build(); + if ( canAdminister() ) { + LOGGER.setLevel( Level.INFO ); + LOGGER.info( "Reloading configuration..." ); + + DOMConfigurator.configure( Loader.getResource( CONFIG ) ); + return Response.ok( "Done" ).build(); + } else { + return Response.status( UNAUTHORIZED ).build(); + } } + /** + * Updates the log level for an existing log category. This change does not update the log4j.xml and does not persist across restarts. + * + * If the category parameter is provided, this will only update the level for the specific category specified. If the category parameter is not provided, this will update the log level for every logger. + * + *

Example Request:
+ * PUT pentaho/api/logconfig/reload + *

+ * + * @param category (Optional) The logging category to update the log level. + * @param level The log level to set. Valid values are (ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF, and TRACE). + * + * @return In text/plain the string "Log level updated." if successfully executed if the category is empty. In text/plain the string "Setting log level for: '${category}' to be: ${level}" if the category is not empty. + */ @PUT @Path ( "/update" ) @StatusCodes( { @ResponseCode( code = 200, condition = "Successfully update log level." ), - @ResponseCode( code = 304, condition = "Log level is not modified." ) + @ResponseCode( code = 304, condition = "Log level is not modified." ), + @ResponseCode( code = 401, condition = "User is not an administrator and is unauthorized to perform action." ) } ) @Produces( { MediaType.TEXT_PLAIN } ) - public Response updateLogLevel( @FormParam( "level" ) String targetLevel, @FormParam( "category" ) String category ) throws Exception { - LOGGER.setLevel( Level.INFO ); + public Response updateLogLevel( @FormParam( "level" ) String level, @FormParam( "category" ) String category ) throws Exception { + if ( canAdminister() ) { + LOGGER.setLevel( Level.INFO ); - if ( StringUtils.isBlank( targetLevel ) && StringUtils.isBlank( category ) ) { - return Response.notModified( "No parameter provided, log level not modified." ).build(); - } + if ( StringUtils.isBlank( level ) ) { + return Response.notModified( "No parameter provided, log level not modified." ).build(); + } + + Logger root = LogManager.getRootLogger(); - Logger root = LogManager.getRootLogger(); - if ( StringUtils.isNotBlank( targetLevel ) ) { - LOGGER.info( "Request to set log level: " + targetLevel ); + LOGGER.info( "Request to set log level: " + level ); if ( StringUtils.isNotBlank( category ) ) { LOGGER.info( "Request to set log level for package: " + category ); Logger catLog = LogManager.exists( category ); if ( catLog != null ) { - catLog.setLevel( Level.toLevel( targetLevel, root.getLevel() ) ); + catLog.setLevel( Level.toLevel( level, root.getLevel() ) ); return Response.ok( "Setting log level for: '" + catLog.getName() + "' to be: " + catLog.getLevel() ).build(); } - return Response.notModified( "Category: '" + Encode.forHtml( category ) + "' not found, log level not modified." ).build(); + return Response + .notModified( "Category: '" + Encode.forHtml( category ) + "' not found, log level not modified." ).build(); } - root.setLevel( Level.toLevel( targetLevel, root.getLevel() ) ); + root.setLevel( Level.toLevel( level, root.getLevel() ) ); LOGGER.info( "Root logger level set to: " + root.getLevel() ); Enumeration e = LogManager.getCurrentLoggers(); while ( e.hasMoreElements() ) { Logger logger = (Logger) e.nextElement(); - logger.setLevel( Level.toLevel( targetLevel, root.getLevel() ) ); + logger.setLevel( Level.toLevel( level, root.getLevel() ) ); } + + + return Response.ok( "Log level updated." ).build(); + } else { + return Response.status( UNAUTHORIZED ).build(); } + } + + /** + * Check if user has the rights to administrator + * + */ + private boolean canAdminister() { + return policy.isAllowed( RepositoryReadAction.NAME ) && policy.isAllowed( RepositoryCreateAction.NAME ) + && ( policy.isAllowed( AdministerSecurityAction.NAME ) ); - return Response.ok( "Log level updated." ).build(); } } diff --git a/extensions/src/test/java/org/pentaho/platform/web/http/api/resources/Log4jResourceTest.java b/extensions/src/test/java/org/pentaho/platform/web/http/api/resources/Log4jResourceTest.java index ae481aa78d..1e0ed42e88 100644 --- a/extensions/src/test/java/org/pentaho/platform/web/http/api/resources/Log4jResourceTest.java +++ b/extensions/src/test/java/org/pentaho/platform/web/http/api/resources/Log4jResourceTest.java @@ -14,7 +14,7 @@ * See the GNU Lesser General Public License for more details. * * - * Copyright (c) 2002-2018 Hitachi Vantara. All rights reserved. + * Copyright (c) 2002-2019 Hitachi Vantara. All rights reserved. * */ @@ -25,11 +25,15 @@ import org.apache.log4j.Logger; import org.apache.log4j.helpers.Loader; import org.apache.log4j.xml.DOMConfigurator; +import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.pentaho.platform.api.engine.IAuthorizationPolicy; +import org.pentaho.test.platform.engine.core.MicroPlatform; import javax.ws.rs.core.Response; import java.util.Enumeration; +import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -37,10 +41,30 @@ public class Log4jResourceTest { private static final String CONFIG = "log4j.xml"; - private Log4jResource target = new Log4jResource(); + + private MicroPlatform mp = null; + + @Before + public void setUp() throws Exception { + DOMConfigurator.configure( Loader.getResource( CONFIG ) ); + } + + @After + public void tearDown() { + if ( mp != null ) { + mp.stop(); + } + } @Test public void resetLogLevel() throws Exception { + // Setup the authorization + mp = new MicroPlatform(); + mp.defineInstance( IAuthorizationPolicy.class, new AllowAuthorizationPolicy() ); + mp.start(); + + Log4jResource target = new Log4jResource(); + Logger root = LogManager.getRootLogger(); Level initialLevel = root.getLevel(); assertNotEquals( initialLevel, Level.ALL ); @@ -53,8 +77,36 @@ public void resetLogLevel() throws Exception { assertEquals( "Done", res.getEntity().toString() ); } + @Test + public void resetLogLevelNotAdmin() throws Exception { + // Setup the authorization + mp = new MicroPlatform(); + mp.defineInstance( IAuthorizationPolicy.class, new DenyAuthorizationPolicy() ); + mp.start(); + + Log4jResource target = new Log4jResource(); + + Logger root = LogManager.getRootLogger(); + Level initialLevel = root.getLevel(); + assertNotEquals( initialLevel, Level.ALL ); + root.setLevel( Level.ALL ); + assertEquals( Level.ALL, root.getLevel() ); + + Response res = target.reloadConfiguration(); + assertEquals( Level.ALL, LogManager.getRootLogger().getLevel() ); + + assertEquals( 401, res.getStatus() ); + } + @Test public void updateLogLevel() throws Exception { + // Setup the authorization + mp = new MicroPlatform(); + mp.defineInstance( IAuthorizationPolicy.class, new AllowAuthorizationPolicy() ); + mp.start(); + + Log4jResource target = new Log4jResource(); + Response res = target.updateLogLevel( null, null ); assertEquals( 304, res.getStatus() ); @@ -87,9 +139,49 @@ public void updateLogLevel() throws Exception { res.getMetadata().values().toArray()[0].toString() ); } - @Before - public void readFromConfig() { - DOMConfigurator.configure( Loader.getResource( CONFIG ) ); + @Test + public void updateLogLevelNotAdmin() throws Exception { + // Setup the authorization + mp = new MicroPlatform(); + mp.defineInstance( IAuthorizationPolicy.class, new DenyAuthorizationPolicy() ); + mp.start(); + + Log4jResource target = new Log4jResource(); + + Response res = target.updateLogLevel( "DEBUG", "org.pentaho" ); + assertEquals( 401, res.getStatus() ); + } + + class AllowAuthorizationPolicy implements IAuthorizationPolicy { + + @Override + public boolean isAllowed( String actionName ) { + // TODO Auto-generated method stub + return true; + } + + @Override + public List getAllowedActions( String actionNamespace ) { + // TODO Auto-generated method stub + return null; + } + + } + + class DenyAuthorizationPolicy implements IAuthorizationPolicy { + + @Override + public boolean isAllowed( String actionName ) { + // TODO Auto-generated method stub + return false; + } + + @Override + public List getAllowedActions( String actionNamespace ) { + // TODO Auto-generated method stub + return null; + } + } }