You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by of...@apache.org on 2020/05/04 14:45:46 UTC

[incubator-dlab] branch audit created (now e688b73)

This is an automated email from the ASF dual-hosted git repository.

ofuks pushed a change to branch audit
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git.


      at e688b73  Added audit support for projects

This branch includes the following new commits:

     new e688b73  Added audit support for projects

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org


[incubator-dlab] 01/01: Added audit support for projects

Posted by of...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ofuks pushed a commit to branch audit
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git

commit e688b732aa1f87bbff80544ca44b0045a9e951c9
Author: Oleh Fuks <ol...@gmail.com>
AuthorDate: Mon May 4 17:44:34 2020 +0300

    Added audit support for projects
---
 services/self-service/self-service.yml             |   6 +-
 .../com/epam/dlab/backendapi/annotation/Audit.java |  33 +++++++
 .../com/epam/dlab/backendapi/annotation/Info.java  |  30 +++++++
 .../dlab/backendapi/annotation/ResourceName.java   |  30 +++++++
 .../conf/SelfServiceApplicationConfiguration.java  |   7 ++
 .../com/epam/dlab/backendapi/dao/AuditDAO.java     |  26 ++++++
 .../com/epam/dlab/backendapi/dao/AuditDAOImpl.java |  31 +++++++
 .../java/com/epam/dlab/backendapi/dao/BaseDAO.java |   2 +
 .../dlab/backendapi/domain/AuditActionEnum.java    |  24 +++++
 .../AuditCreateDTO.java}                           |  42 ++-------
 .../com/epam/dlab/backendapi/domain/AuditDTO.java  |  32 +++++++
 .../backendapi/interceptor/AuditInterceptor.java   | 100 +++++++++++++++++++++
 .../backendapi/modules/CloudProviderModule.java    |  11 ++-
 .../epam/dlab/backendapi/modules/DevModule.java    |   6 ++
 .../dlab/backendapi/modules/ProductionModule.java  |   6 ++
 .../dlab/backendapi/resources/ProjectResource.java |   8 +-
 .../resources/dto/HealthStatusPageDTO.java         |   2 +
 .../epam/dlab/backendapi/service/AuditService.java |  26 ++++++
 .../dlab/backendapi/service/ProjectService.java    |   5 +-
 .../backendapi/service/impl/AuditServiceImpl.java  |  39 ++++++++
 .../impl/InfrastructureInfoServiceImpl.java        |   1 +
 .../service/impl/ProjectServiceImpl.java           |  95 ++++++++++++++++----
 22 files changed, 499 insertions(+), 63 deletions(-)

diff --git a/services/self-service/self-service.yml b/services/self-service/self-service.yml
index df92c25..a5e2c15 100644
--- a/services/self-service/self-service.yml
+++ b/services/self-service/self-service.yml
@@ -50,8 +50,10 @@ roleDefaultAccess: true
 
 # Set to true to enable the scheduler of billing report.
 billingSchedulerEnabled: false
-# Name of configuration file for billing report.
-<#if DEV_MODE == "true">
+# Set to true to enable audit
+auditEnabled: true
+  # Name of configuration file for billing report.
+  <#if DEV_MODE == "true">
 billingConfFile: ${sys['user.dir']}/../billing/billing.yml
 <#else>
 billingConfFile: ${DLAB_CONF_DIR}/billing.yml
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/Audit.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/Audit.java
new file mode 100644
index 0000000..d6bd5e9
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/Audit.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.epam.dlab.backendapi.annotation;
+
+import com.epam.dlab.backendapi.domain.AuditActionEnum;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Audit {
+    AuditActionEnum action();
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/Info.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/Info.java
new file mode 100644
index 0000000..44ca32b
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/Info.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.epam.dlab.backendapi.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Info {
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/ResourceName.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/ResourceName.java
new file mode 100644
index 0000000..100df0d
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/annotation/ResourceName.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.epam.dlab.backendapi.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ResourceName {
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/conf/SelfServiceApplicationConfiguration.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/conf/SelfServiceApplicationConfiguration.java
index aaface6..7d0e874 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/conf/SelfServiceApplicationConfiguration.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/conf/SelfServiceApplicationConfiguration.java
@@ -78,6 +78,9 @@ public class SelfServiceApplicationConfiguration extends ServiceConfiguration {
 	@JsonProperty
 	private boolean billingSchedulerEnabled = false;
 
+	@JsonProperty
+	private boolean auditEnabled = false;
+
 	@NotEmpty
 	@JsonProperty
 	private String billingConfFile;
@@ -202,6 +205,10 @@ public class SelfServiceApplicationConfiguration extends ServiceConfiguration {
 		return billingSchedulerEnabled;
 	}
 
+	public boolean isAuditEnabled() {
+		return auditEnabled;
+	}
+
 	/**
 	 * Return the default access to DLab features using roles policy.
 	 */
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/AuditDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/AuditDAO.java
new file mode 100644
index 0000000..b9a7727
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/AuditDAO.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.epam.dlab.backendapi.dao;
+
+import com.epam.dlab.backendapi.domain.AuditCreateDTO;
+
+public interface AuditDAO {
+    void save(AuditCreateDTO audit);
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/AuditDAOImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/AuditDAOImpl.java
new file mode 100644
index 0000000..2de30aa
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/AuditDAOImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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 com.epam.dlab.backendapi.dao;
+
+import com.epam.dlab.backendapi.domain.AuditCreateDTO;
+
+public class AuditDAOImpl extends BaseDAO implements AuditDAO {
+    private final static String AUDIT_COLLECTION = "audit";
+
+    @Override
+    public void save(AuditCreateDTO audit) {
+        insertOne(AUDIT_COLLECTION, audit);
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java
index c2ff69b..96532a5 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java
@@ -28,6 +28,7 @@ import com.epam.dlab.util.mongo.modules.MongoModule;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
 import com.google.inject.Inject;
 import com.mongodb.BasicDBObject;
 import com.mongodb.MongoException;
@@ -69,6 +70,7 @@ public class BaseDAO {
 
 	private static final ObjectMapper MAPPER = new ObjectMapper()
 			.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true)
+			.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
 			.registerModule(new IsoDateModule())
 			.registerModule(new JavaPrimitiveModule())
 			.registerModule(new MongoModule());
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditActionEnum.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditActionEnum.java
new file mode 100644
index 0000000..7a02a79
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditActionEnum.java
@@ -0,0 +1,24 @@
+/*
+ * 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 com.epam.dlab.backendapi.domain;
+
+public enum AuditActionEnum {
+    CREATE_PROJECT, START_PROJECT, STOP_PROJECT, TERMINATE_PROJECT, UPDATE_PROJECT
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditCreateDTO.java
similarity index 51%
copy from services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
copy to services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditCreateDTO.java
index 4eb0b3b..de40808 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditCreateDTO.java
@@ -17,45 +17,19 @@
  * under the License.
  */
 
-package com.epam.dlab.backendapi.resources.dto;
+package com.epam.dlab.backendapi.domain;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Builder;
 import lombok.Data;
 
 import java.util.List;
 
-/**
- * Stores the health statuses for environment resources.
- */
+
 @Data
 @Builder
-public class HealthStatusPageDTO {
-	@JsonProperty
-	private String status;
-	@JsonProperty("list_resources")
-	private List<HealthStatusResource> listResources;
-	@JsonProperty
-	private boolean billingEnabled;
-	@JsonProperty
-	private boolean admin;
-	@JsonProperty
-	private boolean projectAdmin;
-	@JsonProperty
-	private int billingQuoteUsed;
-	@JsonProperty
-	private int billingUserQuoteUsed;
-	@JsonProperty
-	private boolean projectAssigned;
-	@JsonProperty
-	private BucketBrowser bucketBrowser;
-
-	@Builder
-	@Data
-	public static class BucketBrowser {
-		private final boolean view;
-		private final boolean upload;
-		private final boolean download;
-		private final boolean delete;
-	}
-}
\ No newline at end of file
+public class AuditCreateDTO {
+    private final String user;
+    private final AuditActionEnum action;
+    private final String resourceName;
+    private final List<String> info;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditDTO.java
new file mode 100644
index 0000000..2a337e5
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/AuditDTO.java
@@ -0,0 +1,32 @@
+/*
+ * 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 com.epam.dlab.backendapi.domain;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class AuditDTO {
+    private final String user;
+    private final AuditActionEnum action;
+    private final String resourceName;
+    private final Date date;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/AuditInterceptor.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/AuditInterceptor.java
new file mode 100644
index 0000000..20a42d7
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/AuditInterceptor.java
@@ -0,0 +1,100 @@
+/*
+ * 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 com.epam.dlab.backendapi.interceptor;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.annotation.Audit;
+import com.epam.dlab.backendapi.annotation.Info;
+import com.epam.dlab.backendapi.annotation.ResourceName;
+import com.epam.dlab.backendapi.annotation.User;
+import com.epam.dlab.backendapi.domain.AuditActionEnum;
+import com.epam.dlab.backendapi.domain.AuditCreateDTO;
+import com.epam.dlab.backendapi.service.AuditService;
+import com.epam.dlab.exceptions.DlabException;
+import com.google.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+@Slf4j
+public class AuditInterceptor implements MethodInterceptor {
+    @Inject
+    private AuditService auditService;
+
+    @Override
+    public Object invoke(MethodInvocation mi) throws Throwable {
+        Method method = mi.getMethod();
+        final Parameter[] parameters = mi.getMethod().getParameters();
+        final String user = getUserInfo(mi, parameters);
+        final AuditActionEnum action = getAuditActionEnum(method);
+        final String resourceName = getResourceName(mi, parameters);
+        final List<String> infoMap = getInfo(mi, parameters);
+
+        AuditCreateDTO auditCreateDTO = AuditCreateDTO.builder()
+                .user(user)
+                .action(action)
+                .resourceName(resourceName)
+                .info(infoMap)
+                .build();
+        auditService.save(auditCreateDTO);
+        return mi.proceed();
+    }
+
+    private String getUserInfo(MethodInvocation mi, Parameter[] parameters) {
+        return IntStream.range(0, parameters.length)
+                .filter(i -> Objects.nonNull(parameters[i].getAnnotation(User.class)))
+                .mapToObj(i -> ((UserInfo) mi.getArguments()[i]).getName())
+                .findAny()
+                .orElseThrow(() -> new DlabException("UserInfo parameter wanted!"));
+    }
+
+    private AuditActionEnum getAuditActionEnum(Method method) {
+        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
+        return IntStream.range(0, method.getDeclaredAnnotations().length)
+                .filter(i -> declaredAnnotations[i] instanceof Audit)
+                .mapToObj(i -> ((Audit) declaredAnnotations[i]).action())
+                .findAny()
+                .orElseThrow(() -> new DlabException("'Audit' annotation wanted!"));
+    }
+
+    private String getResourceName(MethodInvocation mi, Parameter[] parameters) {
+        return IntStream.range(0, parameters.length)
+                .filter(i -> Objects.nonNull(parameters[i].getAnnotation(ResourceName.class)))
+                .mapToObj(i -> (String) mi.getArguments()[i])
+                .findAny()
+                .orElseThrow(() -> new DlabException("Resource name parameter wanted!"));
+    }
+
+    private List<String> getInfo(MethodInvocation mi, Parameter[] parameters) {
+        return IntStream.range(0, parameters.length)
+                .filter(i -> Objects.nonNull(parameters[i].getAnnotation(Info.class)))
+                .mapToObj(i -> (List<String>) mi.getArguments()[i])
+                .findAny()
+                .orElseGet(Collections::emptyList);
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/CloudProviderModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/CloudProviderModule.java
index f75f877..f49c98d 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/CloudProviderModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/CloudProviderModule.java
@@ -20,9 +20,11 @@
 package com.epam.dlab.backendapi.modules;
 
 import com.epam.dlab.backendapi.SelfServiceApplication;
+import com.epam.dlab.backendapi.annotation.Audit;
 import com.epam.dlab.backendapi.annotation.BudgetLimited;
 import com.epam.dlab.backendapi.annotation.ProjectAdmin;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
+import com.epam.dlab.backendapi.interceptor.AuditInterceptor;
 import com.epam.dlab.backendapi.interceptor.BudgetLimitInterceptor;
 import com.epam.dlab.backendapi.interceptor.ProjectAdminInterceptor;
 import com.epam.dlab.backendapi.resources.BillingResource;
@@ -72,11 +74,16 @@ public class CloudProviderModule extends CloudModule {
                 new SchedulerConfiguration(SelfServiceApplication.class.getPackage().getName()));
 
         final BudgetLimitInterceptor budgetLimitInterceptor = new BudgetLimitInterceptor();
-        final ProjectAdminInterceptor projectAdminInterceptor = new ProjectAdminInterceptor();
         requestInjection(budgetLimitInterceptor);
-        requestInjection(projectAdminInterceptor);
         bindInterceptor(any(), annotatedWith(BudgetLimited.class), budgetLimitInterceptor);
+        final ProjectAdminInterceptor projectAdminInterceptor = new ProjectAdminInterceptor();
+        requestInjection(projectAdminInterceptor);
         bindInterceptor(any(), annotatedWith(ProjectAdmin.class), projectAdminInterceptor);
+        if (configuration.isAuditEnabled()) {
+            final AuditInterceptor auditInterceptor = new AuditInterceptor();
+            requestInjection(auditInterceptor);
+            bindInterceptor(any(), annotatedWith(Audit.class), auditInterceptor);
+        }
     }
 
     @Override
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
index 31b4056..5c8f9bf 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
@@ -23,6 +23,8 @@ import com.epam.dlab.ModuleBase;
 import com.epam.dlab.auth.contract.SecurityAPI;
 import com.epam.dlab.backendapi.auth.SelfServiceSecurityAuthorizer;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
+import com.epam.dlab.backendapi.dao.AuditDAO;
+import com.epam.dlab.backendapi.dao.AuditDAOImpl;
 import com.epam.dlab.backendapi.dao.BackupDao;
 import com.epam.dlab.backendapi.dao.BackupDaoImpl;
 import com.epam.dlab.backendapi.dao.BaseBillingDAO;
@@ -40,6 +42,7 @@ import com.epam.dlab.backendapi.dao.UserRoleDaoImpl;
 import com.epam.dlab.backendapi.service.AccessKeyService;
 import com.epam.dlab.backendapi.service.ApplicationSettingService;
 import com.epam.dlab.backendapi.service.ApplicationSettingServiceImpl;
+import com.epam.dlab.backendapi.service.AuditService;
 import com.epam.dlab.backendapi.service.BackupService;
 import com.epam.dlab.backendapi.service.BucketService;
 import com.epam.dlab.backendapi.service.ComputationalService;
@@ -68,6 +71,7 @@ import com.epam.dlab.backendapi.service.UserRoleServiceImpl;
 import com.epam.dlab.backendapi.service.UserSettingService;
 import com.epam.dlab.backendapi.service.UserSettingServiceImpl;
 import com.epam.dlab.backendapi.service.impl.AccessKeyServiceImpl;
+import com.epam.dlab.backendapi.service.impl.AuditServiceImpl;
 import com.epam.dlab.backendapi.service.impl.BackupServiceImpl;
 import com.epam.dlab.backendapi.service.impl.BucketServiceImpl;
 import com.epam.dlab.backendapi.service.impl.ComputationalServiceImpl;
@@ -170,8 +174,10 @@ public class DevModule extends ModuleBase<SelfServiceApplicationConfiguration> i
 		bind(EndpointService.class).to(EndpointServiceImpl.class);
 		bind(EndpointDAO.class).to(EndpointDAOImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
+		bind(AuditService.class).to(AuditServiceImpl.class);
 		bind(ProjectDAO.class).to(ProjectDAOImpl.class);
 		bind(BillingDAO.class).to(BaseBillingDAO.class);
+		bind(AuditDAO.class).to(AuditDAOImpl.class);
 		bind(BucketService.class).to(BucketServiceImpl.class);
 	}
 
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
index 5fb314d..901a61f 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
@@ -22,6 +22,8 @@ package com.epam.dlab.backendapi.modules;
 import com.epam.dlab.ModuleBase;
 import com.epam.dlab.backendapi.auth.SelfServiceSecurityAuthorizer;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
+import com.epam.dlab.backendapi.dao.AuditDAO;
+import com.epam.dlab.backendapi.dao.AuditDAOImpl;
 import com.epam.dlab.backendapi.dao.BackupDao;
 import com.epam.dlab.backendapi.dao.BackupDaoImpl;
 import com.epam.dlab.backendapi.dao.BaseBillingDAO;
@@ -39,6 +41,7 @@ import com.epam.dlab.backendapi.dao.UserRoleDaoImpl;
 import com.epam.dlab.backendapi.service.AccessKeyService;
 import com.epam.dlab.backendapi.service.ApplicationSettingService;
 import com.epam.dlab.backendapi.service.ApplicationSettingServiceImpl;
+import com.epam.dlab.backendapi.service.AuditService;
 import com.epam.dlab.backendapi.service.BackupService;
 import com.epam.dlab.backendapi.service.BucketService;
 import com.epam.dlab.backendapi.service.ComputationalService;
@@ -67,6 +70,7 @@ import com.epam.dlab.backendapi.service.UserRoleServiceImpl;
 import com.epam.dlab.backendapi.service.UserSettingService;
 import com.epam.dlab.backendapi.service.UserSettingServiceImpl;
 import com.epam.dlab.backendapi.service.impl.AccessKeyServiceImpl;
+import com.epam.dlab.backendapi.service.impl.AuditServiceImpl;
 import com.epam.dlab.backendapi.service.impl.BackupServiceImpl;
 import com.epam.dlab.backendapi.service.impl.BucketServiceImpl;
 import com.epam.dlab.backendapi.service.impl.ComputationalServiceImpl;
@@ -159,8 +163,10 @@ public class ProductionModule extends ModuleBase<SelfServiceApplicationConfigura
 		bind(EndpointService.class).to(EndpointServiceImpl.class);
 		bind(EndpointDAO.class).to(EndpointDAOImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
+		bind(AuditService.class).to(AuditServiceImpl.class);
 		bind(ProjectDAO.class).to(ProjectDAOImpl.class);
 		bind(BillingDAO.class).to(BaseBillingDAO.class);
+		bind(AuditDAO.class).to(AuditDAOImpl.class);
 		bind(BucketService.class).to(BucketServiceImpl.class);
 		bind(TagService.class).to(TagServiceImpl.class);
 		bind(SecurityService.class).to(SecurityServiceImpl.class);
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ProjectResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ProjectResource.java
index 7b26d73..6d93e89 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ProjectResource.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ProjectResource.java
@@ -76,7 +76,7 @@ public class ProjectResource {
 		projectService.create(userInfo, new ProjectDTO(projectDTO.getName(), projectDTO.getGroups(),
 				projectDTO.getKey(), projectDTO.getTag(), null,
 				projectDTO.getEndpoints().stream().map(e -> new ProjectEndpointDTO(e, UserInstanceStatus.CREATING,
-						null)).collect(Collectors.toList()), projectDTO.isSharedImageEnabled()));
+						null)).collect(Collectors.toList()), projectDTO.isSharedImageEnabled()), projectDTO.getName());
 		final URI uri = uriInfo.getRequestUriBuilder().path(projectDTO.getName()).build();
 		return Response
 				.ok()
@@ -222,11 +222,7 @@ public class ProjectResource {
 			@Parameter(hidden = true) @Auth UserInfo userInfo,
 			@Parameter(description = "Project name")
 					List<UpdateProjectBudgetDTO> dtos) {
-		final List<ProjectDTO> projects = dtos
-				.stream()
-				.map(dto -> ProjectDTO.builder().name(dto.getProject()).budget(dto.getBudget()).build())
-				.collect(Collectors.toList());
-		projectService.updateBudget(projects);
+		projectService.updateBudget(userInfo, dtos);
 		return Response.ok().build();
 	}
 
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
index 4eb0b3b..642dd06 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
@@ -38,6 +38,8 @@ public class HealthStatusPageDTO {
 	@JsonProperty
 	private boolean billingEnabled;
 	@JsonProperty
+	private boolean auditEnabled;
+	@JsonProperty
 	private boolean admin;
 	@JsonProperty
 	private boolean projectAdmin;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/AuditService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/AuditService.java
new file mode 100644
index 0000000..b7f4fce
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/AuditService.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.epam.dlab.backendapi.service;
+
+import com.epam.dlab.backendapi.domain.AuditCreateDTO;
+
+public interface AuditService {
+    void save(AuditCreateDTO audit);
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/ProjectService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/ProjectService.java
index fa0aedc..953caef 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/ProjectService.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/ProjectService.java
@@ -2,6 +2,7 @@ package com.epam.dlab.backendapi.service;
 
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
+import com.epam.dlab.backendapi.domain.UpdateProjectBudgetDTO;
 import com.epam.dlab.backendapi.domain.UpdateProjectDTO;
 
 import java.util.List;
@@ -15,7 +16,7 @@ public interface ProjectService {
 
 	List<ProjectDTO> getProjectsByEndpoint(String endpointName);
 
-	void create(UserInfo userInfo, ProjectDTO projectDTO);
+	void create(UserInfo userInfo, ProjectDTO projectDTO, String resourceName);
 
 	ProjectDTO get(String name);
 
@@ -33,7 +34,7 @@ public interface ProjectService {
 
 	void update(UserInfo userInfo, UpdateProjectDTO projectDTO, String projectName);
 
-	void updateBudget(List<ProjectDTO> projects);
+	void updateBudget(UserInfo userInfo, List<UpdateProjectBudgetDTO> projects);
 
 	boolean isAnyProjectAssigned(UserInfo userInfo);
 
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/AuditServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/AuditServiceImpl.java
new file mode 100644
index 0000000..d73460b
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/AuditServiceImpl.java
@@ -0,0 +1,39 @@
+/*
+ * 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 com.epam.dlab.backendapi.service.impl;
+
+import com.epam.dlab.backendapi.dao.AuditDAO;
+import com.epam.dlab.backendapi.domain.AuditCreateDTO;
+import com.epam.dlab.backendapi.service.AuditService;
+import com.google.inject.Inject;
+
+public class AuditServiceImpl implements AuditService {
+    private final AuditDAO auditDAO;
+
+    @Inject
+    public AuditServiceImpl(AuditDAO auditDAO) {
+        this.auditDAO = auditDAO;
+    }
+
+    @Override
+    public void save(AuditCreateDTO audit) {
+        auditDAO.save(audit);
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
index 4063ca1..08ce7b9 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
@@ -133,6 +133,7 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 					.status(HealthStatusEnum.OK.toString())
 					.listResources(Collections.emptyList())
 					.billingEnabled(configuration.isBillingSchedulerEnabled())
+					.auditEnabled(configuration.isAuditEnabled())
 					.projectAdmin(UserRoles.isProjectAdmin(userInfo))
 					.admin(UserRoles.isAdmin(userInfo))
 					.projectAssigned(projectService.isAnyProjectAssigned(userInfo))
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java
index 0d1f5ed..a12501f 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java
@@ -1,10 +1,14 @@
 package com.epam.dlab.backendapi.service.impl;
 
 import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.annotation.Audit;
 import com.epam.dlab.backendapi.annotation.BudgetLimited;
+import com.epam.dlab.backendapi.annotation.Info;
 import com.epam.dlab.backendapi.annotation.Project;
 import com.epam.dlab.backendapi.annotation.ProjectAdmin;
+import com.epam.dlab.backendapi.annotation.ResourceName;
 import com.epam.dlab.backendapi.annotation.User;
+import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
 import com.epam.dlab.backendapi.dao.ExploratoryDAO;
 import com.epam.dlab.backendapi.dao.ProjectDAO;
 import com.epam.dlab.backendapi.dao.UserGroupDao;
@@ -12,12 +16,12 @@ import com.epam.dlab.backendapi.domain.EndpointDTO;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.domain.ProjectEndpointDTO;
 import com.epam.dlab.backendapi.domain.RequestId;
+import com.epam.dlab.backendapi.domain.UpdateProjectBudgetDTO;
 import com.epam.dlab.backendapi.domain.UpdateProjectDTO;
 import com.epam.dlab.backendapi.roles.UserRoles;
 import com.epam.dlab.backendapi.service.EndpointService;
 import com.epam.dlab.backendapi.service.ExploratoryService;
 import com.epam.dlab.backendapi.service.ProjectService;
-import com.epam.dlab.backendapi.service.SecurityService;
 import com.epam.dlab.backendapi.util.RequestBuilder;
 import com.epam.dlab.constants.ServiceConsts;
 import com.epam.dlab.dto.UserInstanceStatus;
@@ -28,13 +32,20 @@ import com.google.inject.Inject;
 import com.google.inject.name.Named;
 import lombok.extern.slf4j.Slf4j;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
+import static com.epam.dlab.backendapi.domain.AuditActionEnum.CREATE_PROJECT;
+import static com.epam.dlab.backendapi.domain.AuditActionEnum.START_PROJECT;
+import static com.epam.dlab.backendapi.domain.AuditActionEnum.STOP_PROJECT;
+import static com.epam.dlab.backendapi.domain.AuditActionEnum.TERMINATE_PROJECT;
+import static com.epam.dlab.backendapi.domain.AuditActionEnum.UPDATE_PROJECT;
 import static java.util.stream.Collectors.toSet;
 import static java.util.stream.Stream.concat;
 
@@ -46,7 +57,11 @@ public class ProjectServiceImpl implements ProjectService {
 	private static final String START_PRJ_API = "infrastructure/project/start";
 	private static final String STOP_PRJ_API = "infrastructure/project/stop";
 	private static final String STOP_ACTION = "stop";
-	private static final String TERMINATE_ACTION = "terminate";
+
+	private static final String AUDIT_ADD_ENDPOINT = "Added endpoint(s) %s";
+	private static final String AUDIT_ADD_GROUP = "Added group(s) %s";
+	private static final String AUDIT_REMOVE_GROUP = "Removed group(s) %s";
+	private static final String AUDIT_UPDATE_BUDGET = "Update budget %d->%d";
 
 	private final ProjectDAO projectDAO;
 	private final ExploratoryService exploratoryService;
@@ -56,14 +71,15 @@ public class ProjectServiceImpl implements ProjectService {
 	private final RequestBuilder requestBuilder;
 	private final EndpointService endpointService;
 	private final ExploratoryDAO exploratoryDAO;
-	private final SecurityService securityService;
+	private final SelfServiceApplicationConfiguration configuration;
+
 
 	@Inject
 	public ProjectServiceImpl(ProjectDAO projectDAO, ExploratoryService exploratoryService,
 							  UserGroupDao userGroupDao,
 							  @Named(ServiceConsts.PROVISIONING_SERVICE_NAME) RESTService provisioningService,
 							  RequestId requestId, RequestBuilder requestBuilder, EndpointService endpointService,
-							  ExploratoryDAO exploratoryDAO, SecurityService securityService) {
+							  ExploratoryDAO exploratoryDAO, SelfServiceApplicationConfiguration configuration) {
 		this.projectDAO = projectDAO;
 		this.exploratoryService = exploratoryService;
 		this.userGroupDao = userGroupDao;
@@ -72,7 +88,7 @@ public class ProjectServiceImpl implements ProjectService {
 		this.requestBuilder = requestBuilder;
 		this.endpointService = endpointService;
 		this.exploratoryDAO = exploratoryDAO;
-		this.securityService = securityService;
+		this.configuration = configuration;
 	}
 
 	@Override
@@ -100,7 +116,8 @@ public class ProjectServiceImpl implements ProjectService {
 
 	@BudgetLimited
 	@Override
-	public void create(UserInfo user, ProjectDTO projectDTO) {
+	@Audit(action = CREATE_PROJECT)
+	public void create(@User UserInfo user, ProjectDTO projectDTO, @ResourceName String resourceName) {
 		if (!projectDAO.get(projectDTO.getName()).isPresent()) {
 			projectDAO.create(projectDTO);
 			createProjectOnCloud(user, projectDTO);
@@ -123,9 +140,9 @@ public class ProjectServiceImpl implements ProjectService {
 	}
 
 	@ProjectAdmin
+	@Audit(action = TERMINATE_PROJECT)
 	@Override
-	public void terminateEndpoint(@User UserInfo userInfo, List<String> endpoints, @Project String name) {
-		System.out.println("sd");
+	public void terminateEndpoint(@User UserInfo userInfo, List<String> endpoints, @ResourceName @Project String name) {
 		endpoints.forEach(endpoint -> terminateEndpoint(userInfo, endpoint, name));
 	}
 
@@ -137,8 +154,9 @@ public class ProjectServiceImpl implements ProjectService {
 	}
 
 	@ProjectAdmin
+	@Audit(action = START_PROJECT)
 	@Override
-	public void start(@User UserInfo userInfo, List<String> endpoints, @Project String name) {
+	public void start(@User UserInfo userInfo, List<String> endpoints, @ResourceName @Project String name) {
 		endpoints.forEach(endpoint -> start(userInfo, endpoint, name));
 	}
 
@@ -149,8 +167,9 @@ public class ProjectServiceImpl implements ProjectService {
 	}
 
 	@ProjectAdmin
+	@Audit(action = STOP_PROJECT)
 	@Override
-	public void stopWithResources(@User UserInfo userInfo, List<String> endpoints, @Project String projectName) {
+	public void stopWithResources(@User UserInfo userInfo, List<String> endpoints, @ResourceName @Project String projectName) {
 		List<ProjectEndpointDTO> endpointDTOs = get(projectName)
 				.getEndpoints()
 				.stream()
@@ -178,9 +197,17 @@ public class ProjectServiceImpl implements ProjectService {
 				.stream()
 				.map(ProjectEndpointDTO::getName)
 				.collect(toSet());
-		final HashSet<String> newEndpoints = new HashSet<>(projectDTO.getEndpoints());
+		final Set<String> newEndpoints = new HashSet<>(projectDTO.getEndpoints());
 		newEndpoints.removeAll(endpoints);
-		final List<ProjectEndpointDTO> endpointsToBeCreated = newEndpoints.stream()
+		final List<String> projectAudit = configuration.isAuditEnabled() ? updateProjectAudit(projectDTO, project, newEndpoints) : null;
+		updateProject(userInfo, projectName, projectDTO, project, newEndpoints, projectAudit);
+	}
+
+	@Audit(action = UPDATE_PROJECT)
+	public void updateProject(@User UserInfo userInfo, @ResourceName String projectName, UpdateProjectDTO projectDTO, ProjectDTO project, Set<String> newEndpoints,
+							  @Info List<String> projectAudit) {
+		final List<ProjectEndpointDTO> endpointsToBeCreated = newEndpoints
+				.stream()
 				.map(e -> new ProjectEndpointDTO(e, UserInstanceStatus.CREATING, null))
 				.collect(Collectors.toList());
 		project.getEndpoints().addAll(endpointsToBeCreated);
@@ -190,8 +217,18 @@ public class ProjectServiceImpl implements ProjectService {
 	}
 
 	@Override
-	public void updateBudget(List<ProjectDTO> projects) {
-		projects.forEach(p -> projectDAO.updateBudget(p.getName(), p.getBudget()));
+	public void updateBudget(UserInfo userInfo, List<UpdateProjectBudgetDTO> dtos) {
+		final List<ProjectDTO> projects = dtos
+				.stream()
+				.map(dto -> ProjectDTO.builder().name(dto.getProject()).budget(dto.getBudget()).build())
+				.collect(Collectors.toList());
+
+		projects.forEach(p -> updateBudget(userInfo, p.getName(), p.getBudget(), getUpdateBudgetAudit(p)));
+	}
+
+	@Audit(action = UPDATE_PROJECT)
+	public void updateBudget(@User UserInfo userInfo, @ResourceName String name, Integer budget, @Info List<String> updateBudgetAudit) {
+		projectDAO.updateBudget(name, budget);
 	}
 
 	@Override
@@ -243,9 +280,10 @@ public class ProjectServiceImpl implements ProjectService {
 	}
 
 	private void checkProjectRelatedResourcesInProgress(String projectName, List<ProjectEndpointDTO> endpoints, String action) {
-        boolean edgeProgress = endpoints.stream().anyMatch(e ->
-                Arrays.asList(UserInstanceStatus.CREATING, UserInstanceStatus.STARTING, UserInstanceStatus.STOPPING,
-                        UserInstanceStatus.TERMINATING).contains(e.getStatus()));
+		boolean edgeProgress = endpoints
+				.stream().anyMatch(e ->
+						Arrays.asList(UserInstanceStatus.CREATING, UserInstanceStatus.STARTING, UserInstanceStatus.STOPPING,
+								UserInstanceStatus.TERMINATING).contains(e.getStatus()));
 
 		List<String> endpointsName = endpoints.stream().map(ProjectEndpointDTO::getName).collect(Collectors.toList());
 		if (edgeProgress || !checkExploratoriesAndComputationalProgress(projectName, endpointsName)) {
@@ -254,6 +292,29 @@ public class ProjectServiceImpl implements ProjectService {
 		}
 	}
 
+	private List<String> updateProjectAudit(UpdateProjectDTO projectDTO, ProjectDTO project, Set<String> newEndpoints) {
+		final List<String> audit = new ArrayList<>();
+		final Set<String> newGroups = new HashSet<>(projectDTO.getGroups());
+		newGroups.removeAll(project.getGroups());
+		final Set<String> removedGroups = new HashSet<>(project.getGroups());
+		removedGroups.removeAll(projectDTO.getGroups());
+
+		if (!newEndpoints.isEmpty()) {
+			audit.add(String.format(AUDIT_ADD_ENDPOINT, String.join(", ", newEndpoints)));
+		}
+		if (!newGroups.isEmpty()) {
+			audit.add(String.format(AUDIT_ADD_GROUP, String.join(", ", newGroups)));
+		}
+		if (!removedGroups.isEmpty()) {
+			audit.add(String.format(AUDIT_REMOVE_GROUP, String.join(", ", removedGroups)));
+		}
+		return audit;
+	}
+
+	private List<String> getUpdateBudgetAudit(ProjectDTO p) {
+		return Collections.singletonList(String.format(AUDIT_UPDATE_BUDGET, get(p.getName()).getBudget(), p.getBudget()));
+	}
+
 	private Supplier<ResourceNotFoundException> projectNotFound() {
 		return () -> new ResourceNotFoundException("Project with passed name not found");
 	}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org