Skip to content

Commit

Permalink
GH24992 Admin Logger: Record all admin commands, not only from Admin …
Browse files Browse the repository at this point in the history
…console

System property to configure the level of logged commands.
Refactoring to use HK2 topic to trigger the command recorder (depends on a fix in HK2)
  • Loading branch information
OndroMih committed Jun 22, 2024
1 parent a1aca39 commit 351803d
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 15 deletions.
9 changes: 9 additions & 0 deletions nucleus/admin/rest/rest-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-extras</artifactId>
</dependency>

<dependency>
<groupId>jakarta.ws.rs</groupId>
Expand Down Expand Up @@ -108,6 +112,11 @@
<artifactId>jersey-server</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.extras.ExtrasUtilities;
import org.glassfish.hk2.utilities.Binder;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.internal.api.InternalSystemAdministrator;
import org.glassfish.internal.api.ServerContext;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer;
import org.glassfish.jersey.inject.hk2.Hk2ReferencingFactory;
import org.glassfish.jersey.internal.ServiceFinder;
import org.glassfish.jersey.internal.util.collection.Ref;
Expand Down Expand Up @@ -138,7 +140,9 @@ private JerseyContainer getJerseyContainer(ResourceConfig config) {
ServiceFinder.setIteratorProvider(iteratorProvider);
RestLogging.restLogger.log(Level.FINEST,
() -> this + ": Creating Jersey container for " + HttpHandler.class + " and " + config);
final HttpHandler httpHandler = ContainerFactory.createContainer(HttpHandler.class, config);
final GrizzlyHttpContainer httpHandler = ContainerFactory.createContainer(GrizzlyHttpContainer.class, config);
final ServiceLocator jerseyLocator = httpHandler.getApplicationHandler().getInjectionManager().getInstance(ServiceLocator.class);
ExtrasUtilities.enableTopicDistribution(jerseyLocator);
return new JerseyContainer() {
@Override
public void service(Request request, Response response) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,13 @@
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.extras.ExtrasUtilities;
import org.glassfish.hk2.utilities.Binder;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.internal.api.AdminAccessController;
import org.glassfish.internal.api.RemoteAdminAccessException;
import org.glassfish.internal.api.ServerContext;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer;
import org.glassfish.jersey.inject.hk2.Hk2ReferencingFactory;
import org.glassfish.jersey.internal.util.collection.Ref;
import org.glassfish.jersey.internal.util.collection.Refs;
Expand Down Expand Up @@ -278,7 +280,9 @@ protected JerseyContainer exposeContext() throws EndpointRegistrationException {
private JerseyContainer getJerseyContainer(ResourceConfig config) {
RestLogging.restLogger.log(Level.FINEST,
() -> this + ": Creating Jersey container for " + HttpHandler.class + " and " + config);
final HttpHandler httpHandler = ContainerFactory.createContainer(HttpHandler.class, config);
final GrizzlyHttpContainer httpHandler = ContainerFactory.createContainer(GrizzlyHttpContainer.class, config);
final ServiceLocator jerseyLocator = httpHandler.getApplicationHandler().getInjectionManager().getInstance(ServiceLocator.class);
ExtrasUtilities.enableTopicDistribution(jerseyLocator);
return new JerseyContainer() {
@Override
public void service(Request request, Response response) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,45 @@
*/
package org.glassfish.admin.rest.commandrecorder;

import static java.lang.System.Logger.Level.INFO;
import static java.lang.System.Logger.Level.WARNING;
import static java.util.stream.Collectors.joining;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Stream;
import javax.security.auth.Subject;
import org.glassfish.admin.rest.RestLogging;
import org.glassfish.admin.rest.events.CommandInvokedEvent;
import org.glassfish.api.StartupRunLevel;
import org.glassfish.api.admin.ParameterMap;
import org.glassfish.config.support.TranslatedConfigView;
import org.glassfish.hk2.api.messaging.MessageReceiver;
import org.glassfish.hk2.api.messaging.SubscribeTo;
import org.glassfish.hk2.runlevel.RunLevel;
import org.glassfish.security.common.UserPrincipal;
import org.jvnet.hk2.annotations.Service;

/**
*
* @author Ondro Mihalyi
*/
@Service
@RunLevel(value = StartupRunLevel.VAL, mode = RunLevel.RUNLEVEL_MODE_NON_VALIDATING)
@MessageReceiver({CommandInvokedEvent.class})
public class AdminCommandRecorder {

private static final System.Logger logger = System.getLogger(AdminCommandRecorder.class.getName());

public void receiveCommandInvokedEvent(@SubscribeTo CommandInvokedEvent event) {
logCommand(event.getCommandName(), event.getParameters(), event.getSubject());
}

public void logCommand(String commandName, ParameterMap parameters, Subject subject) {
if (shouldLogCommand(commandName)) {
final Logger logger = RestLogging.restLogger;
String commandLine = constructCommandLine(commandName, parameters);
Optional<UserPrincipal> userPrincipalMaybe = getUserPrincipal(subject);
logger.info(() -> {
logger.log(INFO, () -> {
return userPrincipalMaybe.map(user -> "User " + user.getName())
.orElse("Unknown user")
+ " executed command in the Admin Console: " + commandLine;
Expand All @@ -54,16 +70,64 @@ private String constructCommandLine(String commandName, ParameterMap parameters)
.filter(param -> !DEFAULT_PARAM_KEY.equals(param.getKey()))
.map(param -> "--" + param.getKey() + "=" + param.getValue().get(0));
final List<String> unnamedParams = parameters.get(DEFAULT_PARAM_KEY);
return Stream.concat(Stream.concat(Stream.of(commandName), namedParamsStream), unnamedParams != null ? unnamedParams.stream() : Stream.empty())
final Stream<? extends String> unnamedParamsStream = unnamedParams != null ? unnamedParams.stream() : Stream.empty();
return Stream.concat(Stream.concat(
Stream.of(commandName),
namedParamsStream),
unnamedParamsStream)
.collect(joining(" "));
}

private static enum LogMode {
ALL_COMMANDS, INTERNAL_COMMANDS, WRITE_COMMANDS, READ_WRITE_COMMANDS, NO_COMMAND;

public static final LogMode DEFAULT = LogMode.WRITE_COMMANDS;
public static final String PROPERTY_NAME = "glassfish.commandrecorder.logmode";

public static LogMode get() {
final String logModeValue = TranslatedConfigView.expandValue("${" + LogMode.PROPERTY_NAME + "}");
if (logModeValue != null && !logModeValue.startsWith("$")) {
try {
return LogMode.valueOf(logModeValue);
} catch (IllegalArgumentException e) {
logger.log(WARNING,
() -> "The value of the property " + LogMode.PROPERTY_NAME + " is invalid: " + logModeValue
+ ". It should be one of " + Arrays.toString(LogMode.values()));
return LogMode.DEFAULT;
}
} else {
return LogMode.DEFAULT;
}
}

}

private boolean shouldLogCommand(String commandName) {
return Stream.of("version", "_(.*)", "list(.*)", "get(.*)", "(.*)-list-services", "uptime",
"enable-asadmin-recorder", "disable-asadmin-recorder", "set-asadmin-recorder-configuration",
"asadmin-recorder-enabled")
final LogMode logMode = LogMode.get();
switch (logMode) {
case ALL_COMMANDS:
return true;
case NO_COMMAND:
return false;
case INTERNAL_COMMANDS:
return !isReadCommand(commandName);
case READ_WRITE_COMMANDS:
return !isInternalCommand(commandName);
case WRITE_COMMANDS:
return !isReadCommand(commandName) && !isInternalCommand(commandName);
}
throw new IllegalStateException("Log mode " + logMode + " not supported yet.");
}

private boolean isReadCommand(String commandName) {
return Stream.of(
"version", "list(.*)", "get(.*)", "(.*)-list-services", "uptime")
.filter(commandName::matches)
.findAny().isEmpty();
.findAny().isPresent();
}

private boolean isInternalCommand(String commandName) {
return commandName.matches("_(.*)");
}

private Optional<UserPrincipal> getUserPrincipal(Subject subject) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.admin.rest.events;

import javax.security.auth.Subject;
import org.glassfish.api.admin.ParameterMap;

/**
*
* @author Ondro Mihalyi
*/
public class CommandInvokedEvent {

public CommandInvokedEvent(String commandName, ParameterMap parameters, Subject subject) {
this.commandName = commandName;
this.parameters = parameters;
this.subject = subject;
}

private String commandName;
private ParameterMap parameters;
private Subject subject;

public String getCommandName() {
return commandName;
}

public ParameterMap getParameters() {
return parameters;
}

public Subject getSubject() {
return subject;
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.admin.rest.events;

import jakarta.inject.Inject;
import org.glassfish.hk2.api.messaging.Topic;
import org.glassfish.internal.api.Globals;
import org.jvnet.hk2.annotations.Service;

/**
*
* @author Ondro Mihalyi
*/
@Service
public class InvokeEventService {
@Inject
Topic<CommandInvokedEvent> commandInvokedTopic;

public static InvokeEventService get() {
return Globals.getDefaultHabitat().getService(InvokeEventService.class);
}

public Topic<CommandInvokedEvent> getCommandInvokedTopic() {
return commandInvokedTopic;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import jakarta.ws.rs.core.*;
import jakarta.ws.rs.core.Response.ResponseBuilder;
import org.glassfish.admin.rest.RestLogging;
import org.glassfish.admin.rest.events.CommandInvokedEvent;
import org.glassfish.admin.rest.events.InvokeEventService;
import org.glassfish.admin.rest.utils.SseCommandHelper;
import org.glassfish.api.ActionReport;
import org.glassfish.api.admin.*;
Expand Down Expand Up @@ -356,6 +358,9 @@ private Response executeCommand(CommandName commandName, Payload.Inbound inbound
if (inbound != null) {
commandInvocation.inbound(inbound);
}
InvokeEventService.get()
.getCommandInvokedTopic()
.publish(new CommandInvokedEvent(commandName.getName(), params, getSubject()));
commandInvocation.outbound(outbound).parameters(params).execute();
ar = (ActionReporter) commandInvocation.report();
fixActionReporterSpecialCases(ar);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@
import static org.glassfish.admin.rest.utils.Util.getHtml;
import static org.glassfish.admin.rest.utils.Util.methodNameFromDtdName;

import org.glassfish.admin.rest.commandrecorder.AdminCommandRecorder;
import org.glassfish.admin.rest.events.CommandInvokedEvent;
import org.glassfish.admin.rest.events.InvokeEventService;


/**
* Resource utilities class. Used by resource templates, <code>TemplateListOfResource</code> and
Expand Down Expand Up @@ -223,8 +225,9 @@ public static RestActionReporter runCommand(String commandName, ParameterMap par
*/
public static RestActionReporter runCommand(String commandName, ParameterMap parameters, Subject subject, boolean managedJob) {

AdminCommandRecorder recorder = new AdminCommandRecorder();
recorder.logCommand(commandName, parameters, subject);
InvokeEventService.get()
.getCommandInvokedTopic()
.publish(new CommandInvokedEvent(commandName, parameters, subject));

CommandRunner cr = Globals.getDefaultHabitat().getService(CommandRunner.class);
RestActionReporter ar = new RestActionReporter();
Expand Down
5 changes: 5 additions & 0 deletions nucleus/core/bootstrap/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-core</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-extras</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.glassfish.hk2.api.ServiceLocator;

import java.util.Properties;
import org.glassfish.hk2.extras.ExtrasUtilities;

/**
* @author Sanjeeb.Sahoo@Sun.COM
Expand All @@ -38,6 +39,7 @@ public class GlassFishImpl implements GlassFish {
public GlassFishImpl(ModuleStartup gfKernel, ServiceLocator habitat, Properties gfProps) throws GlassFishException {
this.gfKernel = gfKernel;
this.habitat = habitat;
ExtrasUtilities.enableTopicDistribution(habitat);
configure(gfProps);
}

Expand Down
13 changes: 12 additions & 1 deletion nucleus/featuresets/atomic/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
</exclusions>
</dependency>
<!-- We install fileinstall bundle which monitors modules dir and autodeploy dir for addition/removal/updation of
bundles -->
bundles -->
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.fileinstall</artifactId>
Expand Down Expand Up @@ -251,6 +251,17 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-extras</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
Expand Down
2 changes: 2 additions & 0 deletions nucleus/parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
<jakarta.inject-api.version>2.0.1.MR</jakarta.inject-api.version>
<hk2.version>3.1.0</hk2.version>
<hk2.plugin.version>3.1.0</hk2.plugin.version>
<!-- <hk2.version>3.1.1-SNAPSHOT</hk2.version>
<hk2.plugin.version>3.1.1-SNAPSHOT</hk2.plugin.version>-->

<!-- Jakarta XML Binding -->
<jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version>
Expand Down

0 comments on commit 351803d

Please sign in to comment.