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 <olegfuk...@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-unsubscr...@dlab.apache.org
For additional commands, e-mail: commits-h...@dlab.apache.org

Reply via email to