You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by st...@apache.org on 2016/03/30 21:51:18 UTC
[4/7] ambari git commit: AMBARI-15241. Basic Operational Audit
Logging. (Daniel Gergely via stoader)
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEvent.java
new file mode 100644
index 0000000..200ecfa
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEvent.java
@@ -0,0 +1,161 @@
+/*
+ * 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.ambari.server.audit.request;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AbstractUserAuditEvent;
+
+/**
+ * Base class for start operation audit events.
+ */
+public class RequestAuditEvent extends AbstractUserAuditEvent {
+
+ public static class RequestAuditEventBuilder<T extends RequestAuditEvent, TBuilder extends RequestAuditEventBuilder<T, TBuilder>> extends AbstractUserAuditEventBuilder<T, TBuilder> {
+
+ /**
+ * Request type (PUT, POST, DELETE, etc...)
+ */
+ private Request.Type requestType;
+
+ /**
+ * Result status, that contains http statuses (OK, ACCEPTED, FORBIDDEN, etc...)
+ */
+ private ResultStatus resultStatus;
+
+ /**
+ * The url that is called
+ */
+ private String url;
+
+ /**
+ * Description of the operation
+ */
+ private String operation;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected T newAuditEvent() {
+ return (T) new RequestAuditEvent(this);
+ }
+
+ /**
+ * Appends to the event the details of the incoming request.
+ *
+ * @param builder builder for the audit event details.
+ */
+ @Override
+ protected void buildAuditMessage(StringBuilder builder) {
+ super.buildAuditMessage(builder);
+ if (operation != null) {
+ builder
+ .append(", Operation(")
+ .append(operation)
+ .append(")");
+ }
+ builder
+ .append(", RequestType(")
+ .append(requestType)
+ .append("), ")
+ .append("url(")
+ .append(url)
+ .append("), ResultStatus(")
+ .append(resultStatus.getStatusCode())
+ .append(" ")
+ .append(resultStatus.getStatus())
+ .append(")");
+
+ if (resultStatus.isErrorState()) {
+ builder.append(", Reason(")
+ .append(resultStatus.getMessage())
+ .append(")");
+ }
+ }
+
+ /**
+ * Sets the request type to be added to the audit event.
+ *
+ * @param requestType request type to be added to the audit event.
+ * @return this builder
+ */
+ public TBuilder withRequestType(Request.Type requestType) {
+ this.requestType = requestType;
+
+ return (TBuilder) this;
+ }
+
+ /**
+ * Sets the url to be added to the audit event.
+ *
+ * @param url url to be added to the audit event.
+ * @return this builder
+ */
+ public TBuilder withUrl(String url) {
+ this.url = url;
+
+ return (TBuilder) this;
+ }
+
+ /**
+ * Sets the result status to be added to the audit event.
+ *
+ * @param resultStatus result status to be added to the audit event.
+ * @return this builder
+ */
+ public TBuilder withResultStatus(ResultStatus resultStatus) {
+ this.resultStatus = resultStatus;
+
+ return (TBuilder) this;
+ }
+
+ /**
+ * Sets the operation to be added to the audit event.
+ *
+ * @param operation operation to be added to the audit event.
+ * @return this builder
+ */
+ public TBuilder withOperation(String operation) {
+ this.operation = operation;
+
+ return (TBuilder) this;
+ }
+ }
+
+ protected RequestAuditEvent() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected RequestAuditEvent(RequestAuditEventBuilder<?, ?> builder) {
+ super(builder);
+ }
+
+ /**
+ * Returns an builder for {@link RequestAuditEvent}
+ *
+ * @return a builder instance
+ */
+ public static RequestAuditEventBuilder<?, ?> builder() {
+ return new RequestAuditEventBuilder();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEventCreator.java
new file mode 100644
index 0000000..ccb39de
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditEventCreator.java
@@ -0,0 +1,59 @@
+/*
+ * 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.ambari.server.audit.request;
+
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.controller.spi.Resource;
+
+/**
+ * This interface must be implemented by the plugins for the request audit logger
+ * in order to make custom {@link AuditEvent}s based on {@link org.apache.ambari.server.api.services.Request.Type}s
+ * and {@link org.apache.ambari.server.controller.spi.Resource.Type}
+ */
+public interface RequestAuditEventCreator {
+
+ /**
+ * @return the set of {@link org.apache.ambari.server.api.services.Request.Type}s that are handled by this creator
+ */
+ Set<Request.Type> getRequestTypes();
+
+ /**
+ * @return the {@link org.apache.ambari.server.controller.spi.Resource.Type}s that is handled by this creator
+ */
+ Set<Resource.Type> getResourceTypes();
+
+ /**
+ * @return the {@link ResultStatus}es that is handled by this creator
+ */
+ Set<ResultStatus.STATUS> getResultStatuses();
+
+ /**
+ * Creates and {@link AuditEvent}
+ * @param request HTTP request object
+ * @param result HTTP result object
+ * @return an {@link AuditEvent}
+ */
+ AuditEvent createAuditEvent(Request request, Result result);
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLogger.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLogger.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLogger.java
new file mode 100644
index 0000000..c132139
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLogger.java
@@ -0,0 +1,36 @@
+/*
+ * 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.ambari.server.audit.request;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+
+/**
+ * Audit logger interface for logging requests
+ */
+public interface RequestAuditLogger {
+
+ /**
+ * Logs an audit event based on the http request and result
+ * @param request
+ * @param result
+ */
+ void log(Request request, Result result);
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLoggerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLoggerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLoggerImpl.java
new file mode 100644
index 0000000..56efd18
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/RequestAuditLoggerImpl.java
@@ -0,0 +1,150 @@
+/*
+ * 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.ambari.server.audit.request;
+
+
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.AuditLogger;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * The purpose of this class is to create audit log entries for the HTTP requests
+ */
+@Singleton
+public class RequestAuditLoggerImpl implements RequestAuditLogger {
+
+ /**
+ * Priorities for searching the proper creator
+ */
+ private static final int REQUEST_TYPE_PRIORITY = 1;
+ private static final int RESULT_STATUS_PRIORITY = 2;
+ private static final int RESOURCE_TYPE_PRIORITY = 4;
+
+ /**
+ * Container for the {@link RequestAuditEventCreator}
+ */
+ private Set<RequestAuditEventCreator> creators;
+
+ /**
+ * Audit logger that receives {@link AuditEvent}s and does the actual logging
+ */
+ private AuditLogger auditLogger;
+
+ /**
+ * Injecting dependencies through the constructor
+ * @param auditLogger Audit Logger
+ * @param creatorSet Set of plugins that are registered for requests
+ */
+ @Inject
+ public RequestAuditLoggerImpl(AuditLogger auditLogger, Set<RequestAuditEventCreator> creatorSet) {
+ this.auditLogger = auditLogger;
+ this.creators = creatorSet;
+ }
+
+ /**
+ * Finds the proper creator, then creates and logs and {@link AuditEvent}
+ * @param request
+ * @param result
+ */
+ @Override
+ public void log(Request request, Result result) {
+ if(!auditLogger.isEnabled()) {
+ return;
+ }
+
+ Resource.Type resourceType = request.getResource().getResourceDefinition().getType();
+ Request.Type requestType = request.getRequestType();
+ ResultStatus resultStatus = result.getStatus();
+
+ RequestAuditEventCreator creator = selectCreator(resourceType, resultStatus, requestType);
+ if (creator != null) {
+ AuditEvent ae = creator.createAuditEvent(request, result);
+ if (ae != null) {
+ auditLogger.log(ae);
+ }
+ }
+ }
+
+ /**
+ * Select the proper creator. Priority order: resourceType > resultStatus > requestType
+ * The most matching creator is returned
+ * If there is no creator found, then null is returned.
+ * @param resourceType
+ * @param requestType
+ * @param resultStatus
+ * @return
+ */
+ private RequestAuditEventCreator selectCreator(Resource.Type resourceType, ResultStatus resultStatus, Request.Type requestType) {
+
+ RequestAuditEventCreator selected = null;
+ Integer priority = -1;
+
+ for (RequestAuditEventCreator creator : creators) {
+ Integer creatorPriority = getPriority(creator, resourceType, resultStatus, requestType);
+ if (creatorPriority != null && priority < creatorPriority) {
+ priority = creatorPriority;
+ selected = creator;
+ }
+ }
+ return selected;
+ }
+
+ /**
+ * Calculates the creator priority for the actual resouce type, result status and request type
+ * @param creator
+ * @param resourceType
+ * @param resultStatus
+ * @param requestType
+ * @return
+ */
+ private Integer getPriority(RequestAuditEventCreator creator, Resource.Type resourceType, ResultStatus resultStatus, Request.Type requestType) {
+ Integer priority = 0;
+
+ if (isIncompatible(creator, resourceType, resultStatus, requestType)) {
+ return null;
+ }
+
+ priority += creator.getRequestTypes() != null && creator.getRequestTypes().contains(requestType) ? REQUEST_TYPE_PRIORITY : 0;
+ priority += creator.getResultStatuses() != null && creator.getResultStatuses().contains(resultStatus.getStatus()) ? RESULT_STATUS_PRIORITY : 0;
+ priority += creator.getResourceTypes() != null && creator.getResourceTypes().contains(resourceType) ? RESOURCE_TYPE_PRIORITY : 0;
+ return priority;
+ }
+
+ /**
+ * Checks if the creator is a possible candidate for creating audit log event for the request
+ * @param creator
+ * @param resourceType
+ * @param resultStatus
+ * @param requestType
+ * @return
+ */
+ private boolean isIncompatible(RequestAuditEventCreator creator, Resource.Type resourceType, ResultStatus resultStatus, Request.Type requestType) {
+ return creator.getRequestTypes() != null && !creator.getRequestTypes().contains(requestType) ||
+ creator.getResultStatuses() != null && !creator.getResultStatuses().contains(resultStatus.getStatus()) ||
+ creator.getResourceTypes() != null && !creator.getResourceTypes().contains(resourceType);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertGroupEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertGroupEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertGroupEventCreator.java
new file mode 100644
index 0000000..103fd4d
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertGroupEventCreator.java
@@ -0,0 +1,170 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.AddAlertGroupRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.ChangeAlertGroupRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.DeleteAlertGroupRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles alert group requests
+ * For resource type {@link Resource.Type#AlertGroup}
+ * and request types {@link Request.Type#POST}, {@link Request.Type#PUT} and {@link Request.Type#DELETE}
+ */
+public class AlertGroupEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.PUT, Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.AlertGroup).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ switch (request.getRequestType()) {
+ case POST:
+ return AddAlertGroupRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withName(getName(request))
+ .withDefinitionIds(getDefinitionIds(request))
+ .withNotificationIds(getNotificationIds(request))
+ .build();
+ case PUT:
+ return ChangeAlertGroupRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withName(getName(request))
+ .withDefinitionIds(getDefinitionIds(request))
+ .withNotificationIds(getNotificationIds(request))
+ .build();
+ case DELETE:
+ return DeleteAlertGroupRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withId(request.getResource().getKeyValueMap().get(Resource.Type.AlertGroup))
+ .build();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns the alert group name from the request
+ * @param request
+ * @return
+ */
+ private String getName(Request request) {
+ if (!request.getBody().getNamedPropertySets().isEmpty()) {
+ return String.valueOf(request.getBody().getNamedPropertySets().iterator().next().getProperties().get(PropertyHelper.getPropertyId("AlertGroup", "name")));
+ }
+ return null;
+ }
+
+ /**
+ * Returns definition ids from the request
+ * @param request
+ * @return
+ */
+ private List<String> getDefinitionIds(Request request) {
+ if (!request.getBody().getNamedPropertySets().isEmpty()) {
+ List<String> list = (List<String>) request.getBody().getNamedPropertySets().iterator().next().getProperties().get(PropertyHelper.getPropertyId("AlertGroup", "definitions"));
+ if (list != null) {
+ return list;
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns notification ids from the request
+ * @param request
+ * @return
+ */
+ private List<String> getNotificationIds(Request request) {
+ if (!request.getBody().getNamedPropertySets().isEmpty()) {
+ List<String> list = (List<String>) request.getBody().getNamedPropertySets().iterator().next().getProperties().get(PropertyHelper.getPropertyId("AlertGroup", "targets"));
+ if (list != null) {
+ return list;
+ }
+ }
+ return Collections.emptyList();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertTargetEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertTargetEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertTargetEventCreator.java
new file mode 100644
index 0000000..29a241e
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/AlertTargetEventCreator.java
@@ -0,0 +1,164 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.AddAlertTargetRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.ChangeAlertTargetRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.DeleteAlertTargetRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles alert target requests
+ * For resource type {@link Resource.Type#AlertTarget}
+ * and request types {@link Request.Type#POST}, {@link Request.Type#PUT} and {@link Request.Type#DELETE}
+ */
+public class AlertTargetEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.PUT, Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.AlertTarget).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ switch (request.getRequestType()) {
+ case POST:
+ return AddAlertTargetRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withName(getProperty(request, "name"))
+ .withDescription(getProperty(request, "description"))
+ .withAlertStates(getPropertyList(request, "alert_states"))
+ .withGroupIds(getPropertyList(request, "groups"))
+ .withNotificationType(getProperty(request, "notification_type"))
+ .withEmailFrom(getProperty(request, "properties/mail.smtp.from"))
+ .withEmailRecipients(getPropertyList(request, "properties/ambari.dispatch.recipients"))
+ .build();
+ case PUT:
+ return ChangeAlertTargetRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withName(getProperty(request, "name"))
+ .withDescription(getProperty(request, "description"))
+ .withAlertStates(getPropertyList(request, "alert_states"))
+ .withGroupIds(getPropertyList(request, "groups"))
+ .withNotificationType(getProperty(request, "notification_type"))
+ .withEmailFrom(getProperty(request, "properties/mail.smtp.from"))
+ .withEmailRecipients(getPropertyList(request, "properties/ambari.dispatch.recipients"))
+ .build();
+ case DELETE:
+ return DeleteAlertTargetRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withId(request.getResource().getKeyValueMap().get(Resource.Type.AlertTarget))
+ .build();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns a property list from the request, named by the parameter propertyName
+ * @param request
+ * @param propertyName
+ * @return
+ */
+ private List<String> getPropertyList(Request request, String propertyName) {
+ if (!request.getBody().getNamedPropertySets().isEmpty()) {
+ List<String> list = (List<String>) request.getBody().getNamedPropertySets().iterator().next().getProperties().get(PropertyHelper.getPropertyId("AlertTarget", propertyName));
+ if (list != null) {
+ return list;
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns a property from the request, named by the parameter propertyName
+ * @param request
+ * @param propertyName
+ * @return
+ */
+ private String getProperty(Request request, String propertyName) {
+ if (!request.getBody().getPropertySets().isEmpty()) {
+ return String.valueOf(request.getBody().getPropertySets().iterator().next().get(PropertyHelper.getPropertyId("AlertTarget", propertyName)));
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintEventCreator.java
new file mode 100644
index 0000000..bdd6dbe
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintEventCreator.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.AddBlueprintRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.DeleteBlueprintRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles blueprint add and remove requests
+ * For resource type {@link Resource.Type#Blueprint}
+ * and request types {@link Request.Type#POST} and {@link Request.Type#POST}
+ */
+public class BlueprintEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Blueprint).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ switch (request.getRequestType()) {
+ case POST:
+ return AddBlueprintRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withBlueprintName(request.getResource().getKeyValueMap().get(Resource.Type.Blueprint))
+ .build();
+ case DELETE:
+ return DeleteBlueprintRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withBlueprintName(request.getResource().getKeyValueMap().get(Resource.Type.Blueprint))
+ .build();
+ default:
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintExportEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintExportEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintExportEventCreator.java
new file mode 100644
index 0000000..1416021
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/BlueprintExportEventCreator.java
@@ -0,0 +1,94 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.BlueprintExportRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles blueprint export requests
+ * For resource type {@link Resource.Type#Cluster}
+ * and request types {@link Request.Type#GET}
+ */
+public class BlueprintExportEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.GET).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Cluster).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+ if (!request.getURI().contains("format=blueprint")) {
+ return null;
+ }
+ return BlueprintExportRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ComponentEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ComponentEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ComponentEventCreator.java
new file mode 100644
index 0000000..8034d24
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ComponentEventCreator.java
@@ -0,0 +1,188 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.StartOperationRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.internal.RequestOperationLevel;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles operation requests (start, stop, install, etc)
+ * For resource type {@link org.apache.ambari.server.controller.spi.Resource.Type#HostComponent}
+ * and request types {@link org.apache.ambari.server.api.services.Request.Type#POST}, {@link org.apache.ambari.server.api.services.Request.Type#PUT} and {@link org.apache.ambari.server.api.services.Request.Type#DELETE}
+ */
+public class ComponentEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.PUT, Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.HostComponent).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ // null makes this default
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ String operation = getOperation(request);
+
+ Long requestId = null;
+ if (containsRequestId(result)) {
+ requestId = getRequestId(result);
+ }
+
+ StartOperationRequestAuditEvent.StartOperationAuditEventBuilder auditEventBuilder = StartOperationRequestAuditEvent.builder()
+ .withOperation(operation)
+ .withUserName(username)
+ .withRemoteIp(request.getRemoteAddress())
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestId(String.valueOf(requestId));
+
+ if (result.getStatus().isErrorState()) {
+ auditEventBuilder.withReasonOfFailure(result.getStatus().getMessage());
+ }
+
+ return auditEventBuilder.build();
+ }
+
+ /**
+ * Generates operation name based on the request. It checks the operation level, the host name, the service name, the status
+ * and whether this is a maintenance mode switch change.
+ * @param request
+ * @return
+ */
+ private String getOperation(Request request) {
+ if (request.getRequestType() == Request.Type.DELETE) {
+ return "Delete component " + request.getResource().getKeyValueMap().get(Resource.Type.HostComponent);
+ }
+
+ if (request.getBody().getRequestInfoProperties().containsKey(RequestOperationLevel.OPERATION_LEVEL_ID)) {
+ String operation = "";
+ switch (request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_LEVEL_ID)) {
+ case "CLUSTER":
+ for (Map<String, Object> map : request.getBody().getPropertySets()) {
+ if (map.containsKey(PropertyHelper.getPropertyId("HostRoles", "cluster_name"))) {
+ operation = String.valueOf(map.get(PropertyHelper.getPropertyId("HostRoles", "state"))) + ": all services"
+ + " on all hosts"
+ + (request.getBody().getQueryString().length() > 0 ? " that matches " + request.getBody().getQueryString() : "")
+ + " (" + request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_CLUSTER_ID) + ")";
+ break;
+ }
+ }
+ break;
+ case "HOST":
+ for (Map<String, Object> map : request.getBody().getPropertySets()) {
+ if (map.containsKey(PropertyHelper.getPropertyId("HostRoles", "cluster_name"))) {
+ String query = request.getBody().getRequestInfoProperties().get("query");
+ operation = String.valueOf(map.get(PropertyHelper.getPropertyId("HostRoles", "state"))) + ": " + query.substring(query.indexOf("(") + 1, query.length() - 1)
+ + " on " + request.getBody().getRequestInfoProperties().get("operation_level/host_names")
+ + " (" + request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_CLUSTER_ID) + ")";
+ break;
+ }
+ }
+ break;
+ case "HOST_COMPONENT":
+ for (Map<String, Object> map : request.getBody().getPropertySets()) {
+ if (map.containsKey(PropertyHelper.getPropertyId("HostRoles", "component_name"))) {
+ operation = String.valueOf(map.get(PropertyHelper.getPropertyId("HostRoles", "state"))) + ": " + String.valueOf(map.get(PropertyHelper.getPropertyId("HostRoles", "component_name")))
+ + "/" + request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_SERVICE_ID)
+ + " on " + request.getBody().getRequestInfoProperties().get("operation_level/host_name")
+ + " (" + request.getBody().getRequestInfoProperties().get(RequestOperationLevel.OPERATION_CLUSTER_ID) + ")";
+ break;
+ }
+ }
+ break;
+ }
+ return operation;
+ }
+
+ for (Map<String, Object> map : request.getBody().getPropertySets()) {
+ if (map.containsKey(PropertyHelper.getPropertyId("HostRoles", "maintenance_state"))) {
+ return "Turn " + map.get(PropertyHelper.getPropertyId("HostRoles", "maintenance_state")) + " Maintenance Mode for " + map.get(PropertyHelper.getPropertyId("HostRoles", "component_name"));
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns request id from the result
+ * @param result
+ * @return
+ */
+ private Long getRequestId(Result result) {
+ return (Long) result.getResultTree().getChild("request").getObject().getPropertiesMap().get("Requests").get("id");
+ }
+
+ /**
+ * Checks if request id can be found in the result
+ * @param result
+ * @return
+ */
+ private boolean containsRequestId(Result result) {
+ return result.getResultTree().getChild("request") != null
+ && result.getResultTree().getChild("request").getObject() != null
+ && result.getResultTree().getChild("request").getObject().getPropertiesMap().get("Requests") != null
+ && result.getResultTree().getChild("request").getObject().getPropertiesMap().get("Requests").get("id") != null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ConfigurationChangeEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ConfigurationChangeEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ConfigurationChangeEventCreator.java
new file mode 100644
index 0000000..7e58893
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/ConfigurationChangeEventCreator.java
@@ -0,0 +1,150 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.ClusterNameChangeRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.ConfigurationChangeRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles operation requests (start, stop, install, etc)
+ * For resource type {@link Resource.Type#HostComponent}
+ * and request types {@link Request.Type#POST}, {@link Request.Type#PUT} and {@link Request.Type#DELETE}
+ */
+public class ConfigurationChangeEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.PUT, Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Cluster).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ if (!request.getBody().getPropertySets().isEmpty()) {
+ Map<String, Object> map = request.getBody().getPropertySets().iterator().next();
+ if (map.size() == 1 && map.containsKey(PropertyHelper.getPropertyId("Clusters", "cluster_name"))) {
+ String newName = String.valueOf(map.get(PropertyHelper.getPropertyId("Clusters", "cluster_name")));
+ String oldName = request.getResource().getKeyValueMap().get(Resource.Type.Cluster);
+ return ClusterNameChangeRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withOldName(oldName)
+ .withNewName(newName)
+ .build();
+ }
+ }
+
+ return ConfigurationChangeRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withVersionNote(getServiceConfigVersionNote(result))
+ .withVersionNumber(getServiceConfigVersion(result))
+ .build();
+ }
+
+ /**
+ * Returns service configuration version from the result
+ * @param result
+ * @return
+ */
+ private String getServiceConfigVersion(Result result) {
+ Map<String, Object> map = getServiceConfigMap(result);
+ return map == null ? null : String.valueOf(map.get("service_config_version"));
+ }
+
+ /**
+ * Returns service configurationv ersion note from the result
+ * @param result
+ * @return
+ */
+ private String getServiceConfigVersionNote(Result result) {
+ Map<String, Object> map = getServiceConfigMap(result);
+ return map == null ? null : String.valueOf(map.get("service_config_version_note"));
+ }
+
+ /**
+ * Returns service configuration map from the result. This map contains version number and version info
+ * @param result
+ * @return
+ */
+ private Map<String, Object> getServiceConfigMap(Result result) {
+ if (result.getResultTree().getChild("resources") != null &&
+ !result.getResultTree().getChild("resources").getChildren().isEmpty() &&
+ result.getResultTree().getChild("resources").getChildren().iterator().next().getObject() != null) {
+ return result.getResultTree().getChild("resources").getChildren().iterator().next().getObject().getPropertiesMap().get("");
+ }
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/CredentialEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/CredentialEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/CredentialEventCreator.java
new file mode 100644
index 0000000..3b1f462
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/CredentialEventCreator.java
@@ -0,0 +1,111 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.AddCredentialRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles crednetial requests
+ * For resource type {@link Resource.Type#Upgrade}
+ * and request types {@link Request.Type#POST}
+ */
+public class CredentialEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.POST).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Credential).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ return AddCredentialRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withClusterName(getProperty(request, "cluster_name"))
+ .withType(getProperty(request, "type"))
+ .withAlias(getProperty(request, "alias"))
+ .withPrincipal(getProperty(request, "principal"))
+ .build();
+
+ }
+
+ /**
+ * Returns a property from the resquest
+ * @param request
+ * @param propertyName
+ * @return
+ */
+ private String getProperty(Request request, String propertyName) {
+ if (!request.getBody().getPropertySets().isEmpty()) {
+ return String.valueOf(request.getBody().getPropertySets().iterator().next().get(PropertyHelper.getPropertyId("Credential", propertyName)));
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/DefaultEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/DefaultEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/DefaultEventCreator.java
new file mode 100644
index 0000000..d0f57f2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/DefaultEventCreator.java
@@ -0,0 +1,89 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.audit.request.RequestAuditLogger;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Default creator for {@link RequestAuditLogger}
+ */
+public class DefaultEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link org.apache.ambari.server.api.services.Request.Type}s that are handled by this plugin
+ * In this case all {@link Request.Type}s are listed, except {@link Request.Type#GET}
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().addAll(EnumSet.complementOf(EnumSet.of(Request.Type.GET))).build();
+
+
+ /** {@inheritDoc} */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ // null makes this default
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ // null makes this default
+ return null;
+ }
+
+ /**
+ * Creates a simple {@link AuditEvent} with the details of request and response
+ * @param request HTTP request object
+ * @param result HTTP result object
+ * @return
+ */
+ @Override
+ public AuditEvent createAuditEvent(final Request request, final Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ return RequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withUserName(username)
+ .withRemoteIp(request.getRemoteAddress())
+ .withRequestType(request.getRequestType())
+ .withUrl(request.getURI())
+ .withResultStatus(result.getStatus())
+ .build();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/GroupEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/GroupEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/GroupEventCreator.java
new file mode 100644
index 0000000..d926d94
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/GroupEventCreator.java
@@ -0,0 +1,124 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.CreateGroupRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.DeleteGroupRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles group requests
+ * For resource type {@link Resource.Type#Group}
+ * and request types {@link Request.Type#POST} and {@link Request.Type#DELETE}
+ */
+public class GroupEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Group).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ switch (request.getRequestType()) {
+ case POST:
+ return CreateGroupRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withGroupName(getGroupName(request))
+ .build();
+ case DELETE:
+ return DeleteGroupRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withGroupName(request.getResource().getKeyValueMap().get(Resource.Type.Group))
+ .build();
+ default:
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Returns group name from request
+ * @param request
+ * @return
+ */
+ private String getGroupName(Request request) {
+ if (!request.getBody().getPropertySets().isEmpty()) {
+ return String.valueOf(request.getBody().getPropertySets().iterator().next().get(PropertyHelper.getPropertyId("Groups", "group_name")));
+ }
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/HostEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/HostEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/HostEventCreator.java
new file mode 100644
index 0000000..910280d
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/HostEventCreator.java
@@ -0,0 +1,167 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.AddComponentToHostRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.AddHostRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.DeleteHostRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles host requests (add, delete, add component)
+ * For resource type {@link Resource.Type#HostComponent}
+ * and request types {@link Request.Type#POST}, {@link Request.Type#DELETE} and {@link Request.Type#QUERY_POST}
+ */
+public class HostEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.QUERY_POST, Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Host).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ // null makes this default
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ switch (request.getRequestType()) {
+ case DELETE:
+ return DeleteHostRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withHostName(request.getResource().getKeyValueMap().get(Resource.Type.Host))
+ .build();
+ case POST:
+ return AddHostRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withHostName(getHostName(request))
+ .build();
+ case QUERY_POST:
+ return AddComponentToHostRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withHostName(getHostNameFromQuery(request))
+ .withComponent(getHostComponent(request))
+ .build();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns hostname from the request
+ * @param request
+ * @return
+ */
+ private String getHostName(Request request) {
+ if (!request.getBody().getNamedPropertySets().isEmpty()) {
+ return String.valueOf(request.getBody().getNamedPropertySets().iterator().next().getProperties().get(PropertyHelper.getPropertyId("Hosts", "host_name")));
+ }
+ return null;
+ }
+
+ /**
+ * Returns component name from the request
+ * @param request
+ * @return
+ */
+ private String getHostComponent(Request request) {
+ if (!request.getBody().getNamedPropertySets().isEmpty()) {
+ Set<Map<String, String>> set = (Set<Map<String, String>>) request.getBody().getNamedPropertySets().iterator().next().getProperties().get("host_components");
+ if (set != null && !set.isEmpty()) {
+ return set.iterator().next().get(PropertyHelper.getPropertyId("HostRoles", "component_name"));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns hostname from the query string of the request
+ * @param request
+ * @return
+ */
+ private String getHostNameFromQuery(Request request) {
+ final String key = PropertyHelper.getPropertyId("Hosts", "host_name");
+ if (request.getBody().getQueryString().contains(key)) {
+ String q = request.getBody().getQueryString();
+ int startIndex = q.indexOf(key) + key.length() + 1;
+ int endIndex = q.indexOf("&", startIndex) == -1 ? q.length() : q.indexOf("&", startIndex);
+ return q.substring(startIndex, endIndex);
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/MemberEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/MemberEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/MemberEventCreator.java
new file mode 100644
index 0000000..a3c3164
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/MemberEventCreator.java
@@ -0,0 +1,175 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.AddUserToGroupRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.MembershipChangeRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.RemoveUserFromGroupRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles member requests
+ * For resource type {@link Resource.Type#Member}
+ * and request types {@link Request.Type#POST}, {@link Request.Type#PUT} and {@link Request.Type#DELETE}
+ */
+public class MemberEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.PUT, Request.Type.POST, Request.Type.DELETE).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.Member).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ switch (request.getRequestType()) {
+ case POST:
+ return AddUserToGroupRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withAffectedUserName(getUserName(request))
+ .withGroupName(getGroupName(request))
+ .build();
+ case DELETE:
+ return RemoveUserFromGroupRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withAffectedUserName(getUserName(request))
+ .withGroupName(getGroupName(request))
+ .build();
+ case PUT:
+ return MembershipChangeRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withGroupName(getGroupNameForPut(request))
+ .withUserNameList(getUsers(request))
+ .build();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns users from the request
+ * @param request
+ * @return
+ */
+ private List<String> getUsers(Request request) {
+ List<String> users = new LinkedList<String>();
+
+ for (Map<String, Object> propertyMap : request.getBody().getPropertySets()) {
+ String userName = String.valueOf(propertyMap.get(PropertyHelper.getPropertyId("MemberInfo", "user_name")));
+ users.add(userName);
+ }
+ return users;
+ }
+
+ /**
+ * Returns target group name from the request. This is called when PUT request type is used.
+ * @param request
+ * @return
+ */
+ private String getGroupNameForPut(Request request) {
+
+ for (Map<String, Object> propertyMap : request.getBody().getPropertySets()) {
+ return String.valueOf(propertyMap.get(PropertyHelper.getPropertyId("MemberInfo", "group_name")));
+ }
+ return null;
+ }
+
+ /**
+ * Returns username from the request
+ * @param request
+ * @return
+ */
+ private String getUserName(Request request) {
+ return request.getResource().getKeyValueMap().get(Resource.Type.Member);
+ }
+
+ /**
+ * Returns groupname from the request
+ * @param request
+ * @return
+ */
+ private String getGroupName(Request request) {
+ return request.getResource().getKeyValueMap().get(Resource.Type.Group);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/PrivilegeEventCreator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/PrivilegeEventCreator.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/PrivilegeEventCreator.java
new file mode 100644
index 0000000..bdc7b59
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/request/eventcreator/PrivilegeEventCreator.java
@@ -0,0 +1,147 @@
+/*
+ * 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.ambari.server.audit.request.eventcreator;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.request.ClusterPrivilegeChangeRequestAuditEvent;
+import org.apache.ambari.server.audit.event.request.PrivilegeChangeRequestAuditEvent;
+import org.apache.ambari.server.audit.request.RequestAuditEventCreator;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This creator handles privilege requests
+ * For resource type {@link Resource.Type#ClusterPrivilege}
+ * and request types {@link Request.Type#POST}, {@link Request.Type#PUT}
+ */
+public class PrivilegeEventCreator implements RequestAuditEventCreator {
+
+ /**
+ * Set of {@link Request.Type}s that are handled by this plugin
+ */
+ private Set<Request.Type> requestTypes = ImmutableSet.<Request.Type>builder().add(Request.Type.PUT, Request.Type.POST).build();
+
+ /**
+ * Set of {@link Resource.Type}s that are handled by this plugin
+ */
+ private Set<Resource.Type> resourceTypes = ImmutableSet.<Resource.Type>builder().add(Resource.Type.ClusterPrivilege).build();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Request.Type> getRequestTypes() {
+ return requestTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Resource.Type> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<ResultStatus.STATUS> getResultStatuses() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AuditEvent createAuditEvent(Request request, Result result) {
+ String username = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
+
+ Map<String, List<String>> users = getEntities(request, "USER");
+ Map<String, List<String>> groups = getEntities(request, "GROUP");
+
+ switch (request.getRequestType()) {
+ case PUT:
+ return ClusterPrivilegeChangeRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withUsers(users)
+ .withGroups(groups)
+ .build();
+ case POST:
+ String role = users.isEmpty() ? (groups.isEmpty() ? null : groups.keySet().iterator().next()) : users.keySet().iterator().next();
+ return PrivilegeChangeRequestAuditEvent.builder()
+ .withTimestamp(System.currentTimeMillis())
+ .withRequestType(request.getRequestType())
+ .withResultStatus(result.getStatus())
+ .withUrl(request.getURI())
+ .withRemoteIp(request.getRemoteAddress())
+ .withUserName(username)
+ .withRole(role)
+ .withGroup(groups.get(role) == null ? null : groups.get(role).get(0))
+ .withUser(users.get(role) == null ? null : users.get(role).get(0))
+ .withOperation((users.isEmpty() ? (groups.isEmpty() ? "" : "Group ") : "User ") + "role change")
+ .build();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Assembles entities from the request. The result can contain users or groups based on the value of type parameter
+ * @param request
+ * @param type
+ * @return a map of role -> [user|group] names
+ */
+ private Map<String, List<String>> getEntities(final Request request, final String type) {
+ Map<String, List<String>> entities = new HashMap<String, List<String>>();
+
+ for (Map<String, Object> propertyMap : request.getBody().getPropertySets()) {
+ String ptype = String.valueOf(propertyMap.get(PropertyHelper.getPropertyId("PrivilegeInfo", "principal_type")));
+ if (type.equals(ptype)) {
+ String role = String.valueOf(propertyMap.get(PropertyHelper.getPropertyId("PrivilegeInfo", "permission_name")));
+ String name = String.valueOf(propertyMap.get(PropertyHelper.getPropertyId("PrivilegeInfo", "principal_name")));
+ if (!entities.containsKey(role)) {
+ entities.put(role, new LinkedList<String>());
+ }
+
+ entities.get(role).add(name);
+ }
+ }
+ return entities;
+ }
+
+}