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/06/10 09:52:40 UTC

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

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 16ae6acdcdf9aedd73ebb04b3e9c43a50196cdb1
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             |   4 +-
 .../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           | 104 +++++++++++++++++----
 22 files changed, 505 insertions(+), 64 deletions(-)

diff --git a/services/self-service/self-service.yml b/services/self-service/self-service.yml
index df92c25..f4117ff 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
+# Set to true to enable audit
+auditEnabled: true
 # Name of configuration file for billing report.
-<#if DEV_MODE == "true">
+  <#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..9658897 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()
@@ -165,8 +184,10 @@ public class ProjectServiceImpl implements ProjectService {
 						.collect(Collectors.toList()))
 				.forEach(e -> exploratoryService.stop(new UserInfo(e.getUser(), userInfo.getAccessToken()), projectName, e.getExploratoryName()));
 
-		endpointDTOs.stream().filter(e -> !Arrays.asList(UserInstanceStatus.TERMINATED,
-				UserInstanceStatus.TERMINATING, UserInstanceStatus.STOPPED, UserInstanceStatus.FAILED).contains(e.getStatus()))
+		endpointDTOs
+				.stream()
+				.filter(e -> !Arrays.asList(UserInstanceStatus.TERMINATED, UserInstanceStatus.TERMINATING, UserInstanceStatus.STOPPED,
+						UserInstanceStatus.FAILED).contains(e.getStatus()))
 				.forEach(e -> stop(userInfo, e.getName(), projectName));
 	}
 
@@ -178,9 +199,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 = updateProjectAudit(projectDTO, project, newEndpoints);
+		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 +219,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 +282,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 +294,32 @@ public class ProjectServiceImpl implements ProjectService {
 		}
 	}
 
+	private List<String> updateProjectAudit(UpdateProjectDTO projectDTO, ProjectDTO project, Set<String> newEndpoints) {
+		if (configuration.isAuditEnabled()) {
+			return null;
+		}
+		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