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