You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by dg...@apache.org on 2020/06/10 09:10:27 UTC

[incubator-dlab] branch audit updated: [DLAB-1758]: Merge audit UI (#784)

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

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


The following commit(s) were added to refs/heads/audit by this push:
     new e6d4e5d  [DLAB-1758]: Merge audit UI (#784)
e6d4e5d is described below

commit e6d4e5dee441c32357d53fed0e47a4b52fba55dd
Author: Dmytro Gnatyshyn <42...@users.noreply.github.com>
AuthorDate: Wed Jun 10 12:09:32 2020 +0300

    [DLAB-1758]: Merge audit UI (#784)
    
     [DLAB-1758]: Merge audit UI
---
 .../templates/proxy_location_webapp_template.conf  |   2 +-
 .../src/ssn/templates/ssn.yml                      |   2 +-
 pom.xml                                            |   1 +
 ...BucketDownloadDTO.java => BucketDeleteDTO.java} |   9 +-
 services/provisioning-service/pom.xml              |  22 +-
 .../dlab/backendapi/resources/BucketResource.java  |  74 ++-
 .../dlab/backendapi/service/BucketService.java     |   9 +-
 .../service/impl/aws/BucketServiceAwsImpl.java     |  51 +-
 .../service/impl/azure/BucketServiceAzureImpl.java |  73 ++-
 .../service/impl/gcp/BucketServiceGcpImpl.java     |  37 +-
 services/self-service/pom.xml                      |  10 +
 services/self-service/self-service.yml             |   2 +-
 .../epam/dlab/backendapi/dao/ExploratoryDAO.java   |   4 +-
 .../epam/dlab/backendapi/dao/UserRoleDaoImpl.java  |  20 +-
 .../dlab/backendapi/resources/BucketResource.java  |  82 ++-
 .../backendapi/resources/ExploratoryResource.java  |   3 -
 .../azure/ComputationalResourceAzure.java          |   3 -
 .../backendapi/resources/dto/BucketDeleteDTO.java} |  11 +-
 .../backendapi/resources/dto/UserResourceInfo.java |  70 +--
 .../dlab/backendapi/resources/dto/UserRoleDto.java |   8 +
 .../dlab/backendapi/service/BucketService.java     |   7 +-
 .../backendapi/service/impl/BucketServiceImpl.java |  43 +-
 .../service/impl/EndpointServiceImpl.java          |   6 +-
 .../service/impl/EnvironmentServiceImpl.java       |  29 +-
 .../impl/InfrastructureInfoServiceImpl.java        |  60 +--
 .../impl/InfrastructureTemplateServiceImpl.java    |  35 +-
 .../src/main/resources/mongo/aws/mongo_roles.json  |   8 +-
 .../main/resources/mongo/azure/mongo_roles.json    |   8 +-
 .../src/main/resources/mongo/gcp/mongo_roles.json  |   8 +-
 .../manage-environment-dilog.component.html        |   2 +-
 .../manage-environment-dilog.component.ts          |   4 +
 .../management-grid/management-grid.component.html |   5 +-
 .../management-grid/management-grid.component.scss |   5 +-
 .../management-grid/management-grid.component.ts   |  31 +-
 .../administration/management/management.model.ts  |   6 +-
 .../main/resources/webapp/src/app/app.module.ts    |  10 +-
 .../resources/webapp/src/app/app.routing.module.ts |  12 +-
 .../convert-file-size/convert-file-size.pipe.ts}   |  36 +-
 .../src/app/core/pipes/convert-file-size/index.ts} |  21 +-
 .../src/app/core/services/appRouting.service.ts    |   1 -
 .../services/applicationServiceFacade.service.ts   |  24 +-
 .../webapp/src/app/core/services/audit.service.ts  |  44 ++
 .../app/core/services/bucket-browser.service.ts    |   6 +-
 .../webapp/src/app/core/util/copyPathUtils.ts}     |  27 +-
 .../resources/webapp/src/app/core/util/patterns.ts |   1 +
 .../webapp/src/app/core/util/sortUtils.ts          |   8 +-
 .../audit/audit-grid/audit-grid.component.html     | 147 ++++++
 .../audit/audit-grid/audit-grid.component.scss     | 237 +++++++++
 .../audit/audit-grid/audit-grid.component.ts       | 135 +++++
 .../audit-toolbar/audit-toolbar.component.html}    |  10 +-
 .../audit-toolbar/audit-toolbar.component.scss}    |   0
 .../audit-toolbar/audit-toolbar.component.ts}      |  34 +-
 .../src/app/reports/audit/audit.component.ts       |  99 ++++
 .../audit/audit.module.ts}                         |  26 +-
 .../src/app/reports/audit/filter-audit.model.ts    |  25 +
 .../reporting-grid/reporting-grid.component.html   |   2 +-
 .../reporting-grid/reporting-grid.component.scss   |   0
 .../reporting-grid/reporting-grid.component.ts     |  12 +-
 .../{ => reports}/reporting/reporting.component.ts |   9 +-
 .../{ => reports}/reporting/reporting.module.ts    |   6 +-
 .../reporting/toolbar/toolbar.component.html       |   8 +-
 .../reporting/toolbar/toolbar.component.scss       |   0
 .../reporting/toolbar/toolbar.component.ts         |  10 +-
 .../webapp/src/app/reports/reports.module.ts}      |  22 +-
 .../bucket-browser/bucket-browser.component.html   | 285 ++++++++---
 .../bucket-browser/bucket-browser.component.scss   | 556 +++++++++++++++++++--
 .../bucket-browser/bucket-browser.component.ts     | 395 ++++++++++++---
 .../bucket-browser.module.ts}                      |  35 +-
 .../bucket-confirmation-dialog.component.html      | 122 +++++
 .../bucket-confirmation-dialog.component.scss      | 176 +++++++
 .../bucket-confirmation-dialog.component.ts        |  58 +++
 .../bucket-browser/bucket-data.service.ts          |  87 +++-
 .../buckets-tree/bucket-tree.component.html        |  25 +
 .../buckets-tree/bucket-tree.component.scss        | 106 ++++
 .../buckets-tree/bucket-tree.component.ts          | 101 ++++
 .../folder-tree/folder-tree.component.html         |  75 ++-
 .../folder-tree/folder-tree.component.scss         |  69 ++-
 .../folder-tree/folder-tree.component.ts           | 203 ++++----
 .../cluster-details/cluster-details.component.html |   2 +-
 .../cluster-details/cluster-details.component.ts   |  11 +-
 .../cost-details-dialog.component.html             |   4 +-
 .../detail-dialog/detail-dialog.component.html     | 154 ++++--
 .../detail-dialog/detail-dialog.component.scss     |  19 +-
 .../detail-dialog/detail-dialog.component.ts       |  40 +-
 .../install-libraries.component.ts                 |  10 +-
 .../resources-grid/resources-grid.component.ts     |  40 +-
 .../resources-grid/resources-grid.model.ts         |   4 +
 .../src/app/resources/resources.component.html     |  24 +-
 .../src/app/resources/resources.component.ts       |  18 +-
 .../webapp/src/app/resources/resources.module.ts   |  30 +-
 .../multi-level-select-dropdown.component.ts       |   2 +-
 .../webapp/src/app/shared/material.module.ts       |   3 +-
 .../src/app/shared/navbar/navbar.component.html    |  50 +-
 .../src/assets/img/blank-file-svgrepo-com.svg      |  41 ++
 .../webapp/src/assets/styles/_dialogs.scss         |  62 +++
 .../src/main/resources/webapp/src/styles.scss      |  14 +-
 96 files changed, 3688 insertions(+), 855 deletions(-)

diff --git a/infrastructure-provisioning/src/ssn/templates/proxy_location_webapp_template.conf b/infrastructure-provisioning/src/ssn/templates/proxy_location_webapp_template.conf
index 2b949e0..38958b4 100644
--- a/infrastructure-provisioning/src/ssn/templates/proxy_location_webapp_template.conf
+++ b/infrastructure-provisioning/src/ssn/templates/proxy_location_webapp_template.conf
@@ -28,5 +28,5 @@ location / {
 
       # Fix the "It appears that your reverse proxy set up is broken" error.
       proxy_pass          https://localhost:8443;
-      proxy_read_timeout  90;
+      proxy_read_timeout  1800;
 }
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/ssn/templates/ssn.yml b/infrastructure-provisioning/src/ssn/templates/ssn.yml
index 41a9723..9ccd8d8 100644
--- a/infrastructure-provisioning/src/ssn/templates/ssn.yml
+++ b/infrastructure-provisioning/src/ssn/templates/ssn.yml
@@ -64,7 +64,7 @@ provisioningService:
 
 bucketService:
   jerseyClient:
-    timeout: 10m
+    timeout: 50m
     connectionTimeout: 3s
 
 billingService:
diff --git a/pom.xml b/pom.xml
index b6808a8..e24bfd9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,7 @@
         <lombok.version>1.16.18</lombok.version>
         <hibernate.validator.version>5.4.2.Final</hibernate.validator.version>
         <logback.version>1.2.3</logback.version>
+        <commons-fileupload.version>1.3.3</commons-fileupload.version>
     </properties>
     <dependencyManagement>
         <dependencies>
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDeleteDTO.java
similarity index 82%
copy from services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
copy to services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDeleteDTO.java
index b1201e6..0bec33d 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDeleteDTO.java
@@ -21,13 +21,12 @@ package com.epam.dlab.dto.bucket;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import lombok.Data;
-import org.hibernate.validator.constraints.NotBlank;
+
+import java.util.List;
 
 @Data
 @JsonIgnoreProperties(ignoreUnknown = true)
-public class BucketDownloadDTO {
-    @NotBlank(message = "field cannot be empty")
+public class BucketDeleteDTO {
     private final String bucket;
-    @NotBlank(message = "field cannot be empty")
-    private final String object;
+    private final List<String> objects;
 }
diff --git a/services/provisioning-service/pom.xml b/services/provisioning-service/pom.xml
index 2fc22bd..0cec6a5 100644
--- a/services/provisioning-service/pom.xml
+++ b/services/provisioning-service/pom.xml
@@ -34,14 +34,14 @@
             <dependency>
                 <groupId>com.google.cloud</groupId>
                 <artifactId>libraries-bom</artifactId>
-                <version>3.3.0</version>
+                <version>5.3.0</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
             <dependency>
                 <groupId>software.amazon.awssdk</groupId>
                 <artifactId>bom</artifactId>
-                <version>2.11.9</version>
+                <version>2.13.9</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
@@ -90,7 +90,7 @@
         <dependency>
             <groupId>com.google.cloud</groupId>
             <artifactId>google-cloud-storage</artifactId>
-            <version>1.106.0</version>
+            <version>1.107.0</version>
         </dependency>
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
@@ -101,13 +101,27 @@
             <artifactId>httpclient</artifactId>
             <version>4.5.9</version>
         </dependency>
-
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-storage-blob</artifactId>
+            <version>12.6.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.11.0</version>
+        </dependency>
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
             <version>2.6</version>
         </dependency>
         <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>${commons-fileupload.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
             <version>${org.mockito.version}</version>
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java
index a44d21f..4495f5d 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java
@@ -21,19 +21,25 @@ package com.epam.dlab.backendapi.resources;
 
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.service.BucketService;
+import com.epam.dlab.dto.bucket.BucketDeleteDTO;
+import com.epam.dlab.exceptions.DlabException;
 import com.google.inject.Inject;
 import io.dropwizard.auth.Auth;
 import lombok.extern.slf4j.Slf4j;
-import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
-import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.util.Streams;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.io.InputStream;
@@ -41,6 +47,10 @@ import java.io.InputStream;
 @Slf4j
 @Path("/bucket")
 public class BucketResource {
+    private static final String OBJECT_FORM_FIELD = "object";
+    private static final String BUCKET_FORM_FIELD = "bucket";
+    private static final String SIZE_FORM_FIELD = "file-size";
+
     private final BucketService bucketService;
 
     @Inject
@@ -61,12 +71,8 @@ public class BucketResource {
     @Path("/upload")
     @Consumes(MediaType.MULTIPART_FORM_DATA)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response uploadObject(@Auth UserInfo userInfo,
-                                 @FormDataParam("object") String object,
-                                 @FormDataParam("bucket") String bucket,
-                                 @FormDataParam("file") InputStream inputStream,
-                                 @FormDataParam("file") FormDataContentDisposition fileMetaData) {
-        bucketService.uploadObject(bucket, object, inputStream);
+    public Response uploadObject(@Auth UserInfo userInfo, @Context HttpServletRequest request) {
+        upload(request);
         return Response.ok().build();
     }
 
@@ -74,20 +80,54 @@ public class BucketResource {
     @Path("/{bucket}/object/{object}/download")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_OCTET_STREAM)
-    public Response downloadObject(@Auth UserInfo userInfo,
+    public Response downloadObject(@Auth UserInfo userInfo, @Context HttpServletResponse resp,
                                    @PathParam("object") String object,
                                    @PathParam("bucket") String bucket) {
-        return Response.ok(bucketService.downloadObject(bucket, object)).build();
+        bucketService.downloadObject(bucket, object, resp);
+        return Response.ok().build();
     }
 
-    @DELETE
-    @Path("/{bucket}/object/{object}")
+    @POST
+    @Path("/objects/delete")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response uploadObject(@Auth UserInfo userInfo,
-                                 @PathParam("bucket") String bucket,
-                                 @PathParam("object") String object) {
-        bucketService.deleteObject(bucket, object);
+    public Response uploadObject(@Auth UserInfo userInfo, BucketDeleteDTO bucketDeleteDTO) {
+        bucketService.deleteObjects(bucketDeleteDTO.getBucket(), bucketDeleteDTO.getObjects());
         return Response.ok().build();
     }
+
+    private void upload(HttpServletRequest request) {
+        String object = null;
+        String bucket = null;
+        long fileSize = 0;
+
+        ServletFileUpload upload = new ServletFileUpload();
+        try {
+            FileItemIterator iterStream = upload.getItemIterator(request);
+            while (iterStream.hasNext()) {
+                FileItemStream item = iterStream.next();
+                try (InputStream stream = item.openStream()) {
+                    if (item.isFormField()) {
+                        if (OBJECT_FORM_FIELD.equals(item.getFieldName())) {
+                            object = Streams.asString(stream);
+                        }
+                        if (BUCKET_FORM_FIELD.equals(item.getFieldName())) {
+                            bucket = Streams.asString(stream);
+                        }
+                        if (SIZE_FORM_FIELD.equals(item.getFieldName())) {
+                            fileSize = Long.parseLong(Streams.asString(stream));
+                        }
+                    } else {
+                        bucketService.uploadObject(bucket, object, stream, fileSize);
+                    }
+                } catch (Exception e) {
+                    log.error("Cannot upload object {} to bucket {}. {}", object, bucket, e.getMessage(), e);
+                    throw new DlabException(String.format("Cannot upload object %s to bucket %s. %s", object, bucket, e.getMessage()));
+                }
+            }
+        } catch (Exception e) {
+            log.error("Cannot upload object {} to bucket {}. {}", object, bucket, e.getMessage(), e);
+            throw new DlabException(String.format("Cannot upload object %s to bucket %s. %s", object, bucket, e.getMessage()));
+        }
+    }
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
index f3ed208..c1488e3 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
@@ -21,17 +21,18 @@ package com.epam.dlab.backendapi.service;
 
 import com.epam.dlab.dto.bucket.BucketDTO;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.InputStream;
 import java.util.List;
 
 public interface BucketService {
-    String DATE_FORMAT = "dd-M-yyyy hh:mm:ss";
+    String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
 
     List<BucketDTO> getObjects(String bucket);
 
-    void uploadObject(String bucket, String object, InputStream stream);
+    void uploadObject(String bucket, String object, InputStream stream, long fileSize);
 
-    byte[] downloadObject(String bucket, String object);
+    void downloadObject(String bucket, String object, HttpServletResponse resp);
 
-    void deleteObject(String bucket, String object);
+    void deleteObjects(String bucket, List<String> objects);
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/aws/BucketServiceAwsImpl.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/aws/BucketServiceAwsImpl.java
index 8193fde..347543c 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/aws/BucketServiceAwsImpl.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/aws/BucketServiceAwsImpl.java
@@ -23,18 +23,19 @@ import com.epam.dlab.backendapi.service.BucketService;
 import com.epam.dlab.dto.bucket.BucketDTO;
 import com.epam.dlab.exceptions.DlabException;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import software.amazon.awssdk.awscore.exception.AwsServiceException;
 import software.amazon.awssdk.core.sync.RequestBody;
 import software.amazon.awssdk.core.sync.ResponseTransformer;
 import software.amazon.awssdk.services.s3.S3Client;
-import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
+import software.amazon.awssdk.services.s3.model.Delete;
+import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
 import software.amazon.awssdk.services.s3.model.GetObjectRequest;
 import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
+import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
 import software.amazon.awssdk.services.s3.model.PutObjectRequest;
 import software.amazon.awssdk.services.s3.model.S3Object;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
 import java.io.InputStream;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -64,7 +65,8 @@ public class BucketServiceAwsImpl implements BucketService {
     }
 
     @Override
-    public void uploadObject(String bucket, String object, InputStream stream) {
+    public void uploadObject(String bucket, String object, InputStream stream, long fileSize) {
+        log.info("Uploading file {} to bucket {}", object, bucket);
         try {
             S3Client s3 = S3Client.create();
             PutObjectRequest uploadRequest = PutObjectRequest
@@ -72,53 +74,64 @@ public class BucketServiceAwsImpl implements BucketService {
                     .bucket(bucket)
                     .key(object)
                     .build();
-            s3.putObject(uploadRequest, RequestBody.fromBytes(IOUtils.toByteArray(stream)));
+            s3.putObject(uploadRequest, RequestBody.fromInputStream(stream, fileSize));
         } catch (Exception e) {
             log.error("Cannot upload object {} to bucket {}. Reason: {}", object, bucket, e.getMessage());
             throw new DlabException(String.format("Cannot upload object %s to bucket %s. Reason: %s", object, bucket, e.getMessage()));
         }
+        log.info("Finished uploading file {} to bucket {}", object, bucket);
     }
 
     @Override
-    public byte[] downloadObject(String bucket, String object) {
-        try {
+    public void downloadObject(String bucket, String object, HttpServletResponse resp) {
+        log.info("Downloading file {} from bucket {}", object, bucket);
+        try (ServletOutputStream outputStream = resp.getOutputStream()) {
             S3Client s3 = S3Client.create();
             GetObjectRequest downloadRequest = GetObjectRequest
                     .builder()
                     .bucket(bucket)
                     .key(object)
                     .build();
-            return s3.getObject(downloadRequest, ResponseTransformer.toBytes()).asByteArray();
+            s3.getObject(downloadRequest, ResponseTransformer.toOutputStream(outputStream));
         } catch (Exception e) {
             log.error("Cannot download object {} from bucket {}. Reason: {}", object, bucket, e.getMessage());
             throw new DlabException(String.format("Cannot download object %s from bucket %s. Reason: %s", object, bucket, e.getMessage()));
         }
+        log.info("Finished downloading file {} from bucket {}", object, bucket);
     }
 
     @Override
-    public void deleteObject(String bucket, String object) {
+    public void deleteObjects(String bucket, List<String> objects) {
         try {
             S3Client s3 = S3Client.create();
-            DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest
-                    .builder()
+            List<ObjectIdentifier> objectsToDelete = objects
+                    .stream()
+                    .map(o -> ObjectIdentifier.builder()
+                            .key(o)
+                            .build())
+                    .collect(Collectors.toList());
+
+            DeleteObjectsRequest deleteObjectsRequests = DeleteObjectsRequest.builder()
                     .bucket(bucket)
-                    .key(object)
+                    .delete(Delete.builder()
+                            .objects(objectsToDelete)
+                            .build())
                     .build();
-            s3.deleteObject(deleteObjectRequest);
-        } catch (AwsServiceException e) {
-            log.error("Cannot delete object {} from bucket {}. Reason: {}", object, bucket, e.getMessage());
-            throw new DlabException(String.format("Cannot delete object %s from bucket %s. Reason: %s", object, bucket, e.getMessage()));
+
+            s3.deleteObjects(deleteObjectsRequests);
+        } catch (Exception e) {
+            log.error("Cannot delete objects {} from bucket {}. Reason: {}", objects, bucket, e.getMessage());
+            throw new DlabException(String.format("Cannot delete objects %s from bucket %s. Reason: %s", objects, bucket, e.getMessage()));
         }
     }
 
     private BucketDTO toBucketDTO(String bucket, S3Object s3Object) {
-        final String size = FileUtils.byteCountToDisplaySize(s3Object.size());
         Date date = Date.from(s3Object.lastModified());
         SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
         return BucketDTO.builder()
                 .bucket(bucket)
                 .object(s3Object.key())
-                .size(size)
+                .size(String.valueOf(s3Object.size()))
                 .lastModifiedDate(formatter.format(date))
                 .build();
     }
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/azure/BucketServiceAzureImpl.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/azure/BucketServiceAzureImpl.java
index 0799b8e..f35dbc6 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/azure/BucketServiceAzureImpl.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/azure/BucketServiceAzureImpl.java
@@ -19,30 +19,91 @@
 
 package com.epam.dlab.backendapi.service.impl.azure;
 
+import com.azure.storage.blob.BlobClient;
+import com.azure.storage.blob.BlobContainerClient;
+import com.azure.storage.blob.BlobServiceClient;
+import com.azure.storage.blob.BlobServiceClientBuilder;
+import com.azure.storage.blob.models.BlobItem;
 import com.epam.dlab.backendapi.service.BucketService;
 import com.epam.dlab.dto.bucket.BucketDTO;
+import com.epam.dlab.exceptions.DlabException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
 import java.io.InputStream;
+import java.time.format.DateTimeFormatter;
 import java.util.List;
+import java.util.stream.Collectors;
 
+@Slf4j
 public class BucketServiceAzureImpl implements BucketService {
     @Override
     public List<BucketDTO> getObjects(String bucket) {
-        return null;
+        try {
+            BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(System.getenv("AZURE_STORAGE_CONNECTION_STRING")).buildClient();
+            BlobContainerClient blobContainerClient = blobServiceClient.getBlobContainerClient(bucket);
+            return blobContainerClient.listBlobs()
+                    .stream()
+                    .map(blob -> toBucketDTO(bucket, blob))
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("Cannot retrieve objects from bucket {}. Reason: {}", bucket, e.getMessage());
+            throw new DlabException(String.format("Cannot retrieve objects from bucket %s. Reason: %s", bucket, e.getMessage()));
+        }
     }
 
     @Override
-    public void uploadObject(String bucket, String object, InputStream stream) {
-
+    public void uploadObject(String bucket, String object, InputStream stream, long fileSize) {
+        log.info("Uploading file {} to bucket {}", object, bucket);
+        try {
+            BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(System.getenv("AZURE_STORAGE_CONNECTION_STRING")).buildClient();
+            BlobContainerClient blobContainerClient = blobServiceClient.getBlobContainerClient(bucket);
+            BlobClient blobClient = blobContainerClient.getBlobClient(object);
+            blobClient.upload(stream, fileSize);
+        } catch (Exception e) {
+            log.error("Cannot upload object {} to bucket {}. Reason: {}", object, bucket, e.getMessage());
+            throw new DlabException(String.format("Cannot upload object %s to bucket %s. Reason: %s", object, bucket, e.getMessage()));
+        }
+        log.info("Finished uploading file {} to bucket {}", object, bucket);
     }
 
     @Override
-    public byte[] downloadObject(String bucket, String object) {
-        return new byte[0];
+    public void downloadObject(String bucket, String object, HttpServletResponse resp) {
+        log.info("Downloading file {} from bucket {}", object, bucket);
+        try (ServletOutputStream outputStream = resp.getOutputStream()) {
+            BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(System.getenv("AZURE_STORAGE_CONNECTION_STRING")).buildClient();
+            BlobContainerClient blobContainerClient = blobServiceClient.getBlobContainerClient(bucket);
+            BlobClient blobClient = blobContainerClient.getBlobClient(object);
+            blobClient.download(outputStream);
+        } catch (Exception e) {
+            log.error("Cannot download object {} from bucket {}. Reason: {}", object, bucket, e.getMessage());
+            throw new DlabException(String.format("Cannot download object %s from bucket %s. Reason: %s", object, bucket, e.getMessage()));
+        }
+        log.info("Finished downloading file {} from bucket {}", object, bucket);
     }
 
     @Override
-    public void deleteObject(String bucket, String object) {
+    public void deleteObjects(String bucket, List<String> objects) {
+        try {
+            BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(System.getenv("AZURE_STORAGE_CONNECTION_STRING")).buildClient();
+            BlobContainerClient blobContainerClient = blobServiceClient.getBlobContainerClient(bucket);
+            objects.forEach(object -> blobContainerClient.getBlobClient(object).delete());
+        } catch (Exception e) {
+            log.error("Cannot delete objects {} from bucket {}. Reason: {}", objects, bucket, e.getMessage());
+            throw new DlabException(String.format("Cannot delete objects %s from bucket %s. Reason: %s", objects, bucket, e.getMessage()));
+        }
+    }
 
+    private BucketDTO toBucketDTO(String bucket, BlobItem blob) {
+        final String size = FileUtils.byteCountToDisplaySize(blob.getProperties().getContentLength());
+        String lastModifiedDate = blob.getProperties().getLastModified().format(DateTimeFormatter.ofPattern(DATE_FORMAT));
+        return BucketDTO.builder()
+                .bucket(bucket)
+                .object(blob.getName())
+                .lastModifiedDate(lastModifiedDate)
+                .size(size)
+                .build();
     }
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/gcp/BucketServiceGcpImpl.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/gcp/BucketServiceGcpImpl.java
index 1c17142..a245850 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/gcp/BucketServiceGcpImpl.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/gcp/BucketServiceGcpImpl.java
@@ -29,10 +29,9 @@ import com.google.cloud.storage.Bucket;
 import com.google.cloud.storage.Storage;
 import com.google.cloud.storage.StorageOptions;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
 
-import java.io.ByteArrayOutputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
 import java.io.InputStream;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -58,51 +57,57 @@ public class BucketServiceGcpImpl implements BucketService {
     }
 
     @Override
-    public void uploadObject(String bucket, String object, InputStream stream) {
+    public void uploadObject(String bucket, String object, InputStream stream, long fileSize) {
+        log.info("Uploading file {} to bucket {}", object, bucket);
         try {
             Storage storage = StorageOptions.getDefaultInstance().getService();
             BlobId blobId = BlobId.of(bucket, object);
             BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build();
-            storage.create(blobInfo, IOUtils.toByteArray(stream));
+            storage.create(blobInfo, stream);
         } catch (Exception e) {
             log.error("Cannot upload object {} to bucket {}. Reason: {}", object, bucket, e.getMessage());
             throw new DlabException(String.format("Cannot upload object %s to bucket %s. Reason: %s", object, bucket, e.getMessage()));
         }
+        log.info("Finished uploading file {} to bucket {}", object, bucket);
     }
 
     @Override
-    public byte[] downloadObject(String bucket, String object) {
-        try {
+    public void downloadObject(String bucket, String object, HttpServletResponse resp) {
+        log.info("Downloading file {} from bucket {}", object, bucket);
+        try (ServletOutputStream outputStream = resp.getOutputStream()) {
             Storage storage = StorageOptions.getDefaultInstance().getService();
             Blob blob = storage.get(BlobId.of(bucket, object));
-            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-            blob.downloadTo(outputStream); //todo add check for blob != null and throw exception
-            return outputStream.toByteArray();
+            blob.downloadTo(outputStream);
+            log.info("Finished downloading file {} from bucket {}", object, bucket);
         } catch (Exception e) {
             log.error("Cannot download object {} from bucket {}. Reason: {}", object, bucket, e.getMessage());
             throw new DlabException(String.format("Cannot download object %s from bucket %s. Reason: %s", object, bucket, e.getMessage()));
         }
+        log.info("Finished downloading file {} from bucket {}", object, bucket);
     }
 
     @Override
-    public void deleteObject(String bucket, String object) {
+    public void deleteObjects(String bucket, List<String> objects) {
         try {
             Storage storage = StorageOptions.getDefaultInstance().getService();
-            storage.delete(bucket, object);
+            List<BlobId> blobIds = objects
+                    .stream()
+                    .map(o -> BlobId.of(bucket, o))
+                    .collect(Collectors.toList());
+            storage.delete(blobIds);
         } catch (Exception e) {
-            log.error("Cannot delete object {} from bucket {}. Reason: {}", object, bucket, e.getMessage());
-            throw new DlabException(String.format("Cannot delete object %s from bucket %s. Reason: %s", object, bucket, e.getMessage()));
+            log.error("Cannot delete objects {} from bucket {}. Reason: {}", objects, bucket, e.getMessage());
+            throw new DlabException(String.format("Cannot delete objects %s from bucket %s. Reason: %s", objects, bucket, e.getMessage()));
         }
     }
 
     private BucketDTO toBucketDTO(BlobInfo blobInfo) {
-        final String size = FileUtils.byteCountToDisplaySize(blobInfo.getSize());
         Date date = new Date(blobInfo.getUpdateTime());
         SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
         return BucketDTO.builder()
                 .bucket(blobInfo.getBucket())
                 .object(blobInfo.getName())
-                .size(size)
+                .size(String.valueOf(blobInfo.getSize()))
                 .lastModifiedDate(formatter.format(date))
                 .build();
     }
diff --git a/services/self-service/pom.xml b/services/self-service/pom.xml
index 382dc7c..f21caf9 100644
--- a/services/self-service/pom.xml
+++ b/services/self-service/pom.xml
@@ -186,10 +186,20 @@
         </dependency>
 
         <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+        </dependency>
+        <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-collections4</artifactId>
             <version>4.4</version>
         </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>${commons-fileupload.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/services/self-service/self-service.yml b/services/self-service/self-service.yml
index f4117ff..de7dbc8 100644
--- a/services/self-service/self-service.yml
+++ b/services/self-service/self-service.yml
@@ -46,7 +46,7 @@ checkEnvStatusTimeout: 5m
 # Restrict access to DLab features using roles policy
 rolePolicyEnabled: true
 # Default access to DLab features using roles policy
-roleDefaultAccess: true
+roleDefaultAccess: false
 
 # Set to true to enable the scheduler of billing report.
 billingSchedulerEnabled: false
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java
index fc44569..8c5a7ce 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java
@@ -113,8 +113,8 @@ public class ExploratoryDAO extends BaseDAO {
 	 * @param user name
 	 * @return list of user resources
 	 */
-	public Iterable<Document> findExploratory(String user) {
-		return find(USER_INSTANCES, eq(USER, user),
+	public Iterable<Document> findExploratories(String user, String project) {
+		return find(USER_INSTANCES, and(eq(USER, user), eq(PROJECT, project)),
 				fields(exclude(ExploratoryLibDAO.EXPLORATORY_LIBS, ExploratoryLibDAO.COMPUTATIONAL_LIBS, SCHEDULER_DATA,
 						EXPLORATORY_USER, EXPLORATORY_PASS)));
 	}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/UserRoleDaoImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/UserRoleDaoImpl.java
index 5bc845a..e95a41b 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/UserRoleDaoImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/UserRoleDaoImpl.java
@@ -55,6 +55,9 @@ import static java.util.stream.Collectors.toList;
 @Singleton
 public class UserRoleDaoImpl extends BaseDAO implements UserRoleDao {
 	private static final ObjectMapper MAPPER = new ObjectMapper();
+	private static final String[] DEFAULT_AWS_SHAPES = {"nbShapes_t2.medium_fetching", "compShapes_c4.xlarge_fetching"};
+	private static final String[] DEFAULT_GCP_SHAPES = {"compShapes_n1-standard-2_fetching", "nbShapes_n1-standard-2_fetching"};
+	private static final String[] DEFAULT_AZURE_SHAPES = {"nbShapes_Standard_E4s_v3_fetching", "compShapes_Standard_E4s_v3_fetching"};
 	private static final String ROLES_FILE_FORMAT = "/mongo/%s/mongo_roles.json";
 	private static final String USERS_FIELD = "users";
 	private static final String GROUPS_FIELD = "groups";
@@ -128,13 +131,15 @@ public class UserRoleDaoImpl extends BaseDAO implements UserRoleDao {
 		if (remainingProviders.contains(cloudProviderToBeRemoved)) {
 			return;
 		}
-
 		List<UserRoleDto> remainingRoles = new ArrayList<>();
 		remainingProviders.forEach(p -> remainingRoles.addAll(getUserRoleFromFile(p)));
 
-		getUserRoleFromFile(cloudProviderToBeRemoved).stream()
+		getUserRoleFromFile(cloudProviderToBeRemoved)
+				.stream()
+				.filter(role -> UserRoleDto.cloudSpecificTypes().contains(role.getType()))
 				.map(UserRoleDto::getId)
-				.filter(u -> remainingRoles.stream()
+				.filter(u -> remainingRoles
+						.stream()
 						.map(UserRoleDto::getId)
 						.noneMatch(id -> id.equals(u)))
 				.forEach(this::remove);
@@ -178,14 +183,11 @@ public class UserRoleDaoImpl extends BaseDAO implements UserRoleDao {
 
 	private Set<String> getDefaultShapes(CloudProvider cloudProvider) {
 		if (cloudProvider == CloudProvider.AWS) {
-			return Stream.of("nbShapes_t2.medium_fetching", "compShapes_c4.xlarge_fetching")
-					.collect(Collectors.toSet());
+			return Stream.of(DEFAULT_AWS_SHAPES).collect(Collectors.toSet());
 		} else if (cloudProvider == CloudProvider.GCP) {
-			return Stream.of("compShapes_n1-standard-2_fetching", "nbShapes_n1-standard-2_fetching")
-					.collect(Collectors.toSet());
+			return Stream.of(DEFAULT_GCP_SHAPES).collect(Collectors.toSet());
 		} else if (cloudProvider == CloudProvider.AZURE) {
-			return Stream.of("nbShapes_Standard_E4s_v3_fetching", "compShapes_Standard_E4s_v3_fetching")
-					.collect(Collectors.toSet());
+			return Stream.of(DEFAULT_AZURE_SHAPES).collect(Collectors.toSet());
 		} else {
 			throw new DlabException("Unsupported cloud provider " + cloudProvider);
 		}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java
index 7198e35..6ffc7f9 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BucketResource.java
@@ -20,21 +20,28 @@
 package com.epam.dlab.backendapi.resources;
 
 import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.resources.dto.BucketDeleteDTO;
 import com.epam.dlab.backendapi.service.BucketService;
+import com.epam.dlab.exceptions.DlabException;
 import com.google.inject.Inject;
 import io.dropwizard.auth.Auth;
 import lombok.extern.slf4j.Slf4j;
-import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
-import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.util.Streams;
 
 import javax.annotation.security.RolesAllowed;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
 import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -44,6 +51,11 @@ import java.nio.file.Paths;
 @Path("/bucket")
 @Slf4j
 public class BucketResource {
+    private static final String OBJECT_FORM_FIELD = "object";
+    private static final String BUCKET_FORM_FIELD = "bucket";
+    private static final String ENDPOINT_FORM_FIELD = "endpoint";
+    private static final String SIZE_FORM_FIELD = "size";
+
     private final BucketService bucketService;
 
     @Inject
@@ -67,13 +79,8 @@ public class BucketResource {
     @Consumes(MediaType.MULTIPART_FORM_DATA)
     @Produces(MediaType.APPLICATION_JSON)
     @RolesAllowed("/api/bucket/upload")
-    public Response uploadObject(@Auth UserInfo userInfo,
-                                 @FormDataParam("object") String object,
-                                 @FormDataParam("bucket") String bucket,
-                                 @FormDataParam("endpoint") String endpoint,
-                                 @FormDataParam("file") InputStream fileInputStream,
-                                 @FormDataParam("file") FormDataContentDisposition fileMetaData) {
-        bucketService.uploadObjects(userInfo, bucket, object, endpoint, fileInputStream);
+    public Response uploadObject(@Auth UserInfo userInfo, @Context HttpServletRequest request) {
+        upload(userInfo, request);
         return Response.ok().build();
     }
 
@@ -82,25 +89,62 @@ public class BucketResource {
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_OCTET_STREAM)
     @RolesAllowed("/api/bucket/download")
-    public Response downloadObject(@Auth UserInfo userInfo,
+    public Response downloadObject(@Auth UserInfo userInfo, @Context HttpServletResponse resp,
                                    @PathParam("bucket") String bucket,
                                    @PathParam("object") String object,
                                    @PathParam("endpoint") String endpoint) {
-        return Response.ok(bucketService.downloadObject(userInfo, bucket, object, endpoint))
+        bucketService.downloadObject(userInfo, bucket, object, endpoint, resp);
+        return Response.ok()
                 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + Paths.get(object).getFileName() + "\"")
                 .build();
     }
 
-    @DELETE
-    @Path("/{bucket}/object/{object}/endpoint/{endpoint}")
+    @POST
+    @Path("/objects/delete")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     @RolesAllowed("/api/bucket/delete")
-    public Response deleteObject(@Auth UserInfo userInfo,
-                                 @PathParam("bucket") String bucket,
-                                 @PathParam("object") String object,
-                                 @PathParam("endpoint") String endpoint) {
-        bucketService.deleteObject(userInfo, bucket, object, endpoint);
+    public Response deleteObject(@Auth UserInfo userInfo, @Valid BucketDeleteDTO bucketDto) {
+        bucketService.deleteObjects(userInfo, bucketDto.getBucket(), bucketDto.getObjects(), bucketDto.getEndpoint());
         return Response.ok().build();
     }
+
+    private void upload(UserInfo userInfo, HttpServletRequest request) {
+        String object = null;
+        String bucket = null;
+        String endpoint = null;
+        long fileSize = 0;
+
+        ServletFileUpload upload = new ServletFileUpload();
+        try {
+            FileItemIterator iterStream = upload.getItemIterator(request);
+            while (iterStream.hasNext()) {
+                FileItemStream item = iterStream.next();
+                try (InputStream stream = item.openStream()) {
+                    if (item.isFormField()) {
+                        if (OBJECT_FORM_FIELD.equals(item.getFieldName())) {
+                            object = Streams.asString(stream);
+                        }
+                        if (BUCKET_FORM_FIELD.equals(item.getFieldName())) {
+                            bucket = Streams.asString(stream);
+                        }
+                        if (ENDPOINT_FORM_FIELD.equals(item.getFieldName())) {
+                            endpoint = Streams.asString(stream);
+                        }
+                        if (SIZE_FORM_FIELD.equals(item.getFieldName())) {
+                            fileSize = Long.parseLong(Streams.asString(stream));
+                        }
+                    } else {
+                        bucketService.uploadObjects(userInfo, bucket, object, endpoint, stream, fileSize);
+                    }
+                } catch (Exception e) {
+                    log.error("Cannot upload object {} to bucket {}. {}", object, bucket, e.getMessage(), e);
+                    throw new DlabException(String.format("Cannot upload object %s to bucket %s. %s", object, bucket, e.getMessage()));
+                }
+            }
+        } catch (Exception e) {
+            log.error("User {} cannot upload object {} to bucket {}. {}", userInfo.getName(), object, bucket, e.getMessage(), e);
+            throw new DlabException(String.format("User %s cannot upload object %s to bucket %s. %s", userInfo.getName(), object, bucket, e.getMessage()));
+        }
+    }
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ExploratoryResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ExploratoryResource.java
index 7b29af1..2a0a701 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ExploratoryResource.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/ExploratoryResource.java
@@ -20,7 +20,6 @@
 package com.epam.dlab.backendapi.resources;
 
 import com.epam.dlab.auth.UserInfo;
-import com.epam.dlab.auth.rest.UserSessionDurationAuthorizer;
 import com.epam.dlab.backendapi.resources.dto.ExploratoryActionFormDTO;
 import com.epam.dlab.backendapi.resources.dto.ExploratoryCreateFormDTO;
 import com.epam.dlab.backendapi.roles.RoleType;
@@ -34,7 +33,6 @@ import com.google.inject.Inject;
 import io.dropwizard.auth.Auth;
 import lombok.extern.slf4j.Slf4j;
 
-import javax.annotation.security.RolesAllowed;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
@@ -100,7 +98,6 @@ public class ExploratoryResource implements ExploratoryAPI {
 	 * @return Invocation response as JSON string.
 	 */
 	@POST
-	@RolesAllowed(UserSessionDurationAuthorizer.SHORT_USER_SESSION_DURATION)
 	public String start(@Auth UserInfo userInfo,
 						@Valid @NotNull ExploratoryActionFormDTO formDTO) {
 		log.debug("Starting exploratory environment {} for user {}", formDTO.getNotebookInstanceName(),
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/azure/ComputationalResourceAzure.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/azure/ComputationalResourceAzure.java
index 29f9794..ca18d14 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/azure/ComputationalResourceAzure.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/azure/ComputationalResourceAzure.java
@@ -20,7 +20,6 @@
 package com.epam.dlab.backendapi.resources.azure;
 
 import com.epam.dlab.auth.UserInfo;
-import com.epam.dlab.auth.rest.UserSessionDurationAuthorizer;
 import com.epam.dlab.backendapi.resources.dto.SparkStandaloneClusterCreateForm;
 import com.epam.dlab.backendapi.roles.RoleType;
 import com.epam.dlab.backendapi.roles.UserRoles;
@@ -32,7 +31,6 @@ import io.dropwizard.auth.Auth;
 import io.swagger.v3.oas.annotations.Parameter;
 import lombok.extern.slf4j.Slf4j;
 
-import javax.annotation.security.RolesAllowed;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
@@ -78,7 +76,6 @@ public class ComputationalResourceAzure {
 	 */
 	@PUT
 	@Path("dataengine")
-	@RolesAllowed(UserSessionDurationAuthorizer.SHORT_USER_SESSION_DURATION)
 	public Response createDataEngine(@Auth UserInfo userInfo,
 									 @Valid @NotNull SparkStandaloneClusterCreateForm form) {
 		log.debug("Create computational resources for {} | form is {}", userInfo.getName(), form);
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/BucketDeleteDTO.java
similarity index 80%
copy from services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
copy to services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/BucketDeleteDTO.java
index b1201e6..9f12a86 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/BucketDeleteDTO.java
@@ -17,17 +17,22 @@
  * under the License.
  */
 
-package com.epam.dlab.dto.bucket;
+package com.epam.dlab.backendapi.resources.dto;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import lombok.Data;
 import org.hibernate.validator.constraints.NotBlank;
+import org.hibernate.validator.constraints.NotEmpty;
+
+import java.util.List;
 
 @Data
 @JsonIgnoreProperties(ignoreUnknown = true)
-public class BucketDownloadDTO {
+public class BucketDeleteDTO {
     @NotBlank(message = "field cannot be empty")
     private final String bucket;
     @NotBlank(message = "field cannot be empty")
-    private final String object;
+    private final String endpoint;
+    @NotEmpty(message = "field cannot be empty")
+    private final List<String> objects;
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserResourceInfo.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserResourceInfo.java
index ea1198e..5958411 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserResourceInfo.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserResourceInfo.java
@@ -18,88 +18,38 @@
  */
 package com.epam.dlab.backendapi.resources.dto;
 
+import com.epam.dlab.dto.ResourceURL;
 import com.epam.dlab.dto.computational.UserComputationalResource;
 import com.epam.dlab.model.ResourceEnum;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
 import lombok.Data;
 
-import java.util.Collections;
 import java.util.List;
 
 @Data
+@Builder
 @JsonIgnoreProperties(ignoreUnknown = true)
 public class UserResourceInfo {
-
+	@JsonProperty
+	private String user;
+	@JsonProperty
+	private String project;
 	@JsonProperty("resource_type")
 	private ResourceEnum resourceType;
-
 	@JsonProperty("resource_name")
 	private String resourceName;
-
 	@JsonProperty("shape")
 	private String resourceShape;
-
 	@JsonProperty("status")
 	private String resourceStatus;
-
 	@JsonProperty("computational_resources")
-	private List<UserComputationalResource> computationalResources = Collections.emptyList();
-
-	@JsonProperty
-	private String user;
-	@JsonProperty
-	private String project;
-
+	private List<UserComputationalResource> computationalResources;
 	@JsonProperty("public_ip")
 	private String ip;
-
 	@JsonProperty("cloud_provider")
 	private String cloudProvider;
-
-
-	public UserResourceInfo withResourceType(ResourceEnum resourceType) {
-		setResourceType(resourceType);
-		return this;
-	}
-
-	public UserResourceInfo withResourceName(String resourceName) {
-		setResourceName(resourceName);
-		return this;
-	}
-
-	public UserResourceInfo withResourceShape(String resourceShape) {
-		setResourceShape(resourceShape);
-		return this;
-	}
-
-	public UserResourceInfo withResourceStatus(String resourceStatus) {
-		setResourceStatus(resourceStatus);
-		return this;
-	}
-
-	public UserResourceInfo withCompResources(List<UserComputationalResource> compResources) {
-		setComputationalResources(compResources);
-		return this;
-	}
-
-	public UserResourceInfo withUser(String user) {
-		setUser(user);
-		return this;
-	}
-
-	public UserResourceInfo withIp(String ip) {
-		setIp(ip);
-		return this;
-	}
-
-	public UserResourceInfo withProject(String project) {
-		setProject(project);
-		return this;
-	}
-
-	public UserResourceInfo withCloudProvider(String cloudProvider) {
-		setCloudProvider(cloudProvider);
-		return this;
-	}
+	@JsonProperty("exploratory_urls")
+	private List<ResourceURL> exploratoryUrls;
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserRoleDto.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserRoleDto.java
index 84551af..989ecf4 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserRoleDto.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/UserRoleDto.java
@@ -25,6 +25,8 @@ import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Set;
 
 @Getter
@@ -42,6 +44,8 @@ public class UserRoleDto {
 	private Set<String> exploratories;
 	@JsonProperty("exploratory_shapes")
 	private Set<String> exploratoryShapes;
+	@JsonProperty("computational_shapes")
+	private Set<String> computationalShapes;
 	private Set<String> groups;
 
 	private enum Type {
@@ -53,4 +57,8 @@ public class UserRoleDto {
 		BUCKET_BROWSER,
 		ADMINISTRATION
 	}
+
+	public static List<Type> cloudSpecificTypes() {
+		return Arrays.asList(Type.NOTEBOOK, Type.COMPUTATIONAL, Type.NOTEBOOK_SHAPE, Type.COMPUTATIONAL_SHAPE);
+	}
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
index 0377969..6e5345b 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
@@ -22,15 +22,16 @@ package com.epam.dlab.backendapi.service;
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.dto.bucket.BucketDTO;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.InputStream;
 import java.util.List;
 
 public interface BucketService {
     List<BucketDTO> getObjects(UserInfo userInfo, String bucket, String endpoint);
 
-    void uploadObjects(UserInfo userInfo, String bucket, String object, String endpoint, InputStream inputStream);
+    void uploadObjects(UserInfo userInfo, String bucket, String object, String endpoint, InputStream inputStream, long fileSize);
 
-    byte[] downloadObject(UserInfo userInfo, String bucket, String object, String endpoint);
+    void downloadObject(UserInfo userInfo, String bucket, String object, String endpoint, HttpServletResponse resp);
 
-    void deleteObject(UserInfo userInfo, String bucket, String object, String endpoint);
+    void deleteObjects(UserInfo userInfo, String bucket, List<String> objects, String endpoint);
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BucketServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BucketServiceImpl.java
index ce72a6f..d305532 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BucketServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BucketServiceImpl.java
@@ -25,14 +25,19 @@ import com.epam.dlab.backendapi.service.BucketService;
 import com.epam.dlab.backendapi.service.EndpointService;
 import com.epam.dlab.constants.ServiceConsts;
 import com.epam.dlab.dto.bucket.BucketDTO;
+import com.epam.dlab.dto.bucket.BucketDeleteDTO;
 import com.epam.dlab.exceptions.DlabException;
 import com.epam.dlab.rest.client.RESTService;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
 import org.apache.http.HttpStatus;
 import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -50,7 +55,7 @@ public class BucketServiceImpl implements BucketService {
     private static final String BUCKET_GET_OBJECTS = "%sbucket/%s";
     private static final String BUCKET_UPLOAD_OBJECT = "%sbucket/upload";
     private static final String BUCKET_DOWNLOAD_OBJECT = "%sbucket/%s/object/%s/download";
-    private static final String BUCKET_DELETE_OBJECT = "%sbucket/%s/object/%s";
+    private static final String BUCKET_DELETE_OBJECT = "%sbucket/objects/delete";
 
     private final EndpointService endpointService;
     private final RESTService provisioningService;
@@ -74,10 +79,11 @@ public class BucketServiceImpl implements BucketService {
     }
 
     @Override
-    public void uploadObjects(UserInfo userInfo, String bucket, String object, String endpoint, InputStream inputStream) {
+    public void uploadObjects(UserInfo userInfo, String bucket, String object, String endpoint, InputStream inputStream, long fileSize) {
+        log.info("Uploading file {} for user {} to bucket {}", object, userInfo.getName(), bucket);
         try {
             EndpointDTO endpointDTO = endpointService.get(endpoint);
-            FormDataMultiPart formData = getFormDataMultiPart(bucket, object, inputStream);
+            FormDataMultiPart formData = getFormDataMultiPart(bucket, object, inputStream, fileSize);
             Response response = provisioningService.postForm(String.format(BUCKET_UPLOAD_OBJECT, endpointDTO.getUrl()), userInfo.getAccessToken(), formData, Response.class);
             if (response.getStatus() != HttpStatus.SC_OK) {
                 throw new DlabException(String.format("Something went wrong. Response status is %s ", response.getStatus()));
@@ -86,14 +92,17 @@ public class BucketServiceImpl implements BucketService {
             log.error("Cannot upload object {} to bucket {} for user {}, endpoint {}. Reason {}", object, bucket, userInfo.getName(), endpoint, e.getMessage());
             throw new DlabException(String.format("Cannot upload object %s to bucket %s for user %s, endpoint %s. Reason %s", object, bucket, userInfo.getName(), endpoint, e.getMessage()));
         }
+        log.info("Finished uploading file {} for user {} to bucket {}", object, userInfo.getName(), bucket);
     }
 
     @Override
-    public byte[] downloadObject(UserInfo userInfo, String bucket, String object, String endpoint) {
-        try {
-            EndpointDTO endpointDTO = endpointService.get(endpoint);
-            return provisioningService.getWithMediaTypes(String.format(BUCKET_DOWNLOAD_OBJECT, endpointDTO.getUrl(), bucket, encodeObject(object)), userInfo.getAccessToken(), byte[].class,
-                    APPLICATION_JSON, APPLICATION_OCTET_STREAM);
+    public void downloadObject(UserInfo userInfo, String bucket, String object, String endpoint, HttpServletResponse resp) {
+        log.info("Downloading file {} for user {} from bucket {}", object, userInfo.getName(), bucket);
+        EndpointDTO endpointDTO = endpointService.get(endpoint);
+        try (InputStream inputStream = provisioningService.getWithMediaTypes(String.format(BUCKET_DOWNLOAD_OBJECT, endpointDTO.getUrl(), bucket, encodeObject(object)), userInfo.getAccessToken(),
+                InputStream.class, APPLICATION_JSON, APPLICATION_OCTET_STREAM); ServletOutputStream outputStream = resp.getOutputStream()) {
+            IOUtils.copyLarge(inputStream, outputStream);
+            log.info("Finished downloading file {} for user {} from bucket {}", object, userInfo.getName(), bucket);
         } catch (Exception e) {
             log.error("Cannot upload object {} from bucket {} for user {}, endpoint {}. Reason {}", object, bucket, userInfo.getName(), endpoint, e.getMessage());
             throw new DlabException(String.format("Cannot download object %s from bucket %s for user %s, endpoint %s. Reason %s", object, bucket, userInfo.getName(), endpoint, e.getMessage()));
@@ -101,29 +110,31 @@ public class BucketServiceImpl implements BucketService {
     }
 
     @Override
-    public void deleteObject(UserInfo userInfo, String bucket, String object, String endpoint) {
+    public void deleteObjects(UserInfo userInfo, String bucket, List<String> objects, String endpoint) {
         try {
             EndpointDTO endpointDTO = endpointService.get(endpoint);
-            Response response = provisioningService.delete(String.format(BUCKET_DELETE_OBJECT, endpointDTO.getUrl(), bucket, encodeObject(object)), userInfo.getAccessToken(), Response.class,
-                    APPLICATION_JSON, APPLICATION_JSON);
+            BucketDeleteDTO bucketDeleteDTO = new BucketDeleteDTO(bucket, objects);
+            Response response = provisioningService.post(String.format(BUCKET_DELETE_OBJECT, endpointDTO.getUrl()), userInfo.getAccessToken(), bucketDeleteDTO, Response.class);
             if (response.getStatus() != HttpStatus.SC_OK) {
                 throw new DlabException(String.format("Something went wrong. Response status is %s ", response.getStatus()));
             }
         } catch (Exception e) {
-            log.error("Cannot delete object {} from bucket {} for user {}, endpoint {}. Reason {}", object, bucket, userInfo.getName(), endpoint, e.getMessage());
-            throw new DlabException(String.format("Cannot delete object %s from bucket %s for user %s, endpoint %s. Reason %s", object, bucket, userInfo.getName(), endpoint, e.getMessage()));
+            log.error("Cannot delete objects {} from bucket {} for user {}, endpoint {}. Reason {}", objects, bucket, userInfo.getName(), endpoint, e.getMessage());
+            throw new DlabException(String.format("Cannot delete objects %s from bucket %s for user %s, endpoint %s. Reason %s", objects, bucket, userInfo.getName(), endpoint, e.getMessage()));
         }
     }
 
     private String encodeObject(String object) throws UnsupportedEncodingException {
-        return URLEncoder.encode(object, StandardCharsets.UTF_8.toString());
+        return URLEncoder.encode(object, StandardCharsets.UTF_8.toString()).replace("+", "%20");
     }
 
-    private FormDataMultiPart getFormDataMultiPart(String bucket, String object, InputStream inputStream) {
+    private FormDataMultiPart getFormDataMultiPart(String bucket, String object, InputStream inputStream, long fileSize) {
+        StreamDataBodyPart filePart = new StreamDataBodyPart("file", inputStream, object, MediaType.valueOf(APPLICATION_OCTET_STREAM));
         FormDataMultiPart formData = new FormDataMultiPart();
-        formData.field("file", inputStream, MediaType.valueOf(APPLICATION_OCTET_STREAM));
         formData.field("bucket", bucket);
         formData.field("object", object);
+        formData.field("file-size", String.valueOf(fileSize));
+        formData.bodyPart(filePart);
         return formData;
     }
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java
index 645f84b..fcfe789 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java
@@ -137,9 +137,9 @@ public class EndpointServiceImpl implements EndpointService {
 			response = provisioningService.get(url + HEALTH_CHECK, userInfo.getAccessToken(), Response.class);
 			cloudProvider = response.readEntity(CloudProvider.class);
 		} catch (Exception e) {
-			log.error("Cannot connect to url '{}'. {}", url, e.getMessage());
-			throw new DlabException(String.format("Cannot connect to url '%s'. %s", url, e.getMessage()));
-		}
+            log.error("Cannot connect to url '{}'. {}", url, e.getMessage());
+            throw new DlabException(String.format("Cannot connect to url '%s'.", url));
+        }
 		if (response.getStatus() != 200) {
 			log.warn("Endpoint url {} is not valid", url);
 			throw new ResourceNotFoundException(String.format("Endpoint url '%s' is not valid", url));
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java
index 8b2806b..6815f9d 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java
@@ -194,10 +194,12 @@ public class EnvironmentServiceImpl implements EnvironmentService {
 		if (projectDTO.getEndpoints() != null) {
 			final Stream<UserResourceInfo> edges = projectDTO.getEndpoints()
 					.stream()
-					.map(e -> new UserResourceInfo().withResourceType(ResourceEnum.EDGE_NODE)
-							.withResourceStatus(e.getStatus().toString())
-							.withProject(projectDTO.getName())
-							.withIp(e.getEdgeInfo() != null ? e.getEdgeInfo().getPublicIp() : null));
+					.map(e -> UserResourceInfo.builder()
+							.resourceType(ResourceEnum.EDGE_NODE)
+							.resourceStatus(e.getStatus().toString())
+							.project(projectDTO.getName())
+							.ip(e.getEdgeInfo() != null ? e.getEdgeInfo().getPublicIp() : null)
+							.build());
 			return Stream.concat(edges, userResources).collect(toList());
 		} else {
 			return userResources.collect(toList());
@@ -205,14 +207,17 @@ public class EnvironmentServiceImpl implements EnvironmentService {
 	}
 
 	private UserResourceInfo toUserResourceInfo(UserInstanceDTO userInstance) {
-		return new UserResourceInfo().withResourceType(ResourceEnum.NOTEBOOK)
-				.withResourceName(userInstance.getExploratoryName())
-				.withResourceShape(userInstance.getShape())
-				.withResourceStatus(userInstance.getStatus())
-				.withCompResources(userInstance.getResources())
-				.withUser(userInstance.getUser())
-				.withProject(userInstance.getProject())
-				.withCloudProvider(userInstance.getCloudProvider());
+		return UserResourceInfo.builder()
+				.resourceType(ResourceEnum.NOTEBOOK)
+				.resourceName(userInstance.getExploratoryName())
+				.resourceShape(userInstance.getShape())
+				.resourceStatus(userInstance.getStatus())
+				.computationalResources(userInstance.getResources())
+				.user(userInstance.getUser())
+				.project(userInstance.getProject())
+				.cloudProvider(userInstance.getCloudProvider())
+				.exploratoryUrls(userInstance.getResourceUrl())
+				.build();
 	}
 
 	private void checkProjectResourceConditions(String project, String action) {
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 08ce7b9..7554e32 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
@@ -25,6 +25,7 @@ import com.epam.dlab.backendapi.dao.BillingDAO;
 import com.epam.dlab.backendapi.dao.ExploratoryDAO;
 import com.epam.dlab.backendapi.domain.BillingReport;
 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.resources.dto.HealthStatusEnum;
 import com.epam.dlab.backendapi.resources.dto.HealthStatusPageDTO;
@@ -85,37 +86,14 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 	public List<ProjectInfrastructureInfo> getUserResources(UserInfo user) {
 		log.debug("Loading list of provisioned resources for user {}", user);
 		try {
-			Iterable<Document> documents = expDAO.findExploratory(user.getName());
 			List<EndpointDTO> allEndpoints = endpointService.getEndpoints();
-			return StreamSupport.stream(documents.spliterator(), false)
-					.collect(Collectors.groupingBy(d -> d.getString("project")))
-					.entrySet()
+			return projectService.getUserProjects(user, false)
 					.stream()
-					.map(e -> {
-						List<ProjectEndpointDTO> endpoints = projectService.get(e.getKey()).getEndpoints();
-						List<EndpointDTO> endpointResult = allEndpoints.stream()
-								.filter(endpoint -> endpoints.stream()
-										.anyMatch(endpoint1 -> endpoint1.getName().equals(endpoint.getName())))
-								.collect(Collectors.toList());
-
-						List<BillingReport> billingData = e.getValue()
-								.stream()
-								.map(exp ->
-										billingService.getExploratoryBillingData(exp.getString("project"), exp.getString("endpoint"),
-												exp.getString("exploratory_name"),
-												Optional.ofNullable(exp.get("computational_resources")).map(cr -> (List<Document>) cr).get()
-														.stream()
-														.map(cr -> cr.getString("computational_name"))
-														.collect(Collectors.toList()))
-								)
-								.collect(Collectors.toList());
-
-						final Map<String, Map<String, String>> projectEdges =
-								endpoints
-										.stream()
-										.collect(Collectors.toMap(ProjectEndpointDTO::getName, this::getSharedInfo));
-						return new ProjectInfrastructureInfo(e.getKey(), billingDAO.getBillingProjectQuoteUsed(e.getKey()),
-								projectEdges, e.getValue(), billingData, endpointResult);
+					.map(p -> {
+						Iterable<Document> exploratories = expDAO.findExploratories(user.getName(), p.getName());
+						return new ProjectInfrastructureInfo(p.getName(), billingDAO.getBillingProjectQuoteUsed(p.getName()),
+								getSharedInfo(p.getName()), exploratories, getExploratoryBillingData(exploratories),
+								getEndpoints(allEndpoints, p));
 					})
 					.collect(Collectors.toList());
 		} catch (Exception e) {
@@ -163,6 +141,30 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 				.build();
 	}
 
+	private List<BillingReport> getExploratoryBillingData(Iterable<Document> exploratories) {
+		return StreamSupport.stream(exploratories.spliterator(), false)
+				.map(exp ->
+						billingService.getExploratoryBillingData(exp.getString("project"), exp.getString("endpoint"),
+								exp.getString("exploratory_name"),
+								Optional.ofNullable(exp.get("computational_resources")).map(cr -> (List<Document>) cr).get()
+										.stream()
+										.map(cr -> cr.getString("computational_name"))
+										.collect(Collectors.toList()))
+				)
+				.collect(Collectors.toList());
+	}
+
+	private List<EndpointDTO> getEndpoints(List<EndpointDTO> allEndpoints, ProjectDTO projectDTO) {
+		return allEndpoints.stream().filter(endpoint -> projectDTO.getEndpoints().stream()
+				.anyMatch(endpoint1 -> endpoint1.getName().equals(endpoint.getName())))
+				.collect(Collectors.toList());
+	}
+
+	private Map<String, Map<String, String>> getSharedInfo(String name) {
+		return projectService.get(name).getEndpoints().stream()
+				.collect(Collectors.toMap(ProjectEndpointDTO::getName, this::getSharedInfo));
+	}
+
 	private Map<String, String> getSharedInfo(ProjectEndpointDTO endpointDTO) {
 		Optional<EdgeInfo> edgeInfo = Optional.ofNullable(endpointDTO.getEdgeInfo());
 		if (!edgeInfo.isPresent()) {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureTemplateServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureTemplateServiceImpl.java
index 31f73f3..9f3f05f 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureTemplateServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureTemplateServiceImpl.java
@@ -90,8 +90,7 @@ public class InfrastructureTemplateServiceImpl implements InfrastructureTemplate
 					.peek(e -> e.setImage(getSimpleImageName(e.getImage())))
 					.filter(e -> exploratoryGpuIssuesAzureFilter(e, endpointDTO.getCloudProvider()) &&
 							UserRoles.checkAccess(user, RoleType.EXPLORATORY, e.getImage(), roles))
-					.peek(e -> filterShapes(user, e.getExploratoryEnvironmentShapes(), RoleType.EXPLORATORY_SHAPES,
-							roles))
+					.peek(e -> filterShapes(user, e.getExploratoryEnvironmentShapes(), RoleType.EXPLORATORY_SHAPES, roles))
 					.collect(Collectors.toList());
 
 		} catch (DlabException e) {
@@ -100,20 +99,6 @@ public class InfrastructureTemplateServiceImpl implements InfrastructureTemplate
 		}
 	}
 
-	/**
-	 * Removes shapes for which user does not have an access
-	 *
-	 * @param user              user
-	 * @param environmentShapes shape types
-	 * @param roleType
-	 * @param roles
-	 */
-	private void filterShapes(UserInfo user, Map<String, List<ComputationalResourceShapeDto>> environmentShapes,
-							  RoleType roleType, Set<String> roles) {
-		environmentShapes.forEach((k, v) -> v.removeIf(compResShapeDto ->
-				!UserRoles.checkAccess(user, roleType, compResShapeDto.getType(), roles)));
-	}
-
 	@Override
 	public List<FullComputationalTemplate> getComputationalTemplates(UserInfo user, String project, String endpoint) {
 
@@ -129,8 +114,7 @@ public class InfrastructureTemplateServiceImpl implements InfrastructureTemplate
 
 			return Arrays.stream(array)
 					.peek(e -> e.setImage(getSimpleImageName(e.getImage())))
-					.peek(e -> filterShapes(user, e.getComputationResourceShapes(), RoleType.COMPUTATIONAL_SHAPES,
-							user.getRoles()))
+					.peek(e -> filterShapes(user, e.getComputationResourceShapes(), RoleType.COMPUTATIONAL_SHAPES, roles))
 					.filter(e -> UserRoles.checkAccess(user, RoleType.COMPUTATIONAL, e.getImage(), roles))
 					.map(comp -> fullComputationalTemplate(comp, endpointDTO.getCloudProvider()))
 					.collect(Collectors.toList());
@@ -142,6 +126,21 @@ public class InfrastructureTemplateServiceImpl implements InfrastructureTemplate
 	}
 
 	/**
+	 * Removes shapes for which user does not have an access
+	 *
+	 * @param user              user
+	 * @param environmentShapes shape types
+	 * @param roleType
+	 * @param roles
+	 */
+	private void filterShapes(UserInfo user, Map<String, List<ComputationalResourceShapeDto>> environmentShapes,
+							  RoleType roleType, Set<String> roles) {
+		environmentShapes.forEach((k, v) -> v.removeIf(compResShapeDto ->
+				!UserRoles.checkAccess(user, roleType, compResShapeDto.getType(), roles))
+		);
+	}
+
+	/**
 	 * Temporary filter for creation of exploratory env due to Azure issues
 	 */
 	private boolean exploratoryGpuIssuesAzureFilter(ExploratoryMetadataDTO e, CloudProvider cloudProvider) {
diff --git a/services/self-service/src/main/resources/mongo/aws/mongo_roles.json b/services/self-service/src/main/resources/mongo/aws/mongo_roles.json
index ee7d97e..f727854 100644
--- a/services/self-service/src/main/resources/mongo/aws/mongo_roles.json
+++ b/services/self-service/src/main/resources/mongo/aws/mongo_roles.json
@@ -325,7 +325,7 @@
   },
   {
     "_id": "bucketBrowserView",
-    "description": "Allow to view objects within the bucket",
+    "description": "Allow to view object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -337,7 +337,7 @@
   },
   {
     "_id": "bucketBrowserUpload",
-    "description": "Allow to upload object to the bucket",
+    "description": "Allow to upload object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -349,7 +349,7 @@
   },
   {
     "_id": "bucketBrowserDownload",
-    "description": "Allow to download object from the bucket",
+    "description": "Allow to download object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -361,7 +361,7 @@
   },
   {
     "_id": "bucketBrowserDelete",
-    "description": "Allow to delete object from the bucket",
+    "description": "Allow to delete object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
diff --git a/services/self-service/src/main/resources/mongo/azure/mongo_roles.json b/services/self-service/src/main/resources/mongo/azure/mongo_roles.json
index 1cd44b1..6b8f829 100644
--- a/services/self-service/src/main/resources/mongo/azure/mongo_roles.json
+++ b/services/self-service/src/main/resources/mongo/azure/mongo_roles.json
@@ -265,7 +265,7 @@
   },
   {
     "_id": "bucketBrowserView",
-    "description": "Allow to view objects within the bucket",
+    "description": "Allow to view object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -277,7 +277,7 @@
   },
   {
     "_id": "bucketBrowserUpload",
-    "description": "Allow to upload object to the bucket",
+    "description": "Allow to upload object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -289,7 +289,7 @@
   },
   {
     "_id": "bucketBrowserDownload",
-    "description": "Allow to download object from the bucket",
+    "description": "Allow to download object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -301,7 +301,7 @@
   },
   {
     "_id": "bucketBrowserDelete",
-    "description": "Allow to delete object from the bucket",
+    "description": "Allow to delete object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
diff --git a/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json b/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json
index 8a36ff4..11e8731 100644
--- a/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json
+++ b/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json
@@ -301,7 +301,7 @@
   },
   {
     "_id": "bucketBrowserView",
-    "description": "Allow to view objects within the bucket",
+    "description": "Allow to view object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -313,7 +313,7 @@
   },
   {
     "_id": "bucketBrowserUpload",
-    "description": "Allow to upload object to the bucket",
+    "description": "Allow to upload object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -325,7 +325,7 @@
   },
   {
     "_id": "bucketBrowserDownload",
-    "description": "Allow to download object from the bucket",
+    "description": "Allow to download object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
@@ -337,7 +337,7 @@
   },
   {
     "_id": "bucketBrowserDelete",
-    "description": "Allow to delete object from the bucket",
+    "description": "Allow to delete object via bucket browser",
     "type": "BUCKET_BROWSER",
     "cloud": "GCP",
     "pages": [
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html
index 74ff5af..e3c77b8 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html
@@ -97,7 +97,7 @@
             </div>
             <div class="text-center m-top-30">
               <button mat-raised-button type="button" (click)="dialogRef.close()" class="butt action">Cancel</button>
-              <button mat-raised-button type="submit" [disabled]="!manageUsersForm.valid" class="butt butt-success"
+              <button mat-raised-button type="submit" [disabled]="!manageUsersForm.valid || isFormChanged" class="butt butt-success"
                 [ngClass]="{'not-allowed': !manageUsersForm.valid}">Apply</button>
             </div>
           </mat-list>
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts
index f99944f..0b405be 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts
@@ -37,6 +37,8 @@ export class ManageEnvironmentComponent implements OnInit {
   public manageTotalsForm: FormGroup;
 
   @Output() manageEnv: EventEmitter<{}> = new EventEmitter();
+  private initialFormState: any;
+  private isFormChanged: boolean = true;
 
   constructor(
     @Inject(MAT_DIALOG_DATA) public data: any,
@@ -50,10 +52,12 @@ export class ManageEnvironmentComponent implements OnInit {
     this.setProjectsControl();
     this.manageUsersForm.controls['total'].setValue(this.data.total.conf_max_budget || '');
     this.onFormChange();
+    this.initialFormState = this.manageUsersForm.value;
   }
 
   public onFormChange() {
     this.manageUsersForm.valueChanges.subscribe(value => {
+      this.isFormChanged = JSON.stringify(this.initialFormState) === JSON.stringify(this.manageUsersForm.value);
       if ((this.getCurrentTotalValue() && this.getCurrentTotalValue() >= this.getCurrentUsersTotal())) {
         this.manageUsersForm.controls['projects']['controls'].forEach(v => {
             v.controls['budget'].setErrors(null);
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index b4f726c..8b7e459 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -60,7 +60,10 @@
             <span [hidden]="filtering && filterForm.type.length > 0 && !collapsedFilterRow">more_vert</span>
           </i>
         </button> </th>
-      <td type mat-cell *matCellDef="let element">{{ element.name || element.type }}</td>
+      <td type mat-cell *matCellDef="let element">
+        <span *ngIf="element.name"  class="computation" (click)="openNotebookDetails(element)">{{element.name}}</span>
+        <span *ngIf="!element.name">{{element.type}}</span>
+      </td>
     </ng-container>
 
     <ng-container matColumnDef="shape">
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss
index 30c082d..5d40c3e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss
@@ -42,7 +42,6 @@
 
     .type {
       width: 14%;
-      padding-left: 20px;
     }
 
     .resources {
@@ -119,3 +118,7 @@ table.management {
     background: inherit;
   }
 }
+
+.computation{
+  cursor: pointer;
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
index d0ab9dc..796859a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
@@ -28,6 +28,7 @@ import { ConfirmationDialogComponent } from '../../../shared/modal-dialog/confir
 import { EnvironmentsDataService } from '../management-data.service';
 import { EnvironmentModel, ManagementConfigModel } from '../management.model';
 import {ProgressBarService} from '../../../core/services/progress-bar.service';
+import {DetailDialogComponent} from '../../../resources/exploratory/detail-dialog';
 
 export interface ManageAction {
   action: string;
@@ -112,7 +113,9 @@ export class ManagementGridComponent implements OnInit {
     let filteredData = this.getEnvironmentDataCopy();
 
     const containsStatus = (list, selectedItems) => {
-      return list.filter((item: any) => { if (selectedItems.indexOf(item.status) !== -1) return item; });
+      if (list){
+        return list.filter((item: any) => { if (selectedItems.indexOf(item.status) !== -1) return item; });
+      }
     };
 
     if (filteredData.length) this.filtering = true;
@@ -131,8 +134,8 @@ export class ManagementGridComponent implements OnInit {
 
         if (config.resources.length > 0 && modifiedResources.length > 0) { item.resources = modifiedResources; }
 
-        if (config.resources.length === 0 && config.type === 'active' ||
-          modifiedResources.length >= 0 && config.resources.length > 0 && config.type === 'active') {
+        if (config.resources && config.resources.length === 0 && config.type === 'active' ||
+          modifiedResources && modifiedResources.length >= 0 && config.resources.length > 0 && config.type === 'active') {
           item.resources = modifiedResources;
           isResources = true;
         }
@@ -216,15 +219,27 @@ export class ManagementGridComponent implements OnInit {
       if (item.status && statuses.indexOf(item.status.toLowerCase()) === -1) statuses.push(item.status.toLowerCase());
       if (item.project && projects.indexOf(item.project) === -1) projects.push(item.project);
       if (item.shape && shapes.indexOf(item.shape) === -1) shapes.push(item.shape);
-
-      item.computational_resources.map((resource: any) => {
-        if (resources.indexOf(resource.status) === -1) resources.push(resource.status);
-        resources.sort(SortUtils.statusSort);
-      });
+      if (item.computational_resources) {
+         item.computational_resources.map((resource: any) => {
+              if (resources.indexOf(resource.status) === -1) resources.push(resource.status);
+              resources.sort(SortUtils.statusSort);
+            });
+      }
     });
 
     this.filterConfiguration = new ManagementConfigModel(users, '', projects, shapes, statuses, resources);
   }
+
+  openNotebookDetails(data) {
+    if (!data.exploratory_urls || !data.exploratory_urls.length) {
+      return;
+    }
+    this.dialog.open(DetailDialogComponent, { data:
+        {notebook: data, bucketStatus: {view: true, upload: true, download: true, delete: true},  buckets: [], type: 'environment'},
+      panelClass: 'modal-lg'
+    })
+      .afterClosed().subscribe(() => {});
+  }
 }
 
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
index 2b2fdf3..6b0f600 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
@@ -27,7 +27,8 @@ export class EnvironmentModel {
     public ip: string,
     public type?: string,
     public project?: string,
-    public cloud_provider?: string
+    public cloud_provider?: string,
+    public exploratory_urls?: Array<any>
   ) { }
 
   public static loadEnvironments(data: Array<any>) {
@@ -41,7 +42,8 @@ export class EnvironmentModel {
         value.public_ip,
         value.resource_type,
         value.project,
-        value.cloud_provider
+        value.cloud_provider,
+        value.exploratory_urls
       ));
     }
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/app.module.ts b/services/self-service/src/main/resources/webapp/src/app/app.module.ts
index e23e14a..91ec87d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/app.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/app.module.ts
@@ -30,17 +30,17 @@ import { AppComponent } from './app.component';
 import { AppRoutingModule } from './app.routing.module';
 
 import { LoginModule } from './login/login.module';
-import { LayoutModule } from './layout/layout.module'
+import { LayoutModule } from './layout/layout.module';
 
 import { GuidesModule } from './help';
 import { ServicePagesModule } from './service-pages/service-pages.module';
 import { ResourcesModule } from './resources/resources.module';
-
-import { ReportingModule } from './reporting/reporting.module';
 import { AdministrationModule } from './administration/administration.module';
 import { WebterminalModule } from './webterminal';
 import { CoreModule } from './core/core.module';
 import { SwaggerAPIModule } from './swagger';
+import {ReportsModule} from './reports/reports.module';
+
 
 @NgModule({
   declarations: [AppComponent],
@@ -55,8 +55,10 @@ import { SwaggerAPIModule } from './swagger';
     ResourcesModule,
     GuidesModule,
     ServicePagesModule,
-    ReportingModule,
+    // ReportingModule,
+
     AdministrationModule,
+    ReportsModule,
     WebterminalModule,
     SwaggerAPIModule,
     RouterModule,
diff --git a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
index 3c8ae3f..b8792e5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
@@ -21,12 +21,12 @@ import { ModuleWithProviders } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
 
 import { LoginComponent } from './login/login.module';
-import { LayoutComponent } from './layout/layout.component'
+import { LayoutComponent } from './layout/layout.component';
 import { ResourcesComponent } from './resources/resources.component';
 import { AccessNotebookGuideComponent, PublicKeyGuideComponent } from './help';
 import { NotFoundComponent } from './service-pages/not-found/not-found.component';
 import { AccessDeniedComponent } from './service-pages/access-denied/access-denied.component';
-import { ReportingComponent } from './reporting/reporting.component';
+import { ReportingComponent } from './reports/reporting/reporting.component';
 import { WebterminalComponent } from './webterminal/webterminal.component';
 import { ManagementComponent } from './administration/management/management.component';
 import { ProjectComponent } from './administration/project/project.component';
@@ -34,6 +34,7 @@ import { RolesComponent } from './administration/roles/roles.component';
 import { SwaggerComponent } from './swagger/swagger.component';
 
 import { AuthorizationGuard, CheckParamsGuard, CloudProviderGuard, AdminGuard } from './core/services';
+import {AuditComponent} from './reports/audit/audit.component';
 
 const routes: Routes = [{
   path: 'login',
@@ -79,7 +80,12 @@ const routes: Routes = [{
       path: 'help/accessnotebookguide',
       component: AccessNotebookGuideComponent,
       canActivate: [AuthorizationGuard]
-    }
+    },
+    {
+      path: 'audit',
+      component: AuditComponent,
+      canActivate: [AuthorizationGuard, AdminGuard],
+    },
   ]
 }, {
   path: 'terminal/:id/:endpoint',
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-file-size/convert-file-size.pipe.ts
similarity index 60%
copy from services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
copy to services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-file-size/convert-file-size.pipe.ts
index f3ed208..0bd2f61 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/BucketService.java
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-file-size/convert-file-size.pipe.ts
@@ -17,21 +17,23 @@
  * under the License.
  */
 
-package com.epam.dlab.backendapi.service;
-
-import com.epam.dlab.dto.bucket.BucketDTO;
-
-import java.io.InputStream;
-import java.util.List;
-
-public interface BucketService {
-    String DATE_FORMAT = "dd-M-yyyy hh:mm:ss";
-
-    List<BucketDTO> getObjects(String bucket);
-
-    void uploadObject(String bucket, String object, InputStream stream);
-
-    byte[] downloadObject(String bucket, String object);
-
-    void deleteObject(String bucket, String object);
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'convertFileSize' })
+
+export class ConvertFileSizePipe implements PipeTransform {
+  transform(bytes: number): any {
+      const sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
+      if (bytes === 0) {
+        return '0 byte';
+      }
+      for (let i = 0; i < sizes.length; i++) {
+        if (bytes <= 1024) {
+          return bytes + ' ' + sizes[i];
+        } else {
+          bytes = parseFloat((bytes / 1024).toFixed(2));
+        }
+      }
+      return bytes;
+    }
 }
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-file-size/index.ts
similarity index 65%
copy from services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
copy to services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-file-size/index.ts
index b1201e6..2080443 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/convert-file-size/index.ts
@@ -17,17 +17,14 @@
  * under the License.
  */
 
-package com.epam.dlab.dto.bucket;
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ConvertFileSizePipe } from './convert-file-size.pipe';
 
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import lombok.Data;
-import org.hibernate.validator.constraints.NotBlank;
+@NgModule({
+  imports: [CommonModule],
+  declarations: [ConvertFileSizePipe],
+  exports: [ConvertFileSizePipe]
+})
 
-@Data
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class BucketDownloadDTO {
-    @NotBlank(message = "field cannot be empty")
-    private final String bucket;
-    @NotBlank(message = "field cannot be empty")
-    private final String object;
-}
+export class ConvertFileSizePipeModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
index 0f05bcb..1a80759 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
@@ -34,7 +34,6 @@ export class AppRoutingService {
   }
 
   redirectToHomePage(): void {
-    console.log('redirect');
     this.router.navigate(['/resources_list']);
   }
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
index 6ac4fc1..7f125b3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
@@ -77,6 +77,7 @@ export class ApplicationServiceFacade {
   private static readonly PROJECT = 'project';
   private static readonly ENDPOINT = 'endpoint';
   private static readonly ENDPOINT_CONNECTION = 'endpoint_connection';
+  private static readonly AUDIT = 'audit';
 
   private requestRegistry: Dictionary<string>;
 
@@ -263,7 +264,7 @@ export class ApplicationServiceFacade {
   public buildUploadFileToBucket(data): Observable<any> {
     return this.buildRequest(HTTPMethod.POST,
       this.requestRegistry.Item(ApplicationServiceFacade.BUCKET) + '/upload',
-      data);
+      data, { reportProgress: true, observe: 'events' });
   }
 
   public buildDownloadFileFromBucket(data) {
@@ -271,12 +272,12 @@ export class ApplicationServiceFacade {
       this.requestRegistry.Item(ApplicationServiceFacade.BUCKET),
       data, { dataType : 'binary',
         processData : false,
-        responseType : 'arraybuffer' } );
+        responseType : 'arraybuffer', reportProgress: true, observe: 'events' } );
   }
 
   public buildDeleteFileFromBucket(data): Observable<any> {
-    return this.buildRequest(HTTPMethod.DELETE,
-      this.requestRegistry.Item(ApplicationServiceFacade.BUCKET),
+    return this.buildRequest(HTTPMethod.POST,
+      this.requestRegistry.Item(ApplicationServiceFacade.BUCKET) + '/objects/delete',
       data );
   }
 
@@ -627,6 +628,18 @@ export class ApplicationServiceFacade {
       null);
   }
 
+  public getAuditList(): Observable<any> {
+    return this.buildRequest(HTTPMethod.GET,
+      this.requestRegistry.Item(ApplicationServiceFacade.AUDIT),
+      null);
+  }
+
+  public postActionToAudit(data): Observable<any> {
+    return this.buildRequest(HTTPMethod.POST,
+      this.requestRegistry.Item(ApplicationServiceFacade.AUDIT),
+      data);
+  }
+
   private setupRegistry(): void {
     this.requestRegistry = new Dictionary<string>();
 
@@ -709,6 +722,9 @@ export class ApplicationServiceFacade {
     this.requestRegistry.Add(ApplicationServiceFacade.PROJECT, '/api/project');
     this.requestRegistry.Add(ApplicationServiceFacade.ENDPOINT, '/api/endpoint');
     this.requestRegistry.Add(ApplicationServiceFacade.ENDPOINT_CONNECTION, '/api/endpoint/url/');
+
+    // audit
+    this.requestRegistry.Add(ApplicationServiceFacade.AUDIT, '/api/audit');
   }
 
   private buildRequest(method: HTTPMethod, url_path: string, body: any, opt?) {
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/audit.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/audit.service.ts
new file mode 100644
index 0000000..70d1f17
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/audit.service.ts
@@ -0,0 +1,44 @@
+import { Injectable } from '@angular/core';
+import {ApplicationServiceFacade} from './applicationServiceFacade.service';
+import {catchError, map} from 'rxjs/operators';
+import {ErrorUtils} from '../util';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AuditService {
+  constructor(private applicationServiceFacade: ApplicationServiceFacade) {
+  }
+
+  public getAuditData() {
+    return this.applicationServiceFacade
+      .getAuditList()
+      .pipe(
+        map(response => response),
+        catchError(ErrorUtils.handleServiceError));
+  }
+
+  public sendDataToAudit(data) {
+    return this.applicationServiceFacade
+      .postActionToAudit(data)
+      .pipe(
+        map(response => response),
+        catchError(ErrorUtils.handleServiceError));
+  }
+    // return [
+    //   {user: 'Dlab-test-user1', action: 'Deleted users from group', project: '', date: new Date().toLocaleString(), info: {name: 'admin', objects: ['user1', 'user2', 'user3', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1']}},
+    //   {user: 'Dlab-test-user1', action: 'Created project', project: 'ProjectA', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user1', action: 'Created project', project: 'ProjectA', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user1', action: 'Created project', project: 'ProjectA', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user2', action: 'Created notebook ', project: 'ProjectA', resource: 'Rstudio', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user1', action: 'Deleted user to group', project: '', date: new Date().toLocaleString(), info: {name: 'admin', objects: ['user1', 'user2', 'user3', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1']}},
+    //   {user: 'Dlab-test-user1', action: 'Stopped notebook', project: 'ProjectA', resource: 'Rstudio', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user1', action: 'Started notebook', project: 'ProjectA', resource: 'Rstudio', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user1', action: 'Deleted Users from group', project: '', date: new Date().toLocaleString(), info: {name: 'admin', objects: ['user1', 'user2', 'user3', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1']}},
+    //   {user: 'Dlab-test-user3', action: 'Created EMR', project: 'ProjectA', resource: 'Rstudio:Emr1', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user1', action: 'Created notebook', project: 'ProjectA', resource: 'Rstudio', date: new Date().toLocaleString()},
+    //   {user: 'Dlab-test-user1', action: 'Deleted user to group', project: '', date: new Date().toLocaleString(), info: {name: 'admin', objects: ['user1', 'user2', 'user3', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1', 'Dlab-test-user1']}},
+    //   {user: 'Dlab-test-user2', action: 'Terminated notebook', project: 'ProjectA', resource: 'Rstudio', date: new Date().toLocaleString()},
+    //   ];
+
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
index c251ed9..8b2c4f6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
@@ -8,7 +8,7 @@ export class TodoItemNode {
   children: TodoItemNode[];
   item: string;
   id: string;
-  size: number;
+  object: any;
 }
 
 export class TodoItemFlatNode {
@@ -50,12 +50,10 @@ export class BucketBrowserService {
   }
 
   public deleteFile(data) {
-    const url = JSON.stringify(data);
     return this.applicationServiceFacade
-      .buildDeleteFileFromBucket(url)
+      .buildDeleteFileFromBucket(data)
       .pipe(
         map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
-
 }
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java b/services/self-service/src/main/resources/webapp/src/app/core/util/copyPathUtils.ts
similarity index 65%
copy from services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
copy to services/self-service/src/main/resources/webapp/src/app/core/util/copyPathUtils.ts
index b1201e6..86ff250 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
+++ b/services/self-service/src/main/resources/webapp/src/app/core/util/copyPathUtils.ts
@@ -17,17 +17,18 @@
  * under the License.
  */
 
-package com.epam.dlab.dto.bucket;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import lombok.Data;
-import org.hibernate.validator.constraints.NotBlank;
-
-@Data
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class BucketDownloadDTO {
-    @NotBlank(message = "field cannot be empty")
-    private final String bucket;
-    @NotBlank(message = "field cannot be empty")
-    private final String object;
+export class CopyPathUtils {
+  public static copyPath(copyValue) {
+    const selBox = document.createElement('textarea');
+    selBox.style.position = 'fixed';
+    selBox.style.left = '0';
+    selBox.style.top = '0';
+    selBox.style.opacity = '0';
+    selBox.value = copyValue;
+    document.body.appendChild(selBox);
+    selBox.focus();
+    selBox.select();
+    document.execCommand('copy');
+    document.body.removeChild(selBox);
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/util/patterns.ts b/services/self-service/src/main/resources/webapp/src/app/core/util/patterns.ts
index ac9137f..8246bba 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/util/patterns.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/util/patterns.ts
@@ -24,5 +24,6 @@ export const PATTERNS = {
   url: '[a-zA-Z0-9.://%#&\\.@:%-_\+~#=]*\.[^\s]*[a-zA-Z0-9]/+',
   nodeCountPattern: '^[1-9]\\d*$',
   integerRegex: '^[0-9]*$',
+  folderRegex: /^[a-zA-Z0-9!@$%^&*()_+\-=\[\]{};':|,.<>~` ]*$/,
   fullUrl: /^(http?|ftp|https):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+([.:])(\d{4}|com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*\/$/
 };
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts b/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts
index 0a613ad..2573d0d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts
@@ -34,7 +34,11 @@ export class SortUtils {
 
     Object.keys(shapesJson)
       .sort((a, b) => sortOrder.indexOf(a) - sortOrder.indexOf(b))
-      .forEach(key => { sortedShapes[key] = shapesJson[key]; });
+      .forEach(key => {
+        if (shapesJson[key].length) {
+          sortedShapes[key] = shapesJson[key];
+        }
+      });
 
     return sortedShapes;
   }
@@ -48,5 +52,5 @@ export class SortUtils {
   public static flatDeep(arr, d = 1) {
     return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? this.flatDeep(val, d - 1) : val), [])
       : arr.slice();
-  };
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.html
new file mode 100644
index 0000000..d280224
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.html
@@ -0,0 +1,147 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<section class="audit-table-wrapper">
+  <table mat-table [dataSource]="auditData" class="data-grid audit mat-elevation-z6">
+
+    <ng-container matColumnDef="user">
+      <th mat-header-cell *matHeaderCellDef class="th_user label-header">
+        <div class="label"><span> User</span></div>
+        <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+          <i class="material-icons">
+            <span *ngIf="filterAuditData.users.length > 0; else user_filtered">filter_list</span>
+            <ng-template #user_filtered>more_vert</ng-template>
+          </i>
+        </button>
+      </th>
+      <td mat-cell *matCellDef="let element"><span class="table-item user-col">{{element.user}}</span></td>
+      <td mat-footer-cell *matFooterCellDef class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="project">
+      <th mat-header-cell *matHeaderCellDef class="th_project label-header">
+        <div class="label"><span class="text">Project</span></div>
+        <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+          <i class="material-icons">
+            <span *ngIf="filterAuditData.users.length > 0; else user_filtered">filter_list</span>
+            <ng-template #user_filtered>more_vert</ng-template>
+          </i>
+        </button>
+      </th>
+      <td mat-cell *matCellDef="let element"><span class="table-item">{{element.project}}</span></td>
+      <td mat-footer-cell *matFooterCellDef class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="resource">
+      <th mat-header-cell *matHeaderCellDef class="th_resource label-header">
+        <div class="label"><span class="text">Resource</span></div>
+        <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+          <i class="material-icons">
+            <span *ngIf="filterAuditData.users.length > 0; else user_filtered">filter_list</span>
+            <ng-template #user_filtered>more_vert</ng-template>
+          </i>
+        </button>
+      </th>
+      <td mat-cell *matCellDef="let element"><span class="table-item">{{element.resource}}</span></td>
+      <td mat-footer-cell *matFooterCellDef class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="action">
+      <th mat-header-cell *matHeaderCellDef class="th_action label-header">
+<!--        <div class="sort">-->
+<!--          <div class="sort-arrow up" (click)="sortBy('user', 'down')" [ngClass]="{'active': !!this.active['userdown']}"></div>-->
+<!--          <div class="sort-arrow down" (click)="sortBy('user', 'up')" [ngClass]="{'active': !!this.active['userup']}"></div>-->
+<!--        </div>-->
+        <div class="label">
+          <span class="text"> Action </span>
+        </div>
+        <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+          <i class="material-icons">
+            <span *ngIf="filterAuditData.actions.length > 0; else action_filtered">filter_list</span>
+            <ng-template #action_filtered>more_vert</ng-template>
+          </i>
+        </button>
+      </th>
+      <td mat-cell *matCellDef=" let element">
+        <div class="action-wrapper">
+          <span>{{element.action}}</span>
+          <div class="audit-info" (click)="openActionInfo(element)" *ngIf="element.info">
+            <i class="material-icons">info</i>
+          </div>
+        </div>
+      </td>
+      <td mat-footer-cell *matFooterCellDef  class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="date">
+      <th mat-header-cell *matHeaderCellDef class="th_date label-header">
+<!--        <div class="sort">-->
+<!--          <div class="sort-arrow up" (click)="sortBy('project', 'down')" [ngClass]="{'active': !!this.active['projectdown']}"></div>-->
+<!--          <div class="sort-arrow down" (click)="sortBy('project', 'up')" [ngClass]="{'active': !!this.active['projectup']}"></div>-->
+<!--        </div>-->
+        <div class="label"><span class="text">Date</span></div>
+      </th>
+      <td mat-cell *matCellDef="let element"> {{element.date}} </td>
+      <td mat-footer-cell *matFooterCellDef class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="user-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+        <multi-select-dropdown *ngIf="filterConfiguration" (selectionChange)="onUpdate($event)" [type]="'users'"
+                               [items]="filterConfiguration.users" [model]="filterAuditData.users"></multi-select-dropdown>
+      </th>
+    </ng-container>
+
+    <ng-container matColumnDef="project-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+        <multi-select-dropdown *ngIf="filterConfiguration" (selectionChange)="onUpdate($event)" [type]="'projects'"
+                               [items]="filterConfiguration.users" [model]="filterAuditData.users"></multi-select-dropdown>
+      </th>
+    </ng-container>
+
+    <ng-container matColumnDef="resource-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+        <multi-select-dropdown *ngIf="filterConfiguration" (selectionChange)="onUpdate($event)" [type]="'resources'"
+                               [items]="filterConfiguration.users" [model]="filterAuditData.users"></multi-select-dropdown>
+      </th>
+    </ng-container>
+
+    <ng-container matColumnDef="action-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+<!--        <multi-select-dropdown *ngIf="filterConfiguration" (selectionChange)="onUpdate($event)" [type]="'actions'"-->
+<!--                               [items]="filterConfiguration.actions" [model]="filterAuditData.actions"></multi-select-dropdown>-->
+      </th>
+    </ng-container>
+    <ng-container matColumnDef="date-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+
+      </th>
+    </ng-container>
+
+    <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true" class="header-row"></tr>
+
+    <tr [hidden]="!collapseFilterRow" mat-header-row *matHeaderRowDef="displayedFilterColumns; sticky: true"
+      class="filter-row"></tr>
+    <tr mat-row *matRowDef="let row; columns: displayedColumns;" class="content-row"></tr>
+
+<!--    <tr [hidden]="!auditData?.length" mat-footer-row *matFooterRowDef="displayedColumns; sticky: true"-->
+<!--      class="header-row"></tr>-->
+<!--    <tr [hidden]="reportData?.length" mat-footer-row *matFooterRowDef="['placeholder']"></tr>-->
+  </table>
+</section>
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.scss
new file mode 100644
index 0000000..09b1baa
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.scss
@@ -0,0 +1,237 @@
+/*!
+ * 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.
+ */
+
+.audit-table-wrapper {
+  width: 100%;
+
+  .audit {
+    width: 100%;
+    min-width: 1100px;
+    overflow: auto;
+    border-collapse: inherit;
+
+    .mat-cell {
+      vertical-align: middle;
+    }
+
+    tr {
+      .th_user {
+        width: 25%;
+      }
+
+      .th_action {
+        width: 20%;
+      }
+
+      .th_date {
+        width: 20%;
+      }
+
+      .th_project{
+        width: 15%;
+      }
+
+      .th_resource{
+        width: 20%;
+      }
+
+      th {
+        padding-right: 5px;
+        z-index: 2 !important;
+
+        &.th_charges {
+          z-index: 3 !important;
+        }
+      }
+
+      td {
+        font-size: 13px;
+        padding-left: 15px;
+
+        &.info {
+          z-index: 1 !important;
+          text-align: center;
+          padding: 40px;
+        }
+      }
+
+      &.filter-row {
+        th {
+          padding: 5px;
+          font-size: 13px;
+        }
+
+        .filter-field {
+          font-size: 13px;
+        }
+
+      }
+
+      &.header-row {
+        th {
+          font-size: 11px;
+
+          .label {
+            padding-left: 0;
+          }
+        }
+      }
+    }
+
+
+    .tags {
+      .label {
+        padding-top: 0;
+      }
+    }
+
+    .service {
+      min-width: 175px;
+    }
+
+    .env_name {
+      width: 16%;
+      min-width: 200px;
+    }
+
+    .th_project {
+      width: 12%;
+    }
+
+    .th_type {
+      width: 10%;
+      min-width: 150px;
+    }
+
+    .th_status {
+      width: 8%;
+      min-width: 150px;
+    }
+
+    .th_charges {
+      width: 10%;
+      min-width: 155px;
+      text-align: right;
+
+      .label {
+        padding-top: 0;
+      }
+    }
+
+    .th_project {
+      min-width: 150px;
+    }
+
+    .tags-col {
+      padding: 5px;
+
+      mat-chip {
+        min-height: 20px;
+        padding: 5px 10px;
+        font-size: 13px;
+        max-width: 110px !important;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        display: inline-block;
+        line-height: 10px;
+        margin: 2px;
+      }
+    }
+
+    .mat-column-charge {
+      text-align: right;
+    }
+
+    .header-row {
+      position: unset;
+
+      .th_charges {
+        padding-top: 0;
+
+        .label {
+          padding-top: 12px;
+        }
+      }
+
+      .label {
+        display: inline-block;
+        padding-top: 13px;
+        vertical-align: super !important;
+
+        .text {
+          padding-left: 15px;
+        }
+      }
+
+      .sort {
+        position: absolute;
+        bottom: 20px;
+
+        &-arrow {
+          width: 6px;
+          height: 6px;
+          border: 3px solid transparent;
+          border-bottom: 3px solid rgba(0, 0, 0, .54);
+          border-left: 3px solid rgba(0, 0, 0, .54);
+          cursor: pointer;
+
+          &.active {
+            border-bottom: 3px solid #35afd5;
+            border-left: 3px solid #35afd5;
+          }
+        }
+
+        .down {
+          transform: rotate(-45deg);
+        }
+
+        .up {
+          transform: rotate(135deg);
+        }
+      }
+    }
+  }
+
+  .action-wrapper{
+    display: flex;
+    align-items: center;
+    .audit-info{
+      color: lightgray;
+      cursor: pointer;
+      margin-left: 5px;
+    }
+  }
+
+  .dashboard_table_body {
+    td:first-child {
+      cursor: default;
+    }
+
+    .dropdown-multiselect {
+      button {
+        font-size: 14px;
+        height: 34px;
+        padding: 7px 20px;
+      }
+    }
+  }
+  .user-col{
+    padding-left: 5px;
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.ts
new file mode 100644
index 0000000..d18908c
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.ts
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+import {Component, Inject, OnInit} from '@angular/core';
+import {FilterAuditModel} from '../filter-audit.model';
+import {NotificationDialogComponent} from '../../../shared/modal-dialog/notification-dialog';
+import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
+
+@Component({
+  selector: 'dlab-audit-grid',
+  templateUrl: './audit-grid.component.html',
+  styleUrls: ['./audit-grid.component.scss'],
+
+})
+export class AuditGridComponent implements OnInit {
+  public auditData: Array<object>;
+  public displayedColumns: string[] = ['user', 'project', 'resource', 'action', 'date'];
+  public displayedFilterColumns: string[] = ['user-filter', 'project-filter', 'resource-filter', 'action-filter', 'date-filter'];
+  public collapseFilterRow: boolean = true;
+  public filterConfiguration: FilterAuditModel = new FilterAuditModel([], [], [], [], '', '');
+  public filterAuditData: FilterAuditModel = new FilterAuditModel([], [], [], [], '', '');
+
+  constructor(
+    public dialogRef: MatDialogRef<AuditInfoDialogComponent>,
+    public dialog: MatDialog
+  ) {
+  }
+
+  ngOnInit() {}
+
+  public refreshAudit(auditData) {
+    this.auditData = auditData;
+  }
+
+  toggleFilterRow(): void {
+    this.collapseFilterRow = !this.collapseFilterRow;
+  }
+
+  onUpdate($event): void {
+    this.filterAuditData[$event.type] = $event.model;
+  }
+
+  openActionInfo(element) {
+    // console.log('Open audit info ' + action.action);
+    this.dialog.open(AuditInfoDialogComponent, { data: {data: element.info, action: element.action}, panelClass: 'modal-sm' });
+  }
+}
+
+@Component({
+  selector: 'audit-info-dialog',
+  template: `
+      <div id="dialog-box">
+          <header class="dialog-header">
+              <h4 class="modal-title">{{data.action}}</h4>
+              <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
+          </header>
+          <div mat-dialog-content class="content">
+            <ul info-items-list>
+              <li class="info-item">
+                  <span class="info-item-title">Group:</span>
+                  <span class="info-item-data"> {{data.data.name}}</span>
+              </li>
+              <li class="info-item">
+                <span class="info-item-title">Users:</span>
+                <span class="info-item-data">
+                    <span>{{data.data.objects}}</span>
+                </span>
+              </li>
+            </ul>
+            <div class="text-center m-top-30 m-bott-10">
+<!--               <button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">No</button>-->
+<!--               <button type="button" class="butt butt-success" mat-raised-button-->
+<!--                       (click)="dialogRef.close(true)">Yes-->
+<!--               </button>-->
+             </div>
+          </div>
+      </div>
+  `,
+  styles: [`
+    .content { color: #718ba6; padding: 20px 50px; font-size: 14px; font-weight: 400; margin: 0; }
+    .info { color: #35afd5; }
+    .info .confirm-dialog { color: #607D8B; }
+    header { display: flex; justify-content: space-between; color: #607D8B; }
+    header h4 i { vertical-align: bottom; }
+    header a i { font-size: 20px; }
+    header a:hover i { color: #35afd5; cursor: pointer; }
+    .plur { font-style: normal; }
+    .scrolling-content{overflow-y: auto; max-height: 200px; }
+    .endpoint { width: 70%; text-align: left; color: #577289;}
+    .status { width: 30%;text-align: left;}
+    .label { font-size: 15px; font-weight: 500; font-family: "Open Sans",sans-serif;}
+    .node { font-weight: 300;}
+    .resource-name { width: 280px;text-align: left; padding: 10px 0;line-height: 26px;}
+    .project { width: 30%;text-align: left; padding: 10px 0;line-height: 26px;}
+    .resource-list{max-width: 100%; margin: 0 auto;margin-top: 20px; }
+    .resource-list-header{display: flex; font-weight: 600; font-size: 16px;height: 48px; border-top: 1px solid #edf1f5; border-bottom: 1px solid #edf1f5; padding: 0 20px;}
+    .resource-list-row{display: flex; border-bottom: 1px solid #edf1f5;padding: 0 20px;}
+    .confirm-resource-terminating{text-align: left; padding: 10px 20px;}
+    .confirm-message{color: #ef5c4b;font-size: 13px;min-height: 18px; text-align: center; padding-top: 20px}
+    .checkbox{margin-right: 5px;vertical-align: middle; margin-bottom: 3px;}
+    label{cursor: pointer}
+    .bottom-message{padding-top: 15px;}
+    .table-header{padding-bottom: 10px;}
+    .info-item{display: flex; justify-content: space-between; padding: 10px 0; width: 100%}
+    .info-item-title{width: 50%}
+    .info-item-data{width: 50%; text-align: left;}
+
+
+  `]
+})
+export class AuditInfoDialogComponent {
+  constructor(
+    public dialogRef: MatDialogRef<AuditInfoDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: any
+  ) {
+    console.log(data);
+  }
+
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.html
similarity index 86%
copy from services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
copy to services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.html
index 583371e..601a4dd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.html
@@ -17,8 +17,8 @@
   ~ under the License.
   -->
 <section class="toolbar">
-  <div class="info_color" *ngIf="reportData">
-    <div class="general">
+  <div class="info_color">
+    <div class="general" *ngIf="reportData">
       <div><span>Service base name: </span><strong>{{ reportData.sbn }}</strong></div>
       <div *ngIf="reportData.tag_resource_id"><span>Resource tag ID:
         </span><strong>{{ reportData.tag_resource_id }}</strong></div>
@@ -34,9 +34,9 @@
     <ng-daterangepicker [(ngModel)]="value" [options]="options" (ngModelChange)="onChange($event)"></ng-daterangepicker>
   </div>
   <div class="action-butt">
-    <button mat-raised-button class="butt" (click)="export($event)" [disabled]="!reportData?.report_lines.length">
-      <i class="material-icons">file_download</i>Export
-    </button>
+<!--    <button mat-raised-button class="butt" (click)="export($event)" [disabled]="!reportData?.report_lines.length">-->
+<!--      <i class="material-icons">file_download</i>Export-->
+<!--    </button>-->
     <button mat-raised-button class="butt" (click)="rebuild($event)">
       <i class="material-icons">autorenew</i>Refresh
     </button>
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.scss b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.scss
similarity index 100%
copy from services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.scss
copy to services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.scss
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.ts b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.ts
similarity index 78%
copy from services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.ts
copy to services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.ts
index 5272e37..87daf36 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-toolbar/audit-toolbar.component.ts
@@ -19,19 +19,19 @@
 
 import { Component, OnInit, AfterViewInit, Output, EventEmitter, ViewEncapsulation, ViewChild } from '@angular/core';
 import { NgDateRangePickerOptions } from 'ng-daterangepicker';
-import { DICTIONARY } from '../../../dictionary/global.dictionary';
+import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 import {skip} from 'rxjs/operators';
 import {Subscription} from 'rxjs';
-import {HealthStatusService} from '../../core/services';
-import {GeneralEnvironmentStatus} from '../../administration/management/management.model';
+import {HealthStatusService} from '../../../core/services';
+import {GeneralEnvironmentStatus} from '../../../administration/management/management.model';
 
 @Component({
-  selector: 'dlab-toolbar',
-  templateUrl: './toolbar.component.html',
-  styleUrls: ['./toolbar.component.scss'],
+  selector: 'audit-toolbar',
+  templateUrl: './audit-toolbar.component.html',
+  styleUrls: ['./audit-toolbar.component.scss'],
   encapsulation: ViewEncapsulation.None
 })
-export class ToolbarComponent implements OnInit, AfterViewInit {
+export class AuditToolbarComponent implements OnInit, AfterViewInit {
   readonly DICTIONARY = DICTIONARY;
   value: any;
   reportData: any;
@@ -61,13 +61,13 @@ export class ToolbarComponent implements OnInit, AfterViewInit {
   }
 
   ngOnInit() {
-    if (localStorage.getItem('report_period')) {
-      const availableRange = JSON.parse(localStorage.getItem('report_period'));
-      this.availablePeriodFrom = availableRange.start_date;
-      this.availablePeriodTo = availableRange.end_date;    }
-    this.subscriptions.add(this.healthStatusService.statusData.pipe(skip(1)).subscribe(result => {
-      this.healthStatus = result;
-    }));
+    // if (localStorage.getItem('report_period')) {
+    //   const availableRange = JSON.parse(localStorage.getItem('report_period'));
+    //   this.availablePeriodFrom = availableRange.start_date;
+    //   this.availablePeriodTo = availableRange.end_date;    }
+    // this.subscriptions.add(this.healthStatusService.statusData.pipe(skip(1)).subscribe(result => {
+    //   this.healthStatus = result;
+    // }));
   }
 
   ngAfterViewInit() {
@@ -107,7 +107,7 @@ export class ToolbarComponent implements OnInit, AfterViewInit {
     this.rebuildReport.emit($event);
   }
 
-  export($event): void {
-    this.exportReport.emit($event);
-  }
+  // export($event): void {
+  //   this.exportReport.emit($event);
+  // }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit.component.ts b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit.component.ts
new file mode 100644
index 0000000..fba41b2
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit.component.ts
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+
+import {Component, OnInit, OnDestroy, ViewChild, Inject} from '@angular/core';
+import { ToastrService } from 'ngx-toastr';
+import {HealthStatusService} from '../../core/services';
+import { DICTIONARY} from '../../../dictionary/global.dictionary';
+import {AuditToolbarComponent} from './audit-toolbar/audit-toolbar.component';
+import {AuditGridComponent} from './audit-grid/audit-grid.component';
+import {AuditService} from '../../core/services/audit.service';
+import {Endpoint} from '../../administration/project/project.component';
+import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
+
+
+@Component({
+  selector: 'dlab-reporting',
+  template: `
+  <div class="base-retreat">
+<!--    <dlab-toolbar (rebuildReport)="rebuildBillingReport()"-->
+<!--                  (exportReport)="exportBillingReport()"-->
+<!--                  (setRangeOption)="setRangeOption($event)">-->
+<!--    </dlab-toolbar>-->
+<!--    <mat-divider></mat-divider>-->
+<!--    <dlab-reporting-grid (filterReport)="filterReport($event)" (resetRangePicker)="resetRangePicker()"></dlab-reporting-grid>-->
+    <audit-toolbar>
+    </audit-toolbar>
+    <mat-divider></mat-divider>
+    <dlab-audit-grid></dlab-audit-grid>
+  </div>
+
+  `,
+  styles: [`
+    footer {
+      position: fixed;
+      left: 0;
+      bottom: 0;
+      width: 100%;
+      background: #a1b7d1;
+      color: #ffffff;
+      text-align: right;
+      padding: 5px 15px;
+      font-size: 18px;
+      box-shadow: 0 9px 18px 15px #f5f5f5;
+    }
+  `]
+})
+export class AuditComponent implements OnInit, OnDestroy {
+  readonly DICTIONARY = DICTIONARY;
+
+  @ViewChild(AuditGridComponent, { static: true }) auditGrid: AuditGridComponent;
+  @ViewChild(AuditToolbarComponent, { static: true }) auditToolbar: AuditToolbarComponent;
+
+  public auditData;
+
+  constructor(
+    private healthStatusService: HealthStatusService,
+    private auditService: AuditService,
+    public toastr: ToastrService,
+  ) { }
+
+  ngOnInit() {
+    this.getEnvironmentHealthStatus();
+    this.buildAuditReport();
+  }
+
+  ngOnDestroy() {
+  }
+
+  public buildAuditReport() {
+    this.auditData = this.auditService.getAuditData().subscribe(auditData => {
+      console.log(auditData);
+      // this.auditGrid.refreshAudit(auditData);
+    });
+
+
+  }
+
+  private getEnvironmentHealthStatus() {
+    this.healthStatusService.getEnvironmentHealthStatus()
+      .subscribe((result: any) => {});
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.module.ts b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit.module.ts
similarity index 65%
copy from services/self-service/src/main/resources/webapp/src/app/reporting/reporting.module.ts
copy to services/self-service/src/main/resources/webapp/src/app/reports/audit/audit.module.ts
index 3067e9a..ae4d245 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit.module.ts
@@ -16,18 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { NgDateRangePickerModule } from 'ng-daterangepicker';
-
-import { MaterialModule } from '../shared/material.module';
-import { FormControlsModule } from '../shared/form-controls';
-import { ReportingComponent } from './reporting.component';
-import { KeysPipeModule, LineBreaksPipeModule } from '../core/pipes';
-import { ReportingGridComponent } from './reporting-grid/reporting-grid.component';
-import { ToolbarComponent } from './toolbar/toolbar.component';
+import { MaterialModule } from '../../shared/material.module';
+import { FormControlsModule } from '../../shared/form-controls';
+import { KeysPipeModule, LineBreaksPipeModule } from '../../core/pipes';
+import {AuditComponent} from './audit.component';
+import {AuditGridComponent, AuditInfoDialogComponent} from './audit-grid/audit-grid.component';
+import {AuditToolbarComponent} from './audit-toolbar/audit-toolbar.component';
 
 @NgModule({
   imports: [
@@ -40,10 +38,12 @@ import { ToolbarComponent } from './toolbar/toolbar.component';
     MaterialModule
   ],
   declarations: [
-    ReportingComponent,
-    ReportingGridComponent,
-    ToolbarComponent
+    AuditGridComponent,
+    AuditToolbarComponent,
+    AuditComponent,
+    AuditInfoDialogComponent
   ],
-  exports: [ReportingComponent]
+  entryComponents: [AuditInfoDialogComponent],
+  exports: [AuditComponent]
 })
-export class ReportingModule { }
+export class AuditModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/audit/filter-audit.model.ts b/services/self-service/src/main/resources/webapp/src/app/reports/audit/filter-audit.model.ts
new file mode 100644
index 0000000..c26fdf7
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/filter-audit.model.ts
@@ -0,0 +1,25 @@
+export class FilterAuditModel {
+
+  static getDefault(): FilterAuditModel {
+    return new FilterAuditModel([], [],[],[], '', '');
+  }
+
+  constructor(
+    public users: Array<string>,
+    public resource: Array<string>,
+    public project: Array<string>,
+    public actions: Array<string>,
+    public date_start: string,
+    public date_end: string,
+  ) { }
+
+  defaultConfigurations(): void {
+    this.users = [];
+    this.project = [];
+    this.resource = [];
+    this.actions = [];
+    this.date_start = '';
+    this.date_end = '';
+
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.html
similarity index 99%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.html
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.html
index 537cbb5..577e2d4 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.html
@@ -22,7 +22,7 @@
 
     <ng-container matColumnDef="name">
       <th mat-header-cell *matHeaderCellDef class="env_name label-header">
-        <div class="label"><span class="text"> Environment name</span></div>
+        <div class="label"><span class="text"> Resource name</span></div>
         <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
           <i class="material-icons">
             <span *ngIf="filteredReportData.dlab_id.length > 0; else dlab_id_filtered">filter_list</span>
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.scss
similarity index 100%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.scss
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.scss
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.ts
similarity index 90%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.ts
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.ts
index d4e0076..421ad84 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting-grid/reporting-grid.component.ts
@@ -18,13 +18,13 @@
  */
 
 import {Component, OnInit, Output, EventEmitter, ViewChild, Input} from '@angular/core';
-import { ReportingConfigModel } from '../../../dictionary/global.dictionary';
+import { ReportingConfigModel } from '../../../../dictionary/global.dictionary';
 
 @Component({
   selector: 'dlab-reporting-grid',
   templateUrl: './reporting-grid.component.html',
   styleUrls: ['./reporting-grid.component.scss',
-    '../../resources/resources-grid/resources-grid.component.scss'],
+    '../../../resources/resources-grid/resources-grid.component.scss'],
 
 })
 export class ReportingGridComponent implements OnInit {
@@ -67,15 +67,15 @@ export class ReportingGridComponent implements OnInit {
   let report: Array<object>;
   if (direction === 'down') {
     report = this.reportData.sort((a, b) => {
-      if (a[sortItem] === null) a = '';
-      if (b[sortItem] === null) b = '';
+      if (a[sortItem] === null) a[sortItem] = '';
+      if (b[sortItem] === null) b[sortItem] = '';
      return (a[sortItem] > b[sortItem]) ? 1 : -1;
     });
   }
   if (direction === 'up') {
     report = this.reportData.sort((a, b) => {
-      if (a[sortItem] === null) a = '';
-      if (b[sortItem] === null) b = '';
+      if (a[sortItem] === null) a[sortItem] = '';
+      if (b[sortItem] === null) b[sortItem] = '';
       return (a[sortItem] < b[sortItem]) ? 1 : -1 ;
     });
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting.component.ts
similarity index 96%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting.component.ts
index 03f7ca9..a3e7fa2 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting.component.ts
@@ -20,13 +20,13 @@
 
 import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
 import { ToastrService } from 'ngx-toastr';
-import {ApplicationSecurityService, BillingReportService, HealthStatusService} from '../core/services';
+import {ApplicationSecurityService, BillingReportService, HealthStatusService} from '../../core/services';
 import { ReportingGridComponent } from './reporting-grid/reporting-grid.component';
 import { ToolbarComponent } from './toolbar/toolbar.component';
 
-import { FileUtils } from '../core/util';
-import { DICTIONARY, ReportingConfigModel } from '../../dictionary/global.dictionary';
-import {ProgressBarService} from '../core/services/progress-bar.service';
+import { FileUtils } from '../../core/util';
+import { DICTIONARY, ReportingConfigModel } from '../../../dictionary/global.dictionary';
+import {ProgressBarService} from '../../core/services/progress-bar.service';
 
 @Component({
   selector: 'dlab-reporting',
@@ -156,7 +156,6 @@ export class ReportingComponent implements OnInit, OnDestroy {
         types.push(item['resource_type']);
 
       if (item.shape && types.indexOf(item.shape)) {
-        console.log(item);
        if (item.shape.indexOf('Master') > -1) {
           for (let shape of item.shape.split(/(?=Slave)/g)) {
             shape = shape.replace('Master: ', '');
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.module.ts b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting.module.ts
similarity index 88%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/reporting.module.ts
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting.module.ts
index 3067e9a..2e3ccb6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/reporting.module.ts
@@ -22,10 +22,10 @@ import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { NgDateRangePickerModule } from 'ng-daterangepicker';
 
-import { MaterialModule } from '../shared/material.module';
-import { FormControlsModule } from '../shared/form-controls';
+import { MaterialModule } from '../../shared/material.module';
+import { FormControlsModule } from '../../shared/form-controls';
 import { ReportingComponent } from './reporting.component';
-import { KeysPipeModule, LineBreaksPipeModule } from '../core/pipes';
+import { KeysPipeModule, LineBreaksPipeModule } from '../../core/pipes';
 import { ReportingGridComponent } from './reporting-grid/reporting-grid.component';
 import { ToolbarComponent } from './toolbar/toolbar.component';
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.html
similarity index 89%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.html
index 583371e..7979849 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.html
@@ -17,15 +17,15 @@
   ~ under the License.
   -->
 <section class="toolbar">
-  <div class="info_color" *ngIf="reportData">
-    <div class="general">
+  <div class="info_color" >
+    <div class="general" *ngIf="reportData">
       <div><span>Service base name: </span><strong>{{ reportData.sbn }}</strong></div>
       <div *ngIf="reportData.tag_resource_id"><span>Resource tag ID:
         </span><strong>{{ reportData.tag_resource_id }}</strong></div>
       <div class="report-period info_color" *ngIf="availablePeriodFrom && availablePeriodTo">
         <span>Available reporting period from:</span>
-        <strong>{{ availablePeriodFrom | date }} </strong>
-        to: <strong>{{ availablePeriodTo | date }}</strong>
+        <strong>{{ availablePeriodFrom.join('/') | date }} </strong>
+        to: <strong>{{ availablePeriodTo.join('/') | date }}</strong>
       </div>
     </div>
   </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.scss b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.scss
similarity index 100%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.scss
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.scss
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.ts b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.ts
similarity index 93%
rename from services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.ts
rename to services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.ts
index 5272e37..503ffaa 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reporting/toolbar/toolbar.component.ts
@@ -19,11 +19,11 @@
 
 import { Component, OnInit, AfterViewInit, Output, EventEmitter, ViewEncapsulation, ViewChild } from '@angular/core';
 import { NgDateRangePickerOptions } from 'ng-daterangepicker';
-import { DICTIONARY } from '../../../dictionary/global.dictionary';
+import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 import {skip} from 'rxjs/operators';
 import {Subscription} from 'rxjs';
-import {HealthStatusService} from '../../core/services';
-import {GeneralEnvironmentStatus} from '../../administration/management/management.model';
+import {HealthStatusService} from '../../../core/services';
+import {GeneralEnvironmentStatus} from '../../../administration/management/management.model';
 
 @Component({
   selector: 'dlab-toolbar',
@@ -35,8 +35,8 @@ export class ToolbarComponent implements OnInit, AfterViewInit {
   readonly DICTIONARY = DICTIONARY;
   value: any;
   reportData: any;
-  availablePeriodFrom: string;
-  availablePeriodTo: string;
+  availablePeriodFrom: [];
+  availablePeriodTo: [];
   subscriptions: Subscription = new Subscription();
   healthStatus: GeneralEnvironmentStatus;
 
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java b/services/self-service/src/main/resources/webapp/src/app/reports/reports.module.ts
similarity index 65%
rename from services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
rename to services/self-service/src/main/resources/webapp/src/app/reports/reports.module.ts
index b1201e6..f6b1bf9 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/dto/bucket/BucketDownloadDTO.java
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/reports.module.ts
@@ -17,17 +17,15 @@
  * under the License.
  */
 
-package com.epam.dlab.dto.bucket;
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import {AuditModule} from './audit/audit.module';
+import {ReportingModule} from './reporting/reporting.module';
 
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import lombok.Data;
-import org.hibernate.validator.constraints.NotBlank;
 
-@Data
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class BucketDownloadDTO {
-    @NotBlank(message = "field cannot be empty")
-    private final String bucket;
-    @NotBlank(message = "field cannot be empty")
-    private final String object;
-}
+@NgModule({
+  imports: [CommonModule, AuditModule, ReportingModule],
+  declarations: [],
+  exports: [AuditModule, ReportingModule]
+})
+export class ReportsModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
index 1de656e..05d2715 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
@@ -17,115 +17,250 @@
   ~ under the License.
   -->
 
-<div class="bucket-browser" id="dialog-box">
+<div class="bucket-browser" id="dialog-box"  (click)="closeActions()">
   <header class="dialog-header">
     <h4 class="modal-title">Bucket browser</h4>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </header>
 
-  <div class="dialog-content tabs" [hidden]="!path" >
+<!--  <div class="dialog-content tabs">-->
+  <div class="dialog-content tabs">
     <div class="submit m-bott-10 m-top-10">
-      <span [matTooltip]="'You have not permission to upload data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.upload}}">
-        <button mat-raised-button type="button" class="butt action" [disabled]="!this.bucketStatus.upload">
-          <input [ngClass]="{'not-allowed': !this.bucketStatus.upload}" type="file" (change)="handleFileInput($event)">
-          Add file
+      <div class="left-side-butts">
+      <span [matTooltip]="isQueueFull ? 'Previous upload is still in progress, please wait.' : 'You have not permission to upload data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.upload && !isQueueFull}}">
+        <button mat-raised-button type="button" class="butt first-btn" [disabled]="!this.bucketStatus.upload || allDisable || isSelectionOpened || !path || isQueueFull" (click)="handleFileInput($event)">
+          <input [ngClass]="{'not-allowed': !this.bucketStatus.upload}" type="file" (change)="handleFileInput($event)" title="" multiple>
+          Upload files
         </button>
       </span>
       <span [matTooltip]="'You have not permission to create folder'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.upload}}">
         <button
           mat-raised-button
           type="button"
-          class="butt action"
-          (click)="folderTreeComponent.addNewItem(selectedFolder, '', false)"
-          [disabled]="!this.bucketStatus.upload"
+          class="butt"
+          (click)="createFolder(selectedFolder)"
+          [disabled]="!this.bucketStatus.upload || allDisable || !path || isSelectionOpened"
         >
           Create folder
         </button>
       </span>
+      <span [matTooltip]="'You have not permission to delete data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.delete}}">
+        <button
+          type="button"
+          class="butt"
+          mat-raised-button
+          (click)="fileAction('delete')"
+          [disabled]="(!selected?.length && !selectedFolderForAction?.length) || !this.bucketStatus.delete || allDisable || !path || isSelectionOpened"
+        >
+        Delete
+      </button>
+      </span>
+      <div class="action-wrapper" >
+        <span class="action-button-wrapper">
+          <button
+            type="button" class="butt actions-btn"
+            mat-raised-button
+            [disabled]=" selectedItems?.length !== 1 || allDisable || !path || isSelectionOpened"
+            (click)="toogleActions();$event.stopPropagation()"
+          >
+            Actions <i class="material-icons" >{{ !isActionsOpen ?  'expand_more' : 'expand_less' }}</i>
+          </button>
+          </span>
+        <div class="action-menu" *ngIf="isActionsOpen">
+          <span [matTooltip]="'You have not permission to download data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.download}}">
+          <button
+            type="button" class="butt action-menu-item"
+            [ngClass]="{'disabled': !selected?.length || this.selectedItems?.length > 1 || !this.bucketStatus.download || allDisable || isSelectionOpened}"
+            mat-raised-button
+            [disabled]=" !selected?.length || this.selectedItems?.length > 1 || !this.bucketStatus.download || allDisable || isSelectionOpened"
+            (click)="fileAction('download');$event.stopPropagation()"
+          >
+            Download
+          </button>
+            </span>
+          <button
+            type="button" class="butt action-menu-item"
+            mat-raised-button
+            (click)="copyPath();$event.stopPropagation()"
+          >
+            Copy path
+          </button>
+        </div>
+      </div>
+      </div>
+      <button
+        mat-raised-button
+        type="button"
+        class="butt refresh"
+        (click)="refreshBucket()"
+        [disabled]="allDisable"
+      >
+        <i class="material-icons">autorenew</i>Refresh
+      </button>
     </div>
-    <p class="path"><span>Bucket path:</span><span class="url"> {{path}}</span></p>
-    <div class="backet-wrapper" id="scrolling">
-      <div class="navigation">
-        <dlab-folder-tree (showFolderContent)=onFolderClick($event)> </dlab-folder-tree>
+    <p class="path"><span>Bucket path:</span>
+      <span class="url ellipsis"  [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
+       <span *ngFor="let folder of this.objectPath">
+         <span class="url-icon" *ngIf="this.objectPath.indexOf(folder) !== 0"> <i class="material-icons">
+               chevron_right
+            </i> </span>
+         <span class="url-folder" (click)="folderTreeComponent.showItem(folder);" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">{{folder.item}}</span>
+       </span>
+      </span>
+    </p>
+    <div class="bucket-wrapper" [ngClass]="{'added-upload': addedFiles.length}" id="scrolling">
+      <div class="backet-selection" [ngClass]="{'opened': isSelectionOpened}">
+        <div class="button-wrapper" [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
+          <i (click)="toggleBucketSelection()" class="material-icons close" *ngIf="!isSelectionOpened" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">chevron_right</i>
+        </div>
+        <dlab-bucket-tree
+          [hidden] = "!isSelectionOpened"
+          (emitActiveBucket)=openBucket($event)
+          [buckets]='buckets'
+          [openedBucket] = this.bucketName
+        >
+        </dlab-bucket-tree>
       </div>
-      <div class="directory">
-        <ul class="folder-tree" *ngIf="!addedFiles.length">
-          <li *ngFor="let file of folderItems" class="folder-item" >
+      <div class="navigation" [ngClass]="{'selection-opened': isSelectionOpened}" [hidden]="!path">
+        <dlab-folder-tree
+          (showFolderContent)=onFolderClick($event)
+          (disableAll) = dissableAll($event)
+          [folders] = folders
+          [endpoint] = endpoint
+        > </dlab-folder-tree>
+      </div>
+      <div class="directory" [ngClass]="{'selection-opened': isSelectionOpened}" [hidden]="!path">
+        <div class="folder-item t_header">
+          <div class="folder-item-wrapper header-wrapper folder-tree header-item">
+            <div class="name" [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
+              <span class="th_name" (click)="isFilterVisible = true" *ngIf="!isFilterVisible" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">Name</span>
+              <div class="filter-files"  *ngIf="isFilterVisible">
+                <input _ngcontent-yns-c13=""
+                       [(ngModel)]="searchValue"
+                       (keyup)=filterObjects()
+                       class="form-control filter-field filter-name"
+                       placeholder="Filter by name" type="text"
+                >
+                <span><i (click)="closeFilterInput()" class="material-icons close">close</i></span>
+              </div>
 
-            <div class="folder-item-wrapper" *ngIf="file.children" (click)="showItem(file)">
-              <div class="name name-folder"><i class="material-icons folder-icon" >folder</i><span>{{file.item}}</span></div>
-              <div class="size"></div>
-              <div class="progress-wrapper">
-                <div class="progres" *ngIf="file.isSelected"><div class="bar"></div></div>
+            </div>
+            <div class="size"><span class="th_size">Size</span></div>
+            <div class="date"><span class="th_date">Last modified</span></div>
+          </div>
+        </div>
+        <ul class="folder-tree" [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
+          <li *ngFor="let file of folderItems" class="folder-item" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">
+            <div class="folder-item-wrapper" *ngIf="file.children && file.item" (click)="showItem(file)">
+              <div class="name name-folder">
+<!--                <span *ngIf="this.bucketStatus.delete" class="empty-checkbox" [ngClass]="{'checked': file.isFolderSelected}" (click)="toggleSelectedFile(file, 'folder');$event.stopPropagation()" >-->
+                <span class="empty-checkbox" [ngClass]="{'checked': file.isFolderSelected}" (click)="toggleSelectedFile(file, 'folder');$event.stopPropagation()" >
+                    <span class="checked-checkbox" *ngIf="file.isFolderSelected"></span>
+                </span>
+                  <i class="material-icons folder-icon folder-name">folder</i>
+                  <span class="item-name name-wrap"
+                        matTooltip="{{file.item}}"
+                        matTooltipPosition="above"
+                        matTooltipShowDelay="1000"
+                        [matTooltipClass]="'bucket-item-tooltip'"
+                  >
+                    {{file.item}}
+                </span>
               </div>
+              <div class="size size-folder">-</div>
+              <div class="date" *ngIf="!file.isDownloading">-</div>
             </div>
-
-            <div class="folder-item-wrapper"  (click)="toggleSelectedFile(file)" *ngIf="!file.children">
+            <div class="folder-item-wrapper"  (click)="toggleSelectedFile(file, 'file')" *ngIf="!file.children && file.item !== 'ا'">
               <div class="name name-file">
-                 <span *ngIf="this.bucketStatus.download || this.bucketStatus.download" class="empty-checkbox" [ngClass]="{'checked': file.isSelected}" (click)="toggleSelectedFile(file);$event.stopPropagation()" >
+
+                 <span class="empty-checkbox" [ngClass]="{'checked': file.isSelected, 'not-allowed': file.isDownloading}" (click)="toggleSelectedFile(file, 'file');$event.stopPropagation()" >
                     <span class="checked-checkbox" *ngIf="file.isSelected"></span>
                   </span>
-                <span class="item-name" [ngClass]="{'removed-checkbox': !this.bucketStatus.download && !this.bucketStatus.download}">{{file.item}}</span>
+                <i class="material-icons folder-icon" >description</i>
+
+                <span
+                  class="item-name name-wrap"
+                  matTooltip="{{file.item}}"
+                  matTooltipPosition="above"
+                  matTooltipShowDelay="1000"
+                  [matTooltipClass]="'bucket-item-tooltip'"
+                >
+                  {{file.item}}
+                </span>
               </div>
-              <div class="size">{{file.size.size}}</div>
-              <div class="size" *ngIf="!file.isDownloading">{{file.size.lastModifiedDate }}</div>
+              <div class="size">{{file.object?.size | convertFileSize}}</div>
+              <div class="date" *ngIf="!file.isDownloading">{{convertDate(file.object?.lastModifiedDate) | date: 'dd/MM/yyyy hh:mm:ss aa'  }}</div>
+<!--              <div class="date" *ngIf="!file.isDownloading">{{file.object?.lastModifiedDate }}</div>-->
               <div class="progress-wrapper" *ngIf="file.isDownloading">
-                <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+                <div class="progres">
+                  <span class="progress-bar-text">{{file.progress || 0}}% Downloading...</span>
+                  <div class="bar" [ngStyle]="{width: file.progress + '%'}">
+                  </div>
+                </div>
               </div>
             </div>
-
-          </li>
-        </ul>
-        <ul class="folder-tree">
-          <li *ngFor="let file of addedFiles" class="folder-item">
-            <div class="folder-item-wrapper">
-              <div class="name">{{file.item}}</div>
-              <div class="size">{{file.size}}MB</div>
-              <div class="progress-wrapper">
-                <mat-progress-bar mode="indeterminate" *ngIf="isUploading"></mat-progress-bar>
-              </div>
-              <div (click)="deleteAddedFile(file)"><i class="material-icons close">close</i></div>
-            </div>
           </li>
         </ul>
       </div>
+      <div class="loading-block" *ngIf="!path">
+        <div class="uploading">
+          <p>Please wait until DLab loads bucket: <span class="strong">{{bucketName}}</span>...</p>
+          <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+        </div>
+      </div>
     </div>
-    <div class="text-center m-top-30 m-bott-30">
-      <button type="button" class="butt" mat-raised-button (click)="this.dialogRef.close()">Close</button>
-      <span [matTooltip]="'You have not permission to download data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.download}}">
-       <button
-         *ngIf="!this.addedFiles.length"
-         type="button" class="butt butt-success"
-         mat-raised-button
-         (click)="fileAction('download')"
-         [disabled]="!selected?.length || !this.bucketStatus.download"
-       >
-         Download
-       </button>
-      </span>
-      <span [matTooltip]="'You have not permission to delete data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.delete}}">
-        <button
-          *ngIf="!this.addedFiles.length"
-          type="button"
-          class="butt butt-success"
-          mat-raised-button
-          (click)="fileAction('delete')"
-          [disabled]="!selected?.length || !this.bucketStatus.delete"
+    <div class="upload-window" *ngIf="addedFiles.length">
+      <header class="upload-header">
+        <h4 class="modal-title">Upload files</h4>
+        <span class="close" matTooltip="Upload is still in progress, please wait." matTooltipPosition="above" [ngClass]="{'not-allow': isFileUploading}" [matTooltipDisabled]="!isFileUploading">
+           <button type="button" class="close" (click)="closeUploadWindow()" [disabled]="isFileUploading" [ngClass]="{'not-allow': isFileUploading}" >&times;</button>
+        </span>
 
-        >
-        Delete
-      </button>
-      </span>
-
-      <button *ngIf="this.addedFiles.length !== 0" type="button" class="butt butt-success" mat-raised-button (click)="uploadNewFile()">Upload</button>
-    </div>
-  </div>
+      </header>
+      <ul class="upload-files">
+        <li class="file upload-table-header" *ngIf="addedFiles.length">
+          <div class="name ellipsis" >File</div>
+          <div class="second-block">
+            <div class="upload-path ellipsis">Path</div>
+            <div class="size">Size</div>
+            <div class="state"></div>
+            <div class="remove"></div>
+          </div>
 
-  <div class="loading-block" *ngIf="!path">
-    <div class="uploading">
-      <p>Please wait until DLab loads bucket: <span class="strong">{{data.bucket}}</span>...</p>
-      <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+        </li>
+        <li *ngFor="let file of addedFiles" class="file">
+          <div class="name">
+            <span
+              class="ellipsis"
+              matTooltip="{{file.name}}"
+              [matTooltipClass]="'bucket-item-tooltip'"
+              matTooltipPosition="above"
+            >
+              {{file.name}}
+            </span>
+          </div>
+          <div class="second-block">
+            <div class="upload-path">
+              <span class="ellipsis" matTooltip="{{file.path}}" matTooltipPosition="above" [matTooltipClass]="'bucket-item-tooltip'">{{file.path}}</span>
+            </div>
+            <div class="size">{{file.size | convertFileSize}} </div>
+            <div class="state">
+              <!--           <button mat-raised-button (click)="uploadNewFile(file)" *ngIf="!file.isUploading && !file.uploaded && !file.errorUploading">Upload</button>-->
+              <!--           <mat-progress-bar mode="indeterminate" *ngIf="file.isUploading"></mat-progress-bar>-->
+              <span *ngIf="file.status === 'uploaded'" class="running">Uploaded</span>
+              <div class="progres" *ngIf="file.status === 'uploading'">
+                <span class="progress-bar-text">{{file.progress || 0}}% Uploading...</span>
+                <div class="bar" [ngStyle]="{width: file.progress + '%'}">
+                </div>
+              </div>
+              <span *ngIf="file.status === 'failed'" class="error">Failed</span>
+              <span *ngIf="file.status === 'waiting'" class="stopped">Waiting for uploading...</span>
+            </div>
+            <div class="remove"><i (click)="deleteAddedFile(file)" class="material-icons close">close</i></div>
+          </div>
+        </li>
+        <li id="upload-list"></li>
+      </ul>
     </div>
   </div>
-
 </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
index eb0c55e..fbdf05b 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
@@ -16,47 +16,149 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+.dialog-header {
+  padding-left: 33px !important;
+}
 
 .bucket-browser {
+  font-size: 14px;
+  font-weight: 400;
+
+
   .loading-block{
     width: 80%;
-    margin: 20% auto 0 auto;
+    margin: 0 auto;
     display: flex;
-    align-content: center;
+    align-items: center;
     justify-content: center;
     height: 100%;
     .uploading{
       width: 100%;
       text-align: center;
+      padding-bottom: 50px;
       p{
         margin-bottom: 20px;
       }
     }
   }
 
+  .action-wrapper{
+    position: relative;
+
+    .action-button-wrapper{
+      position: relative;
+      width: 160px;
+    }
+
+    .mat-raised-button.butt{
+      margin-bottom: 0;
+
+      &.actions-btn{
+        padding-right: 38px;
+
+        .material-icons{
+          transition: ease-in-out 1s;
+          font-size: 25px;
+          position: absolute;
+          top: 7px;
+          right: 30px;
+        }
+      }
+    }
+
+    .action-menu{
+      position: absolute;
+      margin: 0 10px;
+      text-align: center;
+
+      &-item.mat-raised-button.butt{
+        z-index: 1;
+        margin: 0;
+        box-shadow: 0 2px 1px -1px rgba(0,0,0,.2), 0 0px 0px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
+        width: 160px;
+        padding: 0 20px;
+        border-radius: 0;
+        font-style: normal;
+        font-weight: 600;
+        font-size: 15px;
+        font-family: 'Open Sans', sans-serif;
+        color: #577289;
+        position: relative;
+        overflow: hidden;
+        line-height: 36px;
+      }
+    }
+  }
+
   .path{
-    margin: 0 4px 10px 4px;
-    padding: 4px 4px 4px 20px;
+    margin: 0 4px 30px 0;
+    padding: 4px 4px 4px 0;
     color: rgba(0,0,0,.87);
+    overflow: hidden;
+    white-space: nowrap;
+    display: flex;
+
     .url{
       font-weight: 600;
+      overflow: hidden;
+      display: block;
+      direction: rtl;
+
+     &-folder{
+       padding-left: 5px;
+       padding-right: 5px;
+       color: #00bcd4;
+       cursor: pointer;
+     }
+
+      &-icon i{
+        transform: translateY(2px);
+        font-size: 15px;
+      }
     }
   }
   bottom: 0;
+
   .dialog-content{
     padding: 0 35px;
   }
+
   .content-box {
     height: 500px;
-
   }
 
   .submit{
     display: flex;
+    justify-content: space-between;
+    .left-side-butts{
+      display: flex;
+    }
+
     .butt{
       position: relative;
       overflow: hidden;
       margin: 10px;
+
+      &.first-btn{
+        margin-left: 0;
+      }
+
+      &.refresh{
+        margin-right: 0;
+      }
+
+      &.action-menu-item{
+        &:hover{
+          color:  #00bcd4;
+          background-color: #fafafa;
+        }
+        &.disabled{
+          &:hover{
+            color: #577289;
+          }
+        }
+      }
+
       input[type="file"] {
         position: absolute;
         left: 0;
@@ -67,98 +169,204 @@
       }
     }
   }
+}
 
-  .text-center{
-    position: absolute;
-    bottom: 3vh;
-    left: 0;
-    right: 0;
+.navigation-line{
+  display: flex;
+  margin-bottom: 25px;
+
+  .endpoint-select{
+    flex: 1;
+    position: relative;
+    opacity: 0;
   }
+
+
 }
 
-.backet-wrapper{
-  height: 50vh;
+
+.bucket-wrapper{
+  height: 57vh;
   border: 2px solid rgba(0,0,0,.12);
   border-radius: 5px;
   display: flex;
+  width: 100%;
+
+  &.added-upload{
+    height: 40vh;
+  }
+   .backet-selection{
+     position: relative;
+     width: 2%;
+     border-right: 2px solid rgba(0,0,0,.12);
+     padding-top: 6px;
+     transition: .2s;
+     &.opened{
+       width: 33.3%;
+
+       .button-wrapper {
+         text-align: right;
+         left: auto;
+
+
+         i{
+           padding-right: 3px;
+         }
+       }
+     }
+
+     .button-wrapper {
+       position: absolute;
+       left: 0;
+       right: 0;
+       top: 9px;
+       text-align: center;
 
 
+       i{
+         cursor: pointer;
+         font-size: 18px;
+
+         &:hover{
+           color:  #00bcd4;
+       }
+       }
+     }
 
+     .buckets-list{
+       color: rgba(0, 0, 0, 0.87);
+       padding-left: 20px;
+       line-height: 24px;
+
+       &-item{
+         &:hover{
+           color:  #00bcd4;
+           cursor: pointer;
+         }
+       }
+     }
+   }
+
+     .buckets-list{
+       color: rgba(0, 0, 0, 0.87);
+       padding-left: 20px;
+       line-height: 24px;
+       &-item{
+         &:hover{
+           color:  #00bcd4;
+           cursor: pointer;
+         }
+       }
+     }
+   }
   .navigation{
-    flex: 1;
-    border-right: 2px solid rgba(0,0,0,.12);
-    max-height: 500px;
+    transition: .2s;
+    width: 31.3%;
+    height: 100%;
     overflow: auto;
-    .folder-tree{
+    padding-top: 6px;
 
+    &.selection-opened{
+      width: 66.7%;
+    }
+
+    .folder-tree{
       .folder{
         line-height: 30px;
       }
     }
 
+    .mat-tree-node{
+      min-height: auto;
+    }
   }
 
   .directory{
-    max-height: 500px;
-    overflow: auto;
-    flex: 2;
+    width: 66.7%;
+    max-height: 100%;
+    font-size: 14px;
+    font-weight: 400;
+    position: relative;
+    border-left: 2px solid rgba(0,0,0,.12);
+
+    &.selection-opened{
+      display: none;
+    }
+
     .folder-tree{
+      overflow-x: auto;
+      overflow-y: overlay;
+      max-height: 100%;
+
       .name{
-        flex:2;
+        width: 60%;
         display: flex;
         align-items: center;
+        overflow: hidden;
+
+        .name-wrap {
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          padding-right: 10px;
+        }
+
         &-folder{
-          span{
-            padding-left: 10px;
+          .folder-name{
+            padding-left: 8px;
           }
         }
+
         &-file{
-          padding-left: 4px;
+          i{
+            transform: translateX(6px);
+          }
           span.item-name{
-            padding-left: 14px;
+            padding-left: 4px;
             &.removed-checkbox{
-              padding-left: 0;
+              padding-left: 4px;
             }
           }
         }
       }
+
       .size{
-        flex:1;
+        width: 15%;
+
+        &-folder{
+          padding-left: 4px;
+        }
+      }
+
+      .date{
+        width: 25%;
+
+        .th_date{
+          font-size: 13px;
+        }
       }
+
       .progress-wrapper{
         flex:1;
-        .progres{
-          border: 1px solid rgba(0,0,0,.12);
-          height: 15px;
-          position: relative;
-          .bar{
-            position: absolute;
-            top: 0;
-            bottom: 0;
-            left: 0;
-            width: 0;
-            background-color:  #00bcd4;
-            &.full{
-              width: 100%;
-              transition: 5s ease-in-out;
-            }
-          }
-        }
       }
+
       .material-icons.close{
         font-size: 15px;
         margin: 0 10px;
         cursor: pointer;
       }
+
       .action {
         display: flex;
         justify-content: space-around;
         width: 100%;
+
         .add-file {
           overflow: hidden;
           min-width: 100px ;
           height: 30px;
           position: relative;
+
           input{
             position: absolute;
             left: 0;
@@ -169,17 +377,74 @@
           }
         }
       }
-
     }
   }
 
   .folder-tree {
-    padding: 20px 30px;
+    padding: 0px 15px;
+    padding-top: 6px;
   }
 
   .folder-item{
      display: flex;
      align-items: center;
+
+    &.t_header{
+      top: -41px;
+
+      position: absolute;
+      left: 0;
+      right: 0;
+
+      .folder-item-wrapper{
+        height: 52px;
+      }
+
+      .th_name{
+        padding-left: 29px;
+        font-size: 14px;
+        cursor: pointer;
+
+        &:hover{
+          color: #00bcd4;
+        }
+      }
+
+      .filter-files{
+        width: 100%;
+        padding: 3px 0;
+        padding-left: 3px;
+        display: flex;
+        align-items: center;
+
+        .filter-name{
+          font-size: 14px;
+          height: 20px;
+          width: 90%;
+        }
+
+        span{
+          transform: translateY(2px);
+
+          &:hover{
+            i{
+              color: #00bcd4;
+            }
+          }
+        }
+      }
+
+      .th_size{
+        font-size: 14px;
+        padding-left: 3px;
+        cursor: auto;
+      }
+
+      .th_date{
+        font-size: 14px !important;
+        cursor: auto;
+      }
+    }
      .folder-item-wrapper{
        width: 100%;
        display: flex;
@@ -187,20 +452,34 @@
        align-items: center;
        cursor: pointer;
        color: rgba(0,0,0,.87);
+
+       &.header-item{
+         cursor: auto;
+
+         .name{
+           margin-left: -3px;
+         }
+
+       }
+
        i{
          color: rgb(232, 232, 232);
        }
-       &:hover{
+
+       &:hover:not(.header-wrapper){
          color: #00bcd4;
          transition: .3s ease-in-out;
          i{
            color: #00bcd4;
            transition: .3s ease-in-out;
          }
+
          .empty-checkbox{
            border-color: #00bcd4
          }
+
          .progress-wrapper{
+
            .progres{
              border-color: #00bcd4 !important;
            }
@@ -208,19 +487,25 @@
        }
      }
    }
+
+input[type='file'] {
+  opacity:1
 }
 
 .empty-checkbox {
+  min-width: 16px;
   width: 16px;
   height: 16px;
   border-radius: 2px;
   border: 2px solid lightgrey;
   margin-top: 2px;
   position: relative;
+
   &.checked {
     border-color: #35afd5;
     background-color: #35afd5;
   }
+
   .checked-checkbox {
     top: 0;
     left: 4px;
@@ -241,18 +526,187 @@
   .check-box {
     background-color: red;
   }
+}
+
+.upload-window{
+  margin-top: 2vh;
+  border: 2px solid rgba(0,0,0,.12);
+  border-radius: 5px;
+  background-color: white;
+  height: 15vh;
+  color: black;
+
+  .upload-header{
+    padding-left: 8px;
+    background: #f6fafe;
+    height: 30px;
+    line-height: 30px;
+    position: relative;
+    .modal-title {
+      width: 90%;
+      margin: 0;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      font-weight: 500;
+      color: #455c74;
+      font-size: 13px;
+      background: #f6fafe;
+  }
+
+    .close{
+      position: absolute;
+      top: 0;
+      right: 0;
+      height: 30px;
+      width: 30px;
+      font-size: 20px;
+      font-weight: 300;
+      border: 0;
+      background: none;
+      color: #577289;
+      outline: none;
+      cursor: pointer;
+      transition: all 0.45s ease-in-out;
+
+      &:hover{
+        color: #36afd5;
+      }
+    }
+
+  }
+  .upload-files{
+    padding: 5px 0;
+    height: calc(100% - 30px);
+    overflow: auto;
+    overflow-y: overlay;
+    width: 100%;
+    .file{
+      padding: 2px;
+      display: flex;
+      font-size: 12px;
+      position: relative;
+
+      &.upload-table-header{
+        font-size: 11px;
+      }
+
+      .name{
+        width: 33.3%;
+        padding-left: 5px;
+        position: relative;
+        display: flex;
+        span{
+          position: absolute;
+          width: calc(100% + 30px);
+        }
+      }
+
+      .second-block{
+        width: 66.7%;
+        display: flex;
+        padding: 0 14px 0 17px;
+        .upload-path{
+          width: 60%;
+          padding-left: 24px;
+          padding-right: 1%;
+          display: flex;
+        }
+
+        .size{
+          width: 15%;
+        }
+        .state{
+          width: 22%;
+          display: flex;
+          align-items: center;
+          .mat-raised-button{
+            width: 60px;
+            padding: 5px;
+            border-radius: 0;
+            font-style: normal;
+            font-weight: 600;
+            font-size: 11px;
+            font-family: "Open Sans", sans-serif;
+            color: #577289;
+            line-height: 8px;
+          }
+        }
+        .remove{
+          display: flex;
+          align-items: center;
+          width: 5%;
+          position: absolute;
+          right: 20px;
+          .close{
+            color: #577289;
+            font-size: 12px;
+            cursor: pointer;
+            position: absolute;
+            top: 3px;
+            right: 0;
+            height: 18px;
+            width: 14px;
+            transition: all 0.45s ease-in-out;
+
+            &:hover{
+              color: #f1696e;
+            }
+          }
+        }
+      }
+
+
+    }
+  }
+}
 
+.events-none{
+  pointer-events: none;
+}
+
+@media only screen and (max-height: 920px) {
+  .bucket-wrapper {
+    height: 55vh;
+    &.added-upload{
+      height: 38vh;
+    }
+  }
 }
 
 @media only screen and (max-height: 840px) {
-  .backet-wrapper {
-    height: 45vh;
+  .bucket-wrapper {
+    height: 53vh;
+    &.added-upload{
+      height: 36vh;
+    }
   }
 }
 
-@media only screen and (max-height: 690px) {
-  .backet-wrapper {
-    height: 40vh;
+@media only screen and (max-height: 760px) {
+  .bucket-wrapper {
+    height: 51vh;
+    &.added-upload{
+      height: 34vh;
+    }
+  }
+}
+
+@media only screen and (max-height: 700px) {
+  .bucket-wrapper {
+    height: 49vh;
+    &.added-upload{
+      height: 32vh;
+    }
+  }
+}
+
+@media only screen and (max-height: 650px) {
+  .bucket-wrapper {
+    height: 47vh;
+    &.added-upload{
+      height: 30vh;
+    }
   }
 }
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
index 706a8f1..696835e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
@@ -21,12 +21,16 @@ import { Component, OnInit, ViewChild, Inject } from '@angular/core';
 import { FormGroup, FormBuilder } from '@angular/forms';
 import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { ToastrService } from 'ngx-toastr';
-import { ManageUngitService } from '../../core/services';
+import {ApplicationSecurityService, ManageUngitService, StorageService} from '../../core/services';
 
 import {FolderTreeComponent} from './folder-tree/folder-tree.component';
 import {BucketBrowserService, TodoItemNode} from '../../core/services/bucket-browser.service';
 import {FileUtils} from '../../core/util';
 import {BucketDataService} from './bucket-data.service';
+import {BucketConfirmationDialogComponent} from './bucket-confirmation-dialog/bucket-confirmation-dialog.component';
+import {logger} from 'codelyzer/util/logger';
+import {HttpEventType, HttpResponse} from '@angular/common/http';
+import {CopyPathUtils} from '../../core/util/copyPathUtils';
 
 @Component({
   selector: 'dlab-bucket-browser',
@@ -36,17 +40,33 @@ import {BucketDataService} from './bucket-data.service';
 export class BucketBrowserComponent implements OnInit {
   public addedFiles = [];
   public folderItems = [];
+  public originFolderItems = [];
+  public objectPath;
   public path = '';
   public pathInsideBucket = '';
   public bucketName = '';
   public endpoint = '';
-
-  @ViewChild(FolderTreeComponent, {static: true}) folderTreeComponent;
   public selectedFolder: any;
-  private isUploading: boolean;
-  private selected: any[];
-  private uploadForm: FormGroup;
+  public selectedFolderForAction: any;
+  public selected: any[];
   public bucketStatus;
+  public allDisable: boolean;
+  public isActionsOpen: boolean;
+  public folders: any[];
+  public selectedItems;
+  public searchValue: string;
+  public isQueueFull: boolean;
+  public refreshTokenLimit = 900000;
+  private isTokenRefreshing = false;
+  @ViewChild(FolderTreeComponent, {static: true}) folderTreeComponent;
+  public isSelectionOpened: any;
+  isFilterVisible: boolean;
+  public buckets;
+  private isFileUploading: boolean;
+  private isFileUploaded: boolean;
+
+
+
 
   constructor(
     @Inject(MAT_DIALOG_DATA) public data: any,
@@ -56,19 +76,34 @@ export class BucketBrowserComponent implements OnInit {
     private manageUngitService: ManageUngitService,
     private _fb: FormBuilder,
     private bucketBrowserService: BucketBrowserService,
-    private bucketDataService: BucketDataService,
-    private formBuilder: FormBuilder
+    public bucketDataService: BucketDataService,
+    private auth: ApplicationSecurityService,
+    private storage: StorageService,
   ) {
 
   }
 
   ngOnInit() {
-    this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
+    this.bucketName = this.data.bucket;
+    this.bucketDataService.refreshBucketdata(this.bucketName, this.data.endpoint);
     this.endpoint = this.data.endpoint;
     this.bucketStatus = this.data.bucketStatus;
-    this.uploadForm = this.formBuilder.group({
-      file: ['']
+    this.buckets = this.data.buckets;
+  }
+
+  public getTokenValidTime() {
+    const token = JSON.parse(atob(this.storage.getToken().split('.')[1]));
+    return token.exp * 1000 - new Date().getTime();
+  }
+
+  private refreshToken() {
+    this.isTokenRefreshing = true;
+    this.auth.refreshToken().subscribe(tokens => {
+      this.storage.storeTokens(tokens);
+      this.isTokenRefreshing = false;
+      this.sendFile();
     });
+
   }
 
   public showItem(item) {
@@ -76,97 +111,333 @@ export class BucketBrowserComponent implements OnInit {
     this.folderTreeComponent.showItem(flatItem);
   }
 
-  public handleFileInput(event) {
-    if (event.target.files.length > 0) {
-      const file = event.target.files[0];
-      this.uploadForm.get('file').setValue(file);
-      const newAddedFiles = Object['values'](event.target.files).map(v => (
-        {item: v['name'], 'size': (v['size'] / 1048576).toFixed(2)} as unknown as TodoItemNode  ));
-      this.addedFiles = [...newAddedFiles];
-    }
+  public closeUploadWindow() {
+    this.addedFiles = [];
   }
 
 
-  public toggleSelectedFile(file) {
-   // remove if when will be possible download several files
-    if (!file.isSelected) {
-      this.folderItems.forEach(item => item.isSelected = false);
-    }
-
-    file.isSelected = !file.isSelected;
-    this.selected = this.folderItems.filter(item => item.isSelected);
+  public toggleSelectedFile(file, type) {
+  type === 'file' ?  file.isSelected = !file.isSelected : file.isFolderSelected = !file.isFolderSelected;
+  this.selected = this.folderItems.filter(item => item.isSelected);
+  this.selectedFolderForAction = this.folderItems.filter(item => item.isFolderSelected);
+  this.selectedItems = [...this.selected, ...this.selectedFolderForAction];
+  this.isActionsOpen = false;
   }
 
   filesPicked(files) {
-
     Array.prototype.forEach.call(files, file => {
       this.addedFiles.push(file.webkitRelativePath);
     });
   }
 
+  public dissableAll(event) {
+    this.allDisable = event;
+  }
+
+  public handleFileInput(event) {
+    const fullFilesList = Object['values'](event.target.files);
+    if (fullFilesList.length > 0) {
+      const files = fullFilesList.filter(v => v.size < 4294967296);
+      const toBigFile = fullFilesList.length !== files.length;
+      const toMany = files.length > 50;
+      if (files.length > 50) {
+        files.length = 50;
+      }
+      if (toBigFile || toMany) {
+        this.dialog.open(BucketConfirmationDialogComponent, {data: {
+          items: {toBig: toBigFile, toMany: toMany}, type: 'uploading-error'
+          } , width: '550px'})
+          .afterClosed().subscribe((res) => {
+         if (res) {
+           if (this.refreshTokenLimit > this.getTokenValidTime()) {
+             this.isTokenRefreshing = true;
+             this.auth.refreshToken().subscribe(v => {
+               this.uploadingQueue(files);
+               this.isTokenRefreshing = false;
+             });
+           } else {
+             this.uploadingQueue(files);
+           }
+         }
+        });
+      } else {
+        if (this.refreshTokenLimit > this.getTokenValidTime()) {
+          this.isTokenRefreshing = true;
+          this.auth.refreshToken().subscribe(v => {
+            this.uploadingQueue(files);
+            this.isTokenRefreshing = false;
+          });
+        } else {
+          this.uploadingQueue(files);
+        }
+      }
+    }
+    event.target.value = '';
+  }
+
+  private async uploadingQueue(files) {
+    if (files.length) {
+      let askForAll = true;
+      let skipAll = false;
+
+      const folderFiles = this.folderItems.filter(v => !v.children).map(v => v.item);
+      for (const file of files) {
+        const existFile = folderFiles.filter(v => v === file['name'])[0];
+        const uploadItem = {
+          name: file['name'],
+          file: file,
+          size: file.size,
+          path: this.path,
+        };
+        if (existFile && askForAll) {
+          const result = await this.openResolveDialog(existFile);
+          if (result) {
+            askForAll = !result.forAll;
+            if (result.forAll && !result.replaceObject) {
+              skipAll = true;
+            }
+            if (result.replaceObject) {
+              this.addedFiles.push(uploadItem);
+              this.uploadNewFile(uploadItem);
+            }
+          }
+        } else if (!existFile || (existFile && !askForAll && !skipAll)) {
+          this.addedFiles.push(uploadItem);
+          this.uploadNewFile(uploadItem);
+        }
+      }
+    }
+    setTimeout(() => {
+      const element = document.querySelector('#upload-list');
+      element && element.scrollIntoView({ block: 'end', behavior: 'smooth' });
+    }, 0);
+  }
+
+  async openResolveDialog(existFile) {
+    const dialog = this.dialog.open(BucketConfirmationDialogComponent, {
+      data: {items: existFile, type: 'upload-dublicat'} , width: '550px'
+    });
+    return dialog.afterClosed().toPromise().then(result => {
+      return Promise.resolve(result);
+    });
+  }
+
   public onFolderClick(event) {
+    this.searchValue = '';
+    this.clearSelection();
     this.selectedFolder = event.flatNode;
+    if (this.isSelectionOpened) {
+      this.isSelectionOpened = false;
+    }
     this.folderItems = event.element ? event.element.children : event.children;
     if (this.folderItems) {
-      const folders = this.folderItems.filter(v => v.children).sort((a, b) => a.item > b.item ? 1 : -1);
+      this.folders = this.folderItems.filter(v => v.children);
       const files = this.folderItems.filter(v => !v.children).sort((a, b) => a.item > b.item ? 1 : -1);
-      this.folderItems = [...folders, ...files];
+      this.folderItems = [...this.folders, ...files];
+      this.objectPath = event.pathObject;
       this.path = event.path;
+      this.originFolderItems = this.folderItems.map(v => v);
       this.pathInsideBucket = this.path.indexOf('/') !== -1 ?  this.path.slice(this.path.indexOf('/') + 1) + '/' : '';
-      this.bucketName = this.path.substring(0, this.path.indexOf('/')) || this.path;
       this.folderItems.forEach(item => item.isSelected = false);
     }
+  }
 
+  public filterObjects() {
+    this.folderItems = this.originFolderItems.filter(v => v.item.toLowerCase().indexOf(this.searchValue.toLowerCase()) !== -1);
   }
 
-  public deleteAddedFile(file) {
-    this.addedFiles.splice(this.addedFiles.indexOf(file), 1);
+  private clearSelection() {
+    this.folderItems.forEach(item => item.isSelected = false);
+    this.folderItems.forEach(item => item.isFolderSelected = false);
+    this.selected = this.folderItems.filter(item => item.isSelected);
+    this.selectedFolderForAction = this.folderItems.filter(item => item.isFolderSelected);
+    this.selectedItems = [];
   }
 
-  private uploadNewFile() {
-    const path = `${this.pathInsideBucket}${this.uploadForm.get('file').value.name}`;
+  public deleteAddedFile(file) {
+    if ( file.subscr && file.request) {
+      this.dialog.open(BucketConfirmationDialogComponent, {data: {items: file, type: 'cancel-uploading'} , width: '550px'})
+        .afterClosed().subscribe((res) => {
+          res && file.subscr.unsubscribe();
+          res && this.addedFiles.splice(this.addedFiles.indexOf(file), 1);
+          this.isFileUploading = !!this.addedFiles.filter(v => v.status === 'uploading').length;
+          this.sendFile();
+      }, () => {
+        this.isFileUploading = !!this.addedFiles.filter(v => v.status === 'uploading').length;
+        this.sendFile();
+      });
+    } else {
+      this.addedFiles.splice(this.addedFiles.indexOf(file), 1);
+      this.isFileUploading = !!this.addedFiles.filter(v => v.status === 'uploading').length;
+      this.sendFile();
+    }
+
+  }
 
+  private uploadNewFile(file) {
+    const path = file.path.indexOf('/') !== -1 ?  this.path.slice(this.path.indexOf('/') + 1) : '';
+    const fullPath = path ? `${path}/${file.name}` : file.name;
     const formData = new FormData();
-    formData.append('file', this.uploadForm.get('file').value);
-    formData.append('object', path);
+    formData.append('size', file.file.size);
+    formData.append('object', fullPath);
     formData.append('bucket', this.bucketName);
     formData.append('endpoint', this.endpoint);
-    this.isUploading = true;
-    this.bucketBrowserService.uploadFile(formData)
-      .subscribe(() => {
-        this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
-        this.addedFiles = [];
-        this.isUploading = false;
-        this.toastr.success('File successfully uploaded!', 'Success!');
-      }, error => this.toastr.error(error.message || 'File uploading error!', 'Oops!')
-    );
+    formData.append('file', file.file);
+    file.status = 'waiting';
+    file.request = this.bucketBrowserService.uploadFile(formData);
+    this.sendFile(file);
+  }
+
+  public sendFile(file?) {
+    const waitUploading = this.addedFiles.filter(v => v.status === 'waiting');
+    const uploading = this.addedFiles.filter(v => v.status === 'uploading');
+    this.isQueueFull = !!waitUploading.length;
+    this.isFileUploading = !!this.addedFiles.filter(v => v.status === 'uploading').length;
+    // console.log((this.getTokenValidTime() / 1000 / 60 ).toFixed(0) + ' minutes');
+    if ((this.refreshTokenLimit > this.getTokenValidTime()) && !this.isTokenRefreshing) {
+      this.refreshToken();
+    }
+    if (waitUploading.length && uploading.length < 10) {
+      if (!file) {
+        file = waitUploading[0];
+      }
+      file.status = 'uploading';
+      this.isFileUploading = !!this.addedFiles.filter(v => v.status === 'uploading').length;
+      this.isQueueFull = !!this.addedFiles.filter(v => v.status === 'waiting').length;
+      file.subscr =  file.request.subscribe((event: any) => {
+          if (event.type === HttpEventType.UploadProgress) {
+             file.progress = Math.round(95 * event.loaded / event.total);
+            if (file.progress === 95 && !file.interval) {
+              file.interval = setInterval(() => {
+                if (file.progress < 99) {
+                  return file.progress++;
+                }
+              }, file.size < 1094967296 ? 12000 : 20000);
+            }
+          } else if (event['type'] === HttpEventType.Response) {
+            window.clearInterval(file.interval);
+            file.status = 'uploaded';
+            delete file.request;
+            this.sendFile(this.addedFiles.filter(v => v.status === 'waiting')[0]);
+            this.bucketDataService.refreshBucketdata(this.bucketName, this.data.endpoint);
+          }
+        }, error => {
+        window.clearInterval(file.interval);
+          file.status = 'failed';
+          delete file.request;
+          this.sendFile(this.addedFiles.filter(v => v.status === 'waiting')[0]);
+        }
+      );
+    }
+
+  }
+
+  public refreshBucket() {
+    this.path = '';
+    this.bucketDataService.refreshBucketdata(this.bucketName, this.data.endpoint);
+    this.isSelectionOpened = false;
+  }
+
+  public openBucket($event) {
+    this.bucketName = $event.name;
+    this.path = '';
+    this.bucketDataService.refreshBucketdata(this.bucketName, $event.endpoint);
+    this.isSelectionOpened = false;
+  }
+
+  public createFolder(folder) {
+    this.allDisable = true;
+    this.folderTreeComponent.addNewItem(folder, '', false);
   }
 
   public fileAction(action) {
-    this.selected = this.folderItems.filter(item => item.isSelected);
-    const selected = this.folderItems.filter(item => item.isSelected)[0];
-    const path = encodeURIComponent(`${this.pathInsideBucket}${this.selected[0].item}`);
+    const selected = this.folderItems.filter(item => item.isSelected);
+    const folderSelected = this.folderItems.filter(item => item.isFolderSelected);
     if (action === 'download') {
-      selected['isDownloading'] = true;
+      this.clearSelection();
+      this.isActionsOpen = false;
+      const path = encodeURIComponent(`${this.pathInsideBucket}${selected[0].item}`);
+      selected[0]['isDownloading'] = true;
+      this.folderItems.forEach(item => item.isSelected = false);
       this.bucketBrowserService.downloadFile(`/${this.bucketName}/object/${path}/endpoint/${this.endpoint}/download`)
-        .subscribe(data =>  {
-        FileUtils.downloadBigFiles(data, selected.item);
-        selected['isDownloading'] = false;
-        this.folderItems.forEach(item => item.isSelected = false);
-        }, error => this.toastr.error(error.message || 'File downloading error!', 'Oops!')
-      );
+        .subscribe(event =>  {
+            if (event['type'] === HttpEventType.DownloadProgress) {
+              selected[0].progress = Math.round(100 * event['loaded'] / selected[0].object.size);
+            }
+            if (event['type'] === HttpEventType.Response) {
+              FileUtils.downloadBigFiles(event['body'], selected[0].item);
+              setTimeout(() => {
+                selected[0]['isDownloading'] = false;
+                selected[0].progress = 0;
+              }, 1000);
+            }
+        }, error => {
+            this.toastr.error(error.message || 'File downloading error!', 'Oops!');
+            selected[0]['isDownloading'] = false;
+          }
+        );
     }
 
     if (action === 'delete') {
-      this.bucketBrowserService.deleteFile(`/${this.bucketName}/object/${path}/endpoint/${this.endpoint}`).subscribe(() => {
+      const itemsForDeleting = [...folderSelected, ...selected];
+      const objects = itemsForDeleting.map(obj => obj.object.object);
+      const dataForServer = [];
+      objects.forEach(object => {
+        dataForServer.push(...this.bucketDataService.serverData.map(v => v.object).filter(v => v.indexOf(object) === 0));
+      });
+
+      this.dialog.open(BucketConfirmationDialogComponent, {data: {items: itemsForDeleting, type: 'delete'} , width: '550px'})
+        .afterClosed().subscribe((res) => {
+        !res && this.clearSelection();
+        res && this.bucketBrowserService.deleteFile({
+          bucket: this.bucketName, endpoint: this.endpoint, 'objects': dataForServer
+        }).subscribe(() => {
+            this.bucketDataService.refreshBucketdata(this.bucketName, this.data.endpoint);
+            this.toastr.success('Objects successfully deleted!', 'Success!');
+            this.clearSelection();
+          }, error => {
+          this.toastr.error(error.message || 'Objects deleting error!', 'Oops!');
+          this.clearSelection();
+        });
+      });
 
-        this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
-          this.toastr.success('File successfully deleted!', 'Success!');
-        this.folderItems.forEach(item => item.isSelected = false);
-        }, error => this.toastr.error(error.message || 'File deleting error!', 'Oops!')
-      );
     }
   }
+
+  public toogleActions() {
+    this.isActionsOpen = !this.isActionsOpen;
+  }
+
+  public closeActions() {
+    this.isActionsOpen = false;
+  }
+
+  public copyPath() {
+    const selected = this.folderItems.filter(item => item.isSelected || item.isFolderSelected)[0];
+    CopyPathUtils.copyPath(selected.object.bucket + '/' + selected.object.object);
+    this.clearSelection();
+    this.isActionsOpen = false;
+    this.toastr.success('Object path successfully copied!', 'Success!');
+  }
+
+  public toggleBucketSelection() {
+    this.isSelectionOpened = !this.isSelectionOpened;
+  }
+
+  public closeFilterInput() {
+    this.isFilterVisible = false;
+    this.searchValue = '';
+    this.filterObjects();
+  }
+
+  public convertDate(date) {
+    const utcDate = new Date(date);
+    return new Date(utcDate.setTime( utcDate.getTime() - utcDate.getTimezoneOffset() * 60 * 1000 ));
+  }
+
+  // public toggleFileUploaded($event: any) {
+  //   this.isFileUploaded = $event;
+  // }
 }
 
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.module.ts
similarity index 56%
copy from services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
copy to services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.module.ts
index c87d12c..2654704 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.module.ts
@@ -21,17 +21,17 @@ import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
-import { MaterialModule } from '../shared/material.module';
-import { ResourcesComponent } from './resources.component';
-import { ResourcesGridModule } from './resources-grid';
-import { ExploratoryEnvironmentCreateModule } from './exploratory/create-environment';
-import { ManageUngitComponent } from './manage-ungit/manage-ungit.component';
-import { ConfirmDeleteAccountDialog } from './manage-ungit/manage-ungit.component';
-import {BucketBrowserComponent} from './bucket-browser/bucket-browser.component';
-import {FolderTreeComponent} from './bucket-browser/folder-tree/folder-tree.component';
-import {MatTreeModule} from '@angular/material/tree';
-import {BucketDataService} from './bucket-browser/bucket-data.service';
+import { MaterialModule } from '../../shared/material.module';
+import { ResourcesGridModule } from '../resources-grid';
+import { ExploratoryEnvironmentCreateModule } from '../exploratory/create-environment';
 
+import {BucketBrowserComponent} from './bucket-browser.component';
+import {FolderTreeComponent} from './folder-tree/folder-tree.component';
+import {MatTreeModule} from '@angular/material/tree';
+import {BucketDataService} from './bucket-data.service';
+import {BucketConfirmationDialogComponent} from './bucket-confirmation-dialog/bucket-confirmation-dialog.component';
+import {BucketTreeComponent} from './buckets-tree/bucket-tree.component';
+import {ConvertFileSizePipeModule} from '../../core/pipes/convert-file-size';
 
 @NgModule({
   imports: [
@@ -41,17 +41,16 @@ import {BucketDataService} from './bucket-browser/bucket-data.service';
     ResourcesGridModule,
     ExploratoryEnvironmentCreateModule,
     MaterialModule,
-    MatTreeModule
+    MatTreeModule,
+    ConvertFileSizePipeModule
   ],
   declarations: [
-    ResourcesComponent,
-    ManageUngitComponent,
-    ConfirmDeleteAccountDialog,
     BucketBrowserComponent,
-    FolderTreeComponent
+    FolderTreeComponent,
+    BucketTreeComponent,
+    BucketConfirmationDialogComponent
   ],
-  entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialog, BucketBrowserComponent, FolderTreeComponent],
+  entryComponents: [BucketBrowserComponent, FolderTreeComponent, BucketTreeComponent, BucketConfirmationDialogComponent],
   providers: [BucketDataService],
-  exports: [ResourcesComponent]
 })
-export class ResourcesModule { }
+export class BucketBrowserModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html
new file mode 100644
index 0000000..642e1e6
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html
@@ -0,0 +1,122 @@
+<!--
+  ~ 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.
+  -->
+
+<div id="dialog-box" class="confirmation-dialog">
+
+  <header class="dialog-header">
+    <h4 class="modal-title">
+        <span *ngIf="data.type === 'delete'">Delete</span>
+        <span *ngIf="data.type === 'upload-dublicat'">Resolve conflicts</span>
+        <span *ngIf="data.type === 'cancel-uploading'">Cancel</span>
+        <span *ngIf="data.type === 'uploading-error'">Upload limitation</span>
+    </h4>
+    <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
+  </header>
+
+  <div class="dialog-content">
+    <div class="content-box">
+      <div *ngIf="data.type === 'delete'">
+        <mat-list class="resources">
+
+          <mat-list-item class="list-header">
+            <div class="object">Object</div>
+            <div class="size">Size</div>
+          </mat-list-item>
+
+          <div class="scrolling-content delete-list" id="scrolling">
+
+            <mat-list-item *ngFor="let object of data.items" class="delete-item">
+              <div class="object">
+                <span *ngIf="object['children']"><i class="material-icons folder-icon" >folder</i></span>
+                <span *ngIf="!object['children']"><i class="material-icons folder-icon file-icon" >description</i></span>
+                <div class="ellipsis"
+                     matTooltip="{{object['item']}}"
+                     matTooltipPosition="above"
+                     matTooltipShowDelay="1000"
+                     [matTooltipClass]="'bucket-item-tooltip'">{{object['item']}}</div>
+              </div>
+              <div  class="size">{{object['children'] ? '-' : object['object'].size | convertFileSize}}</div>
+            </mat-list-item>
+
+          </div>
+        </mat-list>
+
+        <div mat-dialog-content class="bottom-message" *ngIf="data.type === 'delete'">
+          <span class="confirm-message" *ngIf="isFolders">All affected objects will be deleted.</span>
+          <span class="confirm-message" *ngIf="!isFolders"><span *ngIf="data.items.length > 1">These objects</span><span *ngIf="data.items.length === 1">This object</span> will be deleted.</span>
+        </div>
+
+        <div class="text-center m-top-20">
+          <p class="strong">Do you want to proceed?</p>
+        </div>
+      </div>
+
+      <div *ngIf="data.type === 'upload-dublicat'">
+        <p>
+          <span
+          class="strong upload-item-name ellipsis"
+          matTooltip="{{data.items}}"
+          matTooltipPosition="above"
+          matTooltipShowDelay="1000"
+          [matTooltipClass]="'bucket-item-tooltip'"
+        >{{data.items}}</span> already exists in selected folder. How would you like to resolve this conflict?</p>
+        <mat-radio-group
+          aria-labelledby="upload-radio-group-label"
+          class="upload-radio-group"
+          [(ngModel)]="fileAction">
+          <mat-radio-button class="upload-radio-button" *ngFor="let action of uploadActions" [value]="action">
+            {{action}}
+          </mat-radio-button>
+        </mat-radio-group>
+        <div class="repeat-for-all" >
+          <div class="empty-checkbox" [ngClass]="{'checked': actionForAll}" (click)="toggleActionForAll();$event.stopPropagation()" >
+            <span class="checked-checkbox" *ngIf="actionForAll"></span>
+          </div>
+          <span class="repeat-message" (click)="toggleActionForAll();$event.stopPropagation()">Repeat for all remaining conflicts</span>
+        </div>
+      </div>
+
+      <div *ngIf="data.type === 'cancel-uploading'">
+        <p class="upload-message"><span>Cancel uploading file </span> <span class="strong ellipsis upload-item-name">{{data.items.name}}.</span></p>
+        <div class="text-center m-top-20">
+          <span class="strong">Do you want to proceed?</span>
+        </div>
+      </div>
+
+      <div *ngIf="data.type === 'uploading-error'">
+        <p class="upload-limit-message" *ngIf="data.items.toMany">Only the first fifty objects will be uploaded.</p>
+        <p class="upload-limit-message" *ngIf="data.items.toBig">Only file(s) within 4 GB will be uploaded.</p>
+        <div class="text-center m-top-20">
+          <span class="strong">Do you want to proceed?</span>
+        </div>
+      </div>
+
+      <div class="text-center m-top-20" *ngIf="data.type === 'delete' || data.type === 'cancel-uploading' || data.type === 'uploading-error'">
+        <button  mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">No</button>
+        <button mat-raised-button type="button" class="butt butt-success action" (click)="dialogRef.close(true)">Yes</button>
+      </div>
+
+      <div class="text-center m-top-20" *ngIf="data.type === 'upload-dublicat'">
+        <button  mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button>
+        <button mat-raised-button type="button" class="butt butt-success action" (click)="submitResolving()">Continue</button>
+      </div>
+
+    </div>
+  </div>
+</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss
new file mode 100644
index 0000000..4d7ab0c
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss
@@ -0,0 +1,176 @@
+/*!
+ * 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.
+ */
+
+.confirmation-dialog {
+
+  .folder-icon{
+    color: rgb(232, 232, 232);
+    margin-right: 3px;
+    transform: translateX(-2px);
+    &.file-icon{
+      transform: translateX(-4px);
+      margin-right: -2px;
+    }
+  }
+  h3{
+    margin-bottom: 20px;
+  }
+  color: #718ba6;
+  p {
+    font-size: 14px;
+    font-weight: 400;
+    margin: 0;
+    margin-bottom: 10px;
+    &.upload-limit-message{
+      text-align: center;
+      color: red;
+      font-size: 14px;
+    }
+    &.info {
+      font-weight: 500;
+    }
+  }
+  .resources {
+    .mat-list-base .mat-list-item.delete-item{
+      height: 30px;
+    }
+
+    .object {
+      width: 70%;
+      display: flex;
+      align-items: center;
+      padding-right: 10px;
+    }
+
+    .size {
+      width: 30%;
+    }
+    .scrolling-content.delete-list {
+      max-height: 200px;
+      overflow-y: auto;
+      padding-top: 11px;
+    }
+  }
+  .empty-list {
+    display: flex;
+    width: 100%;
+    justify-content: center;
+    color: #35afd5;
+    padding: 15px;
+  }
+
+  .list-header {
+    border-top: 1px solid #edf1f5;
+    border-bottom: 1px solid #edf1f5;
+    color: #577289;
+    width: 100%;
+  }
+
+  .bottom-message{
+    padding-top: 15px;
+    text-align: center;
+    .confirm-message{
+      color: #ef5c4b;
+      font-size: 13px;
+      min-height: 18px;
+      text-align: center;
+      padding-top: 20px}
+  }
+.upload-item-name{
+  max-width: 300px;
+  display: inline-block;
+  margin-bottom: -4px;
+}
+
+.mat-radio-button{
+  font-family: inherit;
+  .mat-radio-ripple{
+    display: none;
+  }
+}
+  .upload-radio-group{
+    display: flex;
+    flex-direction: column;
+    .upload-radio-button {
+      padding: 10px 0;
+
+      .mat-radio-container {
+        width: 15px;
+        height: 15px;
+        .mat-radio-outer-circle, .mat-radio-inner-circle  {
+          width: 15px;
+          height: 15px;
+          border-color: lightgrey;
+        }
+      }
+
+      &.mat-radio-checked {
+        .mat-radio-outer-circle {
+          border-color: #35afd5;
+        }
+
+        .mat-radio-inner-circle {
+          background-color: #35afd5;
+        }
+      }
+    }
+  }
+  .repeat-for-all{
+    display: flex;
+    margin-top: 15px;
+    margin-bottom: 40px;
+    .repeat-message{
+      padding-left: 5px;
+      cursor: pointer;
+    }
+  }
+  .empty-checkbox {
+    min-width: 16px;
+    width: 16px;
+    height: 16px;
+    border-radius: 2px;
+    border: 2px solid lightgrey;
+    margin-top: 2px;
+    position: relative;
+    cursor: pointer;
+    &.checked {
+      border-color: #35afd5;
+      background-color: #35afd5;
+    }
+    .checked-checkbox {
+      top: 0;
+      left: 4px;
+      width: 5px;
+      height: 10px;
+      border-bottom: 2px solid white;
+      border-right: 2px solid white;
+      position: absolute;
+      transform: rotate(45deg);
+    }
+  }
+}
+.upload-message{
+  display: flex;
+  .upload-item-name{
+    max-width: 340px;
+    display: block;
+    padding-left: 4px;
+  }
+}
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts
new file mode 100644
index 0000000..2d75b20
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+import { Component, OnInit, Inject, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
+import { ToastrService } from 'ngx-toastr';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+  selector: 'bucket-confirmation-dialog',
+  templateUrl: 'bucket-confirmation-dialog.component.html',
+  styleUrls: ['./bucket-confirmation-dialog.component.scss'],
+  encapsulation: ViewEncapsulation.None
+})
+
+export class BucketConfirmationDialogComponent implements OnInit {
+  isFolders: boolean = false;
+  uploadActions = ['Replace existing object', 'Skip uploading object'];
+  fileAction: string = this.uploadActions[1];
+  actionForAll: boolean = false;
+  constructor(
+    @Inject(MAT_DIALOG_DATA) public data: any,
+    public dialogRef: MatDialogRef<BucketConfirmationDialogComponent>,
+    public toastr: ToastrService
+  ) {
+
+  }
+
+  ngOnInit() {
+    if (this.data.type === 'delete') {
+      this.isFolders = !!this.data.items.filter(v => v.children).length;
+    }
+  }
+
+  toggleActionForAll() {
+    this.actionForAll = !this.actionForAll;
+  }
+
+  submitResolving() {
+    const submitObj = {replaceObject: !this.uploadActions.indexOf(this.fileAction), forAll: this.actionForAll};
+    this.dialogRef.close(submitObj);
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
index f87c575..0ca004e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
@@ -21,14 +21,17 @@ import { Injectable } from '@angular/core';
 import { BehaviorSubject} from 'rxjs';
 import {BucketBrowserService, TodoItemNode} from '../../core/services/bucket-browser.service';
 
-const array = [{'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '4.txt', 'size': '18 bytes', 'creationDate': '21-4-2020 11:36:36'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '5.txt', 'size': '18 bytes', 'creationDate': '21-4-2020 11:56:46'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'Untitled', 'size': '5 bytes', 'creationDate': '13-4-2020 03:39:11'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'adasdas', 'size': '1 KB', 'creationDate': '15-4-2020 02:17 [...]
 
+const array = [{'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'folder11/', 'size': '18 bytes', 'lastModifiedDate': '21-4-2020 11:36:36'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'folder11/dsafaraorueajkegrgavhsfnvgahsfgsdjfhagsdjfg497frgfhsdajfsgdafjsxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzdvcfdvbgffgsdfgdsafaraorueajkegrgavhsfnvgahsfgsdjfhagsdjfg497frgfhsdajfsgdafjsxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzdvcfdvbgffgsdfgdsfgsdfgggggg', ' [...]
+{'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '3.jpg', 'size': '5 bytes', 'lastModifiedDate': '14-4-2020 09:36:16'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '2.jpg', 'size': '52 bytes', 'lastModifiedDate': '17-4-2020 12:13:26'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '1test', 'size': '112 bytes', 'lastModifiedDate': '14-4-2020 04:55:02'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'test.pem', 'size': '1 KB', 'lastModifiedDate': '14-4-2020 04:57 [...]
+{'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '5.jpg', 'size': '52 bytes', 'lastModifiedDate': '17-4-2020 12:13:26'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'test', 'size': '12 bytes', 'lastModifiedDate': '14-4-2020 04:55:02'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'test.pem', 'size': '1 KB', 'lastModifiedDate': '14-4-2020 04:57:54'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'test1', 'size': '52 bytes', 'lastModifiedDate': '17-4-2020 11:22: [...]
 
 @Injectable()
 export class BucketDataService {
   public _bucketData = new BehaviorSubject<any>(null);
-  private serverData: any = [];
+  public serverData: any = [];
   get data(): TodoItemNode[] { return this._bucketData.value; }
+  emptyFolder = null;
   constructor(
     private bucketBrowserService: BucketBrowserService,
   ) {
@@ -37,43 +40,63 @@ export class BucketDataService {
   public refreshBucketdata(bucket, endpoint) {
     let backetData = [];
     this.bucketBrowserService.getBucketData(bucket, endpoint).subscribe(v => {
+      const copiedData = JSON.parse(JSON.stringify(v));
+
       this.serverData = v;
-      backetData = this.convertToFolderTree(v);
+      if (this.emptyFolder) {
+        copiedData.unshift(this.emptyFolder);
+      }
+      backetData = this.convertToFolderTree(copiedData);
       const data = this.buildFileTree({[bucket]: backetData}, 0);
       this._bucketData.next(data);
     });
-
-      // this.serverData = array;
-      // backetData = this.convertToFolderTree(array);
-      // const data = this.buildFileTree({[bucket]: backetData}, 0);
-      // this._bucketData.next(data);
-    // }
+    // this.serverData = array;
+    // backetData = this.convertToFolderTree(array);
+    // const data = this.buildFileTree({[bucket]: backetData}, 0);
+    // this._bucketData.next(data);
   }
+
   public buildFileTree(obj: {[key: string]: any}, level: number): TodoItemNode[] {
       return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
+        if (key === '') {
+          return accumulator;
+        }
         const value = obj[key];
         const node = new TodoItemNode();
         node.item = key;
-        if (Object.keys(value).length) {
+        if (value === '') {
+          node.children = this.buildFileTree({}, level + 1);
+          return accumulator.concat(node);
+        }
+        if (Object.keys(value).filter(v => v !== 'obj').length > 0) {
           if (typeof value === 'object') {
+            node.object = value.obj || {'bucket': node.item, 'object': '', 'size': '', 'lastModifiedDate': ''};
+            delete value.obj;
             node.children = this.buildFileTree(value, level + 1);
           } else {
             node.item = value;
           }
         } else {
-          node.size = this.serverData.filter(v => v.object.indexOf(node.item) !== -1)[0];
+          node.object = value.obj;
         }
         return accumulator.concat(node);
       }, []);
     }
 
-  public insertItem(parent: TodoItemNode, name, isFile) {
-      if (parent.children) {
+  public insertItem(parent: TodoItemNode, name, isFile, emptyFolderObj?) {
+    if (parent.children) {
         if (isFile) {
-          parent.children.push(name as TodoItemNode);
+          parent.children.unshift(name as TodoItemNode);
         } else {
-          parent.children.unshift({item: name, children: []} as TodoItemNode);
-          this._bucketData.next(this.data);
+          if (name) {
+            const child = {item: name, children: [], object: JSON.parse(JSON.stringify(parent.object))};
+            child.object.object = child.object.object.slice(0, -1) + child.item + '/';
+            parent.children.unshift(child as TodoItemNode);
+          } else {
+            parent.children.unshift({item: '', children: [], object: {}} as TodoItemNode);
+            this.emptyFolder = emptyFolderObj;
+            this._bucketData.next(this.data);
+          }
         }
       }
     }
@@ -83,26 +106,36 @@ export class BucketDataService {
       this._bucketData.next(this.data);
     }
 
-    public processFiles = (files, target) => {
+  public removeItem(parent, child) {
+     parent.children.splice( parent.children.indexOf(child), 1);
+     this._bucketData.next(this.data);
+    }
+
+    public processFiles = (files, target, object) => {
       let pointer = target;
       files.forEach((file) => {
         if (!pointer[file]) {
           pointer[file] = {};
         }
         pointer = pointer[file];
+        if (!pointer.obj) {
+          pointer.obj = object;
+        }
       });
-    }
+    };
 
     public processFolderArray = (acc, curr) => {
-      const files = curr.object.split('/').filter(x => x.length > 0);
-      this.processFiles(files, acc);
-      return acc;
-    }
+      const files = curr.object.split('/');
+      this.processFiles(files, acc, curr);
 
-    public convertToFolderTree = (data) => data
-      .reduce(
-        this.processFolderArray,
-        {}
-      )
+      return acc;
+    };
 
+    public convertToFolderTree = (data) => {
+      const finalData = data.reduce(this.processFolderArray, {});
+      if (Object.keys(finalData).length === 0) {
+        return '';
+      }
+      return finalData;
+    }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.html
new file mode 100644
index 0000000..8d294a5
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.html
@@ -0,0 +1,25 @@
+<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
+  <mat-tree-node
+    *matTreeNodeDef="let node"
+    matTreeNodePadding matTreeNodePaddingIndent="17"
+    (click)="openBucketData(node)"
+    [ngClass]="{'active-item': activeBacket === node}">
+    <button mat-icon-button disabled></button>
+    <div
+      class="ellipsis"
+      matTooltip="{{node.name}}"
+      matTooltipPosition="above"
+      matTooltipShowDelay="1000"
+      [matTooltipClass]="'bucket-item-tooltip'"
+    >{{node.name}}</div>
+  </mat-tree-node>
+  <mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding  matTreeNodePaddingIndent="17">
+    <button mat-icon-button matTreeNodeToggle
+            [attr.aria-label]="'toggle ' + node.name">
+      <mat-icon class="mat-icon-rtl-mirror">
+        {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
+      </mat-icon>
+    </button>
+    <span (click)="toggleProject(node, treeControl.isExpanded(node))">{{node.name}}</span>
+  </mat-tree-node>
+</mat-tree>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.scss
new file mode 100644
index 0000000..fd06d7a
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.scss
@@ -0,0 +1,106 @@
+
+.folder{
+  padding-left: 5px;
+  max-width: 350px;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+
+}
+
+.mat-tree{
+  font-family: 'Open Sans', sans-serif;
+}
+
+.folder-icon{
+  color: rgb(232, 232, 232);
+}
+
+.folder-item-line{
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+
+}
+
+.active-item {
+  color: #00bcd4;
+  i{
+    color: #00bcd4;
+  }
+
+
+}
+
+.add-folder-form{
+  display: flex;
+  align-items: center;
+}
+
+.mat-tree-node:not(.input-node){
+  cursor: pointer;
+  transition: .3s;
+  overflow: unset;
+  min-height: auto;
+  button.mat-icon-button{
+    width: 25px;
+    height: 25px;
+    line-height: 28px;
+  }
+  &:hover{
+    color: #00bcd4;
+    i{
+      color: #00bcd4;
+    }
+  }
+}
+
+.input-node {
+  overflow: unset;
+  padding-top: 5px;
+  padding-bottom: 7px;
+
+
+  button.mat-icon-button {
+    &.action-btn {
+      color: lightgrey;
+      font-size: 20px;
+      cursor: not-allowed;
+    }
+
+    &.check {
+      color: #49af38;
+      cursor: pointer;
+
+      &:hover {
+        border-color: #49af38;
+        background: #f9fafb;
+      }
+    }
+
+    &.close {
+      color: #f1696e;
+      cursor: pointer;
+
+      &:hover {
+        border-color: #f1696e;
+        background: #f9fafb;
+      }
+    }
+  }
+
+  .mat-error {
+    background-color: #ffffff;
+  }
+
+  .d-none{
+    display: none;
+  }
+
+}
+
+
+
+
+
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.ts
new file mode 100644
index 0000000..ed222f6
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.ts
@@ -0,0 +1,101 @@
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {FlatTreeControl} from '@angular/cdk/tree';
+import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
+
+
+interface BucketNode {
+  name: string;
+  endpoint?: string;
+  children?: BucketNode[];
+}
+
+/** Flat node with expandable and level information */
+interface BucketFlatNode {
+  expandable: boolean;
+  name: string;
+  level: number;
+}
+
+@Component({
+  selector: 'dlab-bucket-tree',
+  templateUrl: './bucket-tree.component.html',
+  styleUrls: ['./bucket-tree.component.scss']
+})
+
+export class BucketTreeComponent implements OnInit {
+  @Output() emitActiveBucket: EventEmitter<{}> = new EventEmitter();
+  @Input() openedBucket: string;
+  @Input() buckets: BucketNode[];
+
+  private _transformer = (node: BucketNode, level: number) => {
+    return {
+      expandable: !!node.children && node.children.length > 0,
+      name: node.name,
+      endpoint: node.endpoint,
+      level: level,
+    };
+  }
+
+  treeControl = new FlatTreeControl<BucketFlatNode>(
+    node => node.level, node => node.expandable);
+
+  treeFlattener = new MatTreeFlattener(
+    this._transformer, node => node.level, node => node.expandable, node => node.children);
+
+  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
+  private activeBucketName: string;
+  private activeBacket: any;
+
+  constructor() {
+  }
+
+  ngOnInit() {
+    this.activeBucketName = this.openedBucket || '';
+    this.dataSource.data = this.buckets;
+    this.setActiveBucket();
+  }
+
+  public openBucketData(bucket) {
+    this.dataSource['_treeControl'].collapseAll();
+    this.setActiveBucket(bucket);
+    this.emitActiveBucket.emit(bucket);
+  }
+
+  public setActiveBucket(bucket?) {
+    this.activeBacket = bucket || this.dataSource._flattenedData.getValue().filter(v => v.name === this.openedBucket)[0];
+    this.expandAllParents(this.activeBacket);
+  }
+
+  public toggleProject(el, isExpanded){
+    isExpanded ? this.treeControl.collapse(el) : this.treeControl.expand(el);
+  }
+
+  private expandAllParents(el) {
+    if (el) {
+      this.treeControl.expand(el);
+      if (this.getParentNode(el) !== null) {
+        this.expandAllParents(this.getParentNode(el));
+      }
+    }
+  }
+
+  private getParentNode(node: BucketFlatNode): BucketFlatNode | null {
+    const currentLevel = node.level;
+    if (currentLevel < 1) {
+      return null;
+    }
+
+    const startIndex = this.treeControl.dataNodes.indexOf(node) - 1;
+
+    for (let i = startIndex; i >= 0; i--) {
+      const currentNode = this.treeControl.dataNodes[i];
+
+      if (currentNode.level < currentLevel) {
+        return currentNode;
+      }
+    }
+    return null;
+  }
+
+  public hasChild = (_: number, node: BucketFlatNode) => node.expandable;
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
index b663e78..dab723d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
@@ -1,32 +1,65 @@
 <mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
-  <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding [ngStyle]="{'display': 'none'}">
-  <button mat-icon-button disabled></button>
-  <!--    <mat-checkbox class="checklist-leaf-node"-->
-  <!--                  [checked]="checklistSelection.isSelected(node)"-->
-  <!--                  (change)="todoLeafItemSelectionToggle(node)"></mat-checkbox>-->
-  {{node.item}}
-</mat-tree-node>
 
-  <mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding>
+  <mat-tree-node
+    *matTreeNodeDef="let node"
+    matTreeNodeToggle matTreeNodePadding
+    matTreeNodePaddingIndent="17"
+    [ngStyle]="{'display': 'none'}"
+  >
     <button mat-icon-button disabled></button>
-    <mat-form-field>
-      <mat-label>New folder</mat-label>
-      <input matInput #itemValue>
-    </mat-form-field>
-    <button mat-button (click)="saveNode(node, itemValue.value)">Save</button>
+    {{node.item}}
   </mat-tree-node>
 
-  <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
+  <mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding matTreeNodePaddingIndent="17" class="input-node">
+    <form class="add-folder-form" id="folder-form">
+      <mat-form-field>
+        <mat-label>New folder</mat-label>
+        <input matInput #itemValue [formControl]="folderFormControl" [errorStateMatcher]="matcher">
+        <mat-error *ngIf="!folderFormControl.hasError('required') && !folderFormControl.hasError('isDuplicate')">
+          The folder name can only contain Latin letters, numbers and special characters except for #, ?, /, \, "
+        </mat-error>
+        <mat-error *ngIf="folderFormControl.hasError('required')">
+          Folder name is <strong>required</strong>
+        </mat-error>
+        <mat-error *ngIf="folderFormControl.hasError('isDuplicate')">
+          Folder with this name already exists
+        </mat-error>
+      </mat-form-field>
+      <button (click)="saveNode(node, itemValue.value)"
+              [ngClass]="{'check': folderFormControl.valid && folderFormControl.dirty && !folderCreating}"
+              mat-icon-button class="btn action-btn"
+              [disabled]="!folderFormControl.valid || !folderFormControl.dirty"
+              matTooltip="Please wait! Folder is creating."
+              [matTooltipDisabled]="!folderCreating"
+              matTooltipPosition="above"
+      >
+        <span><i class="material-icons ">check</i></span></button>
+      <button (click)="removeItem(node)" mat-icon-button class="btn close action-btn"><span ><i class="material-icons ">close</i></span></button>
+    </form>
+  </mat-tree-node>
+
+  <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding  matTreeNodePaddingIndent="17"  [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
     <button mat-icon-button matTreeNodeToggle
-            [attr.aria-label]="'toggle ' + node.filename">
-      <mat-icon class="mat-icon-rtl-mirror" [ngClass]="{'active-item': selectedFolder === node}">
+            [attr.aria-label]="'toggle ' + node.filename" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">
+      <mat-icon class="mat-icon-rtl-mirror" [ngClass]="{'active-item': (selectedFolder === node && !bucketDataService.emptyFolder)}">
         {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
       </mat-icon>
     </button>
-<!--    <mat-checkbox [checked]="descendantsAllSelected(node)"-->
-<!--                  [indeterminate]="descendantsPartiallySelected(node)"-->
-<!--                  (change)="todoItemSelectionToggle(node)"></mat-checkbox>-->
-    <div (click)="showItem(node)" class="folder-item-line"  [ngClass]="{'active-item': selectedFolder === node}"><i class="material-icons folder-icon">folder</i> <span class="folder">{{node.item}}</span></div>
-<!--    <button mat-icon-button (click)="addNewItem(node)"><mat-icon>add</mat-icon></button>-->
+    <div
+      (click)="showItem(node)"
+      class="folder-item-line"
+      [ngClass]="{'active-item': (selectedFolder === node && !bucketDataService.emptyFolder), 'not-allowed': bucketDataService.emptyFolder}"
+    >
+      <i class="material-icons folder-icon">folder</i>
+      <span
+        class="folder"
+        matTooltip="{{node.item}}"
+        matTooltipPosition="above"
+        matTooltipShowDelay="1000"
+        [matTooltipClass]="'bucket-item-tooltip'"
+      >
+        {{node.item}}
+      </span>
+    </div>
   </mat-tree-node>
 </mat-tree>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss
index e9902af..22c36d0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss
@@ -1,6 +1,14 @@
 
 .folder{
   padding-left: 5px;
+  max-width: 350px;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
+
+.mat-tree{
+  font-family: 'Open Sans', sans-serif;
 }
 
 .folder-icon{
@@ -15,7 +23,6 @@
 }
 
 .active-item {
-  //border-bottom: 1px solid #00bcd4;
   color: #00bcd4;
   i{
     color: #00bcd4;
@@ -24,10 +31,24 @@
 
 }
 
-.mat-tree-node{
+.add-folder-form{
+  display: flex;
+  align-items: center;
+}
+
+.mat-tree-node:not(.input-node){
   cursor: pointer;
   transition: .3s;
   overflow: unset;
+  min-height: auto;
+  button.mat-icon-button{
+    width: 25px;
+    height: 25px;
+    line-height: 28px;
+  }
+}
+
+.mat-tree-node:not(.input-node):not(.cursor-not-allow){
   &:hover{
     color: #00bcd4;
     i{
@@ -36,3 +57,47 @@
   }
 }
 
+.input-node {
+  overflow: unset;
+  padding-top: 5px;
+  padding-bottom: 7px;
+
+
+  button.mat-icon-button {
+    &.action-btn {
+      color: lightgrey;
+      font-size: 20px;
+      cursor: not-allowed;
+    }
+
+    &.check {
+      color: #49af38;
+      cursor: pointer;
+
+      &:hover {
+        border-color: #49af38;
+        background: #f9fafb;
+      }
+    }
+
+    &.close {
+      color: #f1696e;
+      cursor: pointer;
+
+      &:hover {
+        border-color: #f1696e;
+        background: #f9fafb;
+      }
+    }
+  }
+
+  .mat-error {
+    background-color: #ffffff;
+  }
+}
+
+
+
+
+
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
index 179a4c1..fa40d1e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
@@ -1,10 +1,21 @@
-import {Component, OnInit, AfterViewInit, Output, EventEmitter, OnDestroy} from '@angular/core';
-import {SelectionModel} from '@angular/cdk/collections';
+import {Component, Output, EventEmitter, OnDestroy, Input, OnInit} from '@angular/core';
 import {FlatTreeControl} from '@angular/cdk/tree';
 import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
 import {BucketBrowserService, TodoItemFlatNode, TodoItemNode} from '../../../core/services/bucket-browser.service';
 import {BucketDataService} from '../bucket-data.service';
 import {Subscription} from 'rxjs';
+import {FormControl, FormGroupDirective, NgForm, Validators} from '@angular/forms';
+import {ErrorStateMatcher} from '@angular/material/core';
+import {PATTERNS} from '../../../core/util';
+import {ToastrService} from 'ngx-toastr';
+import {HttpResponse} from '@angular/common/http';
+
+export class MyErrorStateMatcher implements ErrorStateMatcher {
+  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
+    const isSubmitted = form && form.submitted;
+    return !!(control && control.invalid && (control.dirty));
+  }
+}
 
 
 
@@ -14,44 +25,57 @@ import {Subscription} from 'rxjs';
   styleUrls: ['./folder-tree.component.scss']
 })
 
-export class FolderTreeComponent implements OnInit, OnDestroy {
+export class FolderTreeComponent implements OnDestroy {
 
   @Output() showFolderContent: EventEmitter<any> = new EventEmitter();
+  @Output() disableAll: EventEmitter<any> = new EventEmitter();
+  @Input() folders;
+  @Input() endpoint;
+
   private folderTreeSubs;
   private path = [];
   private selectedFolder: TodoItemFlatNode;
   private flatNodeMap = new Map<TodoItemFlatNode, TodoItemNode>();
   private nestedNodeMap = new Map<TodoItemNode, TodoItemFlatNode>();
-  private selectedParent: TodoItemFlatNode | null = null;
-  private newItemName = '';
+
+  private folderCreating = false;
   private subscriptions: Subscription = new Subscription();
   public treeControl: FlatTreeControl<TodoItemFlatNode>;
   private treeFlattener: MatTreeFlattener<TodoItemNode, TodoItemFlatNode>;
   public dataSource: MatTreeFlatDataSource<TodoItemNode, TodoItemFlatNode>;
 
-  private checklistSelection = new SelectionModel<TodoItemFlatNode>(true /* multiple */);
-
   constructor(
+    public toastr: ToastrService,
     private bucketBrowserService: BucketBrowserService,
     private bucketDataService: BucketDataService,
-    ) {
+  ) {
     this.treeFlattener = new MatTreeFlattener(this.transformer, this.getLevel, this.isExpandable, this.getChildren);
     this.treeControl = new FlatTreeControl<TodoItemFlatNode>(this.getLevel, this.isExpandable);
     this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
-
-    this.subscriptions.add(bucketDataService._bucketData.subscribe(data => {
-     if (data) {
-       this.dataSource.data = data;
+    this.subscriptions.add(this.bucketDataService._bucketData.subscribe(data => {
+      if (data) {
+        this.dataSource.data = data;
         const subject = this.dataSource._flattenedData;
-       this.folderTreeSubs = subject.subscribe((subjectData) => {
+        const subjectData = subject.getValue();
           if (this.selectedFolder) {
             this.selectedFolder = subjectData.filter(v => v.item === this.selectedFolder.item && v.level === this.selectedFolder.level)[0];
           }
           this.expandAllParents(this.selectedFolder || subjectData[0]);
           this.showItem(this.selectedFolder || subjectData[0]);
-        });
+          if (this.selectedFolder && !this.bucketDataService.emptyFolder) {
+            setTimeout(() => {
+              const element = document.querySelector('.folder-item-line.active-item');
+              element && element.scrollIntoView({ block: 'center', behavior: 'smooth' });
+            }, 0);
+          } else if (this.selectedFolder && this.bucketDataService.emptyFolder) {
+            setTimeout(() => {
+              const element = document.querySelector('#folder-form');
+              element && element.scrollIntoView({ block: 'end', behavior: 'smooth' });
+            }, 0);
+          }
       }
     }));
+    this.dataSource._flattenedData.subscribe();
   }
 
   getLevel = (node: TodoItemFlatNode) => node.level;
@@ -62,7 +86,7 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
 
   hasChild = (_: number, _nodeData: TodoItemFlatNode) => _nodeData.expandable;
 
-  hasNoContent = (_: number, _nodeData: TodoItemFlatNode) => _nodeData.item === '';
+  hasNoContent = (_: number, _nodeData: TodoItemFlatNode) => _nodeData.item === '' || _nodeData.item === 'ا';
 
   transformer = (node: TodoItemNode, level: number) => {
     const existingNode = this.nestedNodeMap.get(node);
@@ -77,12 +101,25 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
     return flatNode;
   }
 
-
-  ngOnInit() {
-  }
-
   ngOnDestroy() {
     this.bucketDataService._bucketData.next([]);
+    this.subscriptions.unsubscribe();
+    this.bucketDataService.emptyFolder = null;
+  }
+
+  folderFormControl = new FormControl('', [
+    Validators.required,
+    Validators.pattern(PATTERNS.folderRegex),
+    this.duplicate.bind(this)
+  ]);
+
+  matcher = new MyErrorStateMatcher();
+
+  private duplicate(control) {
+    if (control && control.value) {
+      const isDublicat = this.folders.slice(1).some(folder => folder.item === control.value);
+      return isDublicat ? { isDuplicate: true } : null;
+    }
   }
 
   private showItem(el) {
@@ -94,7 +131,8 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
       const data = {
         flatNode: el,
         element: this.flatNodeMap.get(el),
-        path: path.join('/')
+        path: path.map(v => v.item).join('/'),
+        pathObject: path
       };
       this.showFolderContent.emit(data);
     }
@@ -103,10 +141,10 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
   private getPath(el) {
     if (el) {
       if (this.path.length === 0) {
-        this.path.unshift(el.item);
+        this.path.unshift(el);
       }
       if (this.getParentNode(el) !== null) {
-        this.path.unshift(this.getParentNode(el).item);
+        this.path.unshift(this.getParentNode(el));
         this.getPath(this.getParentNode(el));
       }
       return this.path;
@@ -122,63 +160,6 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
     }
   }
 
-  private descendantsAllSelected(node: TodoItemFlatNode): boolean {
-
-    const descendants = this.treeControl.getDescendants(node);
-    const descAllSelected = descendants.every(child =>
-      this.checklistSelection.isSelected(child)
-    );
-    return descAllSelected;
-  }
-
-  private descendantsPartiallySelected(node: TodoItemFlatNode): boolean {
-
-    const descendants = this.treeControl.getDescendants(node);
-    const result = descendants.some(child => this.checklistSelection.isSelected(child));
-    return result && !this.descendantsAllSelected(node);
-  }
-
-  private todoItemSelectionToggle(node: TodoItemFlatNode): void {
-    this.checklistSelection.toggle(node);
-  const descendants = this.treeControl.getDescendants(node);
-  this.checklistSelection.isSelected(node)
-? this.checklistSelection.select(...descendants)
-    : this.checklistSelection.deselect(...descendants);
-
-  // Force update for the parent
-  descendants.every(child =>
-  this.checklistSelection.isSelected(child)
-);
-  this.checkAllParentsSelection(node);
-}
-
-  private todoLeafItemSelectionToggle(node: TodoItemFlatNode): void {
-    this.checklistSelection.toggle(node);
-    this.checkAllParentsSelection(node);
-  }
-
-  private checkAllParentsSelection(node: TodoItemFlatNode): void {
-    let parent: TodoItemFlatNode | null = this.getParentNode(node);
-    while (parent) {
-      this.checkRootNodeSelection(parent);
-      parent = this.getParentNode(parent);
-    }
-  }
-
-
-  private checkRootNodeSelection(node: TodoItemFlatNode): void {
-    const nodeSelected = this.checklistSelection.isSelected(node);
-    const descendants = this.treeControl.getDescendants(node);
-    const descAllSelected = descendants.every(child =>
-      this.checklistSelection.isSelected(child)
-    );
-    if (nodeSelected && !descAllSelected) {
-      this.checklistSelection.deselect(node);
-    } else if (!nodeSelected && descAllSelected) {
-      this.checklistSelection.select(node);
-    }
-  }
-
   private getParentNode(node: TodoItemFlatNode): TodoItemFlatNode | null {
     const currentLevel = this.getLevel(node);
     if (currentLevel < 1) {
@@ -197,15 +178,67 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
     return null;
   }
 
-  private addNewItem(node: TodoItemFlatNode, file, isFile, path) {
-    const parentNode = this.flatNodeMap.get(node);
-    this.bucketDataService.insertItem(parentNode!, file, isFile);
-    this.treeControl.expand(node);
+
+private addNewItem(node: TodoItemFlatNode, file, isFile) {
+  const currNode = this.flatNodeMap.get(node);
+  if (!currNode.object) {
+    currNode.object = {bucket: currNode.item, object: ''};
+  }
+  const emptyFolderObject = currNode.object;
+  if (emptyFolderObject.object.lastIndexOf('ا') !== emptyFolderObject.object.length - 1 || emptyFolderObject.object === '') {
+    emptyFolderObject.object += 'ا';
+  }
+  this.bucketDataService.insertItem(currNode!, file, isFile, emptyFolderObject);
+  this.treeControl.expand(node);
+  setTimeout(() => {
+    const element = document.querySelector('#folder-form');
+    element && element.scrollIntoView({ block: 'end', behavior: 'smooth' });
+  }, 0);
+  }
+
+  private removeItem(node: TodoItemFlatNode) {
+    const parentNode = this.flatNodeMap.get(this.getParentNode(node));
+    const childNode = this.flatNodeMap.get(node);
+    this.bucketDataService.emptyFolder = null;
+    this.bucketDataService.removeItem(parentNode!, childNode);
+    this.resetForm();
   }
 
   private saveNode(node: TodoItemFlatNode, itemValue: string) {
-    const nestedNode = this.flatNodeMap.get(node);
-    this.bucketDataService.updateItem(nestedNode!, itemValue);
+    this.folderCreating = true;
+    const parent = this.getParentNode(node);
+    const flatParent = this.flatNodeMap.get(parent);
+    let flatObject = flatParent.object.object;
+    if (flatObject.indexOf('ا') === flatObject.length - 1) {
+      flatObject = flatObject.substring(0, flatParent.object.object.length - 1);
+    }
+    const path = `${ flatParent.object && flatObject !== '/' ? flatObject : ''}${itemValue}/`;
+    const bucket = flatParent.object ? flatParent.object.bucket : flatParent.item;
+    const formData = new FormData();
+    formData.append('file', '');
+    formData.append('object', path);
+    formData.append('bucket', bucket);
+    formData.append('endpoint', this.endpoint);
+    this.bucketDataService.emptyFolder = null;
+    this.bucketBrowserService.uploadFile(formData)
+      .subscribe((event) => {
+          if (event instanceof HttpResponse) {
+            this.bucketDataService.insertItem(flatParent, itemValue, false);
+            // this.bucketDataService.refreshBucketdata(bucket, this.endpoint);
+            this.toastr.success('Folder successfully created!', 'Success!');
+            this.folderCreating = false;
+            this.removeItem(node);
+          }
+        }, error => {
+          this.folderCreating = false;
+          this.toastr.error(error.message || 'Folder creation error!', 'Oops!');
+        });
+  }
 
+  private resetForm() {
+    this.folderFormControl.setValue('');
+    this.folderFormControl.updateValueAndValidity();
+    this.folderFormControl.markAsPristine();
+    this.disableAll.emit(false);
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html
index 04ea086..44718bd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html
@@ -100,7 +100,7 @@
           <div class="m-top-10">
             <p *ngFor="let item of resource.computational_url" class="ellipsis flex">
               <span class="strong">{{ item.description }}:</span>&nbsp;
-              <a href="{{item.url}}" target="_blank" matTooltip="{{item.url}}"
+              <a (click)="logAction(resource.computational_name, item.description)" href="{{item.url}}" target="_blank" matTooltip="{{item.url}}"
                 matTooltipPosition="above">{{ item.url }}</a>
             </p>
           </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts
index 11002e1..415cfaa 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts
@@ -26,6 +26,7 @@ import { DateUtils, CheckUtils } from '../../../core/util';
 import { DataengineConfigurationService } from '../../../core/services';
 import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 import { CLUSTER_CONFIGURATION } from '../computational-resource-create-dialog/cluster-configuration-templates';
+import {AuditService} from '../../../core/services/audit.service';
 
 @Component({
   selector: 'dlab-cluster-details',
@@ -52,13 +53,12 @@ export class DetailComputationalResourcesComponent implements OnInit {
     public toastr: ToastrService,
     public dialogRef: MatDialogRef<DetailComputationalResourcesComponent>,
     private dataengineConfigurationService: DataengineConfigurationService,
-    private _fb: FormBuilder
+    private _fb: FormBuilder,
+    private auditService: AuditService
   ) { }
 
   ngOnInit() {
     this.open(this.data.environment, this.data.resource);
-
-    console.log(this.PROVIDER);
   }
 
   public open(environment, resource): void {
@@ -117,4 +117,9 @@ export class DetailComputationalResourcesComponent implements OnInit {
         ? (control.value && control.value !== null && CheckUtils.isJSON(control.value) ? null : { valid: false })
         : null;
   }
+
+  private logAction(name: any, description: string) {
+    this.auditService.sendDataToAudit({resource_name: name, info: ['User opened link' + description]}).subscribe();
+    console.log(`${name}: ${description}`);
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html
index 1331f5f..17fe71a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html
@@ -50,8 +50,8 @@
                 </div>
                 <div class="service">{{ item.product }}</div>
 <!--                <div class="resource-type" >{{ item.resourse_type }}</div>-->
-                <div class="usage-date-start">{{ item.from | date }}</div>
-                <div class="usage-date-end">{{ item.to | date }}</div>
+                <div class="usage-date-start">{{ item.from.join('/') | date }}</div>
+                <div class="usage-date-end">{{ item.to.join('/') | date }}</div>
                 <div class="cost-currency">{{ item.cost }} {{ item.currency }}</div>
               </mat-list-item>
             </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
index cd39c46..2095926 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
@@ -22,10 +22,10 @@
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </header>
   <div class="dialog-content">
-    <div *ngIf="data">
+    <div *ngIf="data ">
       <table class="detail-header">
         <tr>
-          <td>{{notebook.template_name}}</td>
+          <td>{{notebook.template_name || notebook.name}}</td>
           <td>
             <span class="status" ngClass="{{notebook.status || ''}}">
               {{notebook.status}}
@@ -34,21 +34,33 @@
           <td>{{notebook.shape}}</td>
         </tr>
       </table>
+
       <div class="content-box">
         <div class="detail-info" *ngIf="notebook.error_message">
           <p class="failed">{{ notebook.error_message }}</p>
         </div>
-
-        <div class="scroll-box" id="scrolling">
+        <div *ngIf="data.type === 'environment'" class="detail-info">
+          <p>Open following URL(s) in your browser to access this box:</p>
+          <div class="links_block">
+            <p *ngFor="let item of notebook.exploratory_urls">
+              <span class="description">{{item.description}}: &nbsp;</span>
+              <a class="ellipsis" matTooltip="{{item.url}}" matTooltipPosition="above" href="{{item.url}}"
+                 target="_blank" (click)="logAction(notebook.name, item.description)">
+                &nbsp;{{item.url}}{{notebook.name}}
+              </a>
+            </p>
+          </div>
+        </div>
+        <div class="scroll-box" id="scrolling" *ngIf="data.type === 'resource'">
           <div class="detail-info" *ngIf="!notebook.error_message">
             <p>Edge Node IP Address {{notebook.node_ip}}</p>
             <p *ngIf="notebook.status === 'running'">Up time {{upTimeInHours}} hour(s) since
               {{upTimeSince || "not specified."}}</p>
-            <p>Open following URL(s) in your browser to access this box:</p>
+            <p *ngIf="notebook.url?.length">Open following URL(s) in your browser to access this box:</p>
             <div class="links_block">
               <p *ngFor="let item of notebook.url">
                 <span class="description">{{item.description}}: &nbsp;</span>
-                <a class="ellipsis" matTooltip="{{item.url}}" matTooltipPosition="above" href="{{item.url}}"
+                <a  (click)="logAction(notebook.name, item.description)" class="ellipsis" matTooltip="{{item.url}}" matTooltipPosition="above" href="{{item.url}}"
                   target="_blank">
                   &nbsp;{{item.url}}
                 </a>
@@ -59,42 +71,105 @@
             <p class="flex" *ngIf="notebook.password">Password: &nbsp;<span
                 class="strong">{{ notebook.password }}</span></p>
 
-            <p class="m-top-30">{{ DICTIONARY[PROVIDER].personal_storage }}: &nbsp;</p>
-            <div class="links_block"
-                 (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint, this.bucketStatus.view)"
-                 [matTooltip]="'You have not permission to open project bucket'"
-                 matTooltipDisabled="{{this.bucketStatus.view}}"
-                 matTooltipPosition="above"
-                 [ngClass]="{'not-allow': !this.bucketStatus.view}"
-            >
-              <p *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'azure' && notebook.account_name">{{ DICTIONARY[PROVIDER].account }}
-                <span class="bucket-info" [ngClass]="{'not-allow': !this.bucketStatus.view}">{{ notebook.account_name}}</span></p>
-              <p *ngIf="notebook.bucket_name">{{ DICTIONARY[PROVIDER].container }} <span
-                  class="bucket-info" [ngClass]="{'not-allow': !this.bucketStatus.view}">{{ notebook.bucket_name }}</span></p>
-            </div>
-            <p>Shared endpoint bucket: &nbsp;</p>
-            <div class="links_block" (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint, this.bucketStatus.view)"
-                 [matTooltip]="'You have not permission to open shared endpoint bucket'"
-                 matTooltipDisabled="{{this.bucketStatus.view}}"
-                 matTooltipPosition="above"
-                 [ngClass]="{'not-allow': !this.bucketStatus.view}"
-            >
-              <p *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'azure' && notebook.shared_account_name">{{ DICTIONARY[PROVIDER].account }}
-                <span class="bucket-info" [ngClass]="{'not-allow': !this.bucketStatus.view}">{{ notebook.shared_account_name}}</span></p>
-              <p *ngIf="notebook.shared_bucket_name">{{ DICTIONARY[PROVIDER].container }}
-                <span class="bucket-info" [ngClass]="{'not-allow': !this.bucketStatus.view}">{{ notebook.shared_bucket_name }}</span></p>
-            </div>
-            <br />
+            <p class="m-top-30">{{ 'Project bucket' }}: &nbsp;</p>
+            <!--                 (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint, this.bucketStatus.view)"-->
+            <div class="links_block" (mouseleave)="hideCopyIcon()">
+                <p *ngIf="PROVIDER === 'azure' && notebook.account_name">
+                  <span
+                    class="bucket-info"
+                    (mouseover)="showCopyIcon()"
+                    [matTooltip]="notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net'"
+                    matTooltipPosition="above"
+                    [matTooltipClass]="'bucket-item-tooltip'"
+                  >
+                    {{notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net'}}
+<!--                    rc-22-projecta-conteiner@1ebobsvx7t.blob.core.windows.net-->
+                  </span>
 
-            <div *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'azure' && notebook.datalake_name">
-              <p>Data Lake Store: &nbsp;</p>
-              <div class="links_block">
-                <p>Data Lake Store Account: &nbsp;<span class="bucket-info">{{ notebook.datalake_name }}</span></p>
-                <p>Personal folder: &nbsp;<span class="bucket-info">{{ notebook.datalake_directory }}</span></p>
-                <p>Collaboration folder: &nbsp;<span class="bucket-info">{{ notebook.datalake_shared_directory }}</span>
+                  <span  *ngIf="isCopyIconVissible" [matTooltip]="isCopied ? 'Copy bucket name' : 'Copied'" matTooltipPosition="above">
+                    <span  class="link-icon" (click)="copyBucketName(notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net');$event.stopPropagation()" >
+                    <span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
+                  </span>
+                  </span>
+                </p>
+                <p *ngIf="notebook.bucket_name && PROVIDER !== 'azure'">{{ DICTIONARY[PROVIDER].container }}
+                  <span
+                    class="bucket-info"
+                    (mouseover)="showCopyIcon()"
+                  >
+                    {{ notebook.bucket_name }}
+                </span>
+                  <span  *ngIf="isCopyIconVissible" [matTooltip]="isCopied ? 'Copy bucket name' : 'Copied'" matTooltipPosition="above">
+                    <span  class="link-icon" (click)="copyBucketName(notebook.bucket_name);$event.stopPropagation()" >
+                    <span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
+                  </span>
+                  </span>
                 </p>
-              </div>
             </div>
+            <div class="bucket-info bucket-link">
+              <span></span>
+<!--              <button-->
+<!--                type="button"-->
+<!--                class="butt"-->
+<!--                mat-raised-button-->
+<!--              >-->
+<!--                Open bucket browser-->
+<!--              </button>-->
+<!--              <span class="description open-bucket"-->
+<!--                 [ngClass]="{'not-allow': !this.bucketStatus.view || !thisdata.buckets.length}"-->
+<!--                (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint, this.bucketStatus.view && thisdata.buckets.length)"-->
+<!--              >-->
+                <span class="description open-bucket"
+                      [matTooltip]="!this.bucketStatus.view
+                 ? 'You have not permission to open bucket'
+                 : 'You have not any bucket'"
+                      matTooltipDisabled="{{this.bucketStatus.view && this.data.buckets.length}}"
+                      matTooltipPosition="above"
+                      [matTooltipClass]="'bucket-item-tooltip'"
+                      [ngClass]="{'not-allow': !this.bucketStatus.view || !this.data.buckets.length}"
+                      (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint, this.bucketStatus.view && this.data.buckets.length)"
+                >
+                  Open bucket browser
+                </span>
+            </div>
+<!--            <p>Shared endpoint bucket: &nbsp;</p>-->
+<!--            <div class="links_block" (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint, this.bucketStatus.view)"-->
+<!--                 [matTooltip]="'You have not permission to open bucket'"-->
+<!--                 matTooltipDisabled="{{this.bucketStatus.view}}"-->
+<!--                 matTooltipPosition="above"-->
+<!--                 [matTooltipClass]="'bucket-item-tooltip'"-->
+<!--            >-->
+<!--              <p *ngIf="DICTIONARY[PROVIDER === 'azure' && notebook.shared_account_name">{{ DICTIONARY[PROVIDER].account }}-->
+<!--                <span class="bucket-info bucket-link" [ngClass]="{'not-allow': !this.bucketStatus.view}" (mouseover)="showCopyIcon('shared')">{{ notebook.shared_account_name}}</span>-->
+<!--                <span *ngIf="isCopyIconVissible.shared" class="link-icon" (click)="copyBucketName(notebook.shared_account_name)">-->
+<!--                  <span _ngcontent-xpv-c19="" class="material-icons" matTooltip="Copy bucket name" matTooltipPosition="above">content_copy</span>-->
+<!--                </span>-->
+<!--              </p>-->
+<!--              <p *ngIf="notebook.shared_bucket_name">{{ DICTIONARY[PROVIDER].container }}-->
+<!--                <span-->
+<!--                  class="bucket-info bucket-link"-->
+<!--                  [ngClass]="{'not-allow': !this.bucketStatus.view}"-->
+<!--                  (mouseover)="showCopyIcon('shared')"-->
+<!--                  (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint, this.bucketStatus.view)"-->
+<!--                >-->
+<!--                  {{ notebook.shared_bucket_name }}-->
+<!--                </span>-->
+<!--                <span *ngIf="isCopyIconVissible.shared" class="link-icon" (click)="copyBucketName(notebook.shared_bucket_name)">-->
+<!--                  <span _ngcontent-xpv-c19="" class="material-icons" matTooltip="Copy bucket name" matTooltipPosition="above">content_copy</span>-->
+<!--                </span>-->
+<!--              </p>-->
+<!--            </div>-->
+<!--            <br />-->
+
+<!--            <div *ngIf="DICTIONARY[PROVIDER === 'azure' && notebook.datalake_name">-->
+<!--              <p>Data Lake Store: &nbsp;</p>-->
+<!--              <div class="links_block">-->
+<!--                <p>Data Lake Store Account: &nbsp;<span class="bucket-info">{{ notebook.datalake_name }}</span></p>-->
+<!--                <p>Personal folder: &nbsp;<span class="bucket-info">{{ notebook.datalake_directory }}</span></p>-->
+<!--                <p>Collaboration folder: &nbsp;<span class="bucket-info">{{ notebook.datalake_shared_directory }}</span>-->
+<!--                </p>-->
+<!--              </div>-->
+<!--            </div>-->
 
             <!-- <p>
               <a href="#/help/accessnotebookguide" target="_blank">
@@ -140,5 +215,6 @@
         </div>
       </div>
     </div>
+
   </div>
 </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
index 24d273f..c6722ef 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
@@ -73,7 +73,6 @@
   }
 
   span {
-
     .danger_color {
       position: absolute;
       bottom: -16px;
@@ -84,11 +83,23 @@
 
 .bucket-info {
   padding-left: 7px;
+  margin-right: 10px;
   font-weight: 600;
   color: $blue-grey-color;
-  cursor: pointer;
 }
 
-.not-allow{
-  cursor: not-allowed;
+.bucket-link{
+  padding: 15px;
+  padding-left: 0;
+  color: #35afd5;
+  .open-bucket{
+    cursor: pointer;
+    font-size: 14px;
+  }
+  &.not-allow{
+    cursor: not-allowed;
+    color: $blue-grey-color !important;
+  }
 }
+
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
index ed359c8..b2788f5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
@@ -27,6 +27,8 @@ import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 import { DataengineConfigurationService } from '../../../core/services';
 import { CLUSTER_CONFIGURATION } from '../../computational/computational-resource-create-dialog/cluster-configuration-templates';
 import {BucketBrowserComponent} from '../../bucket-browser/bucket-browser.component';
+import {CopyPathUtils} from '../../../core/util/copyPathUtils';
+import {AuditService} from '../../../core/services/audit.service';
 
 @Component({
   selector: 'detail-dialog',
@@ -36,18 +38,22 @@ import {BucketBrowserComponent} from '../../bucket-browser/bucket-browser.compon
 
 export class DetailDialogComponent implements OnInit {
   readonly DICTIONARY = DICTIONARY;
-  readonly PROVIDER = this.data.notebook.cloud_provider;
+  readonly PROVIDER = this.data.notebook.cloud_provider.toLowerCase();
+  private isCopied: boolean = true;
   notebook: any;
   upTimeInHours: number;
   upTimeSince: string = '';
   tooltip: boolean = false;
   config: Array<{}> = [];
   bucketStatus: object = {};
+  isBucketAllowed = true;
+  isCopyIconVissible = false;
 
   public configurationForm: FormGroup;
 
   @ViewChild('configurationNode', { static: false }) configuration;
 
+
   constructor(
     @Inject(MAT_DIALOG_DATA) public data: any,
     private dataengineConfigurationService: DataengineConfigurationService,
@@ -55,6 +61,7 @@ export class DetailDialogComponent implements OnInit {
     public dialogRef: MatDialogRef<DetailDialogComponent>,
     private dialog: MatDialog,
     public toastr: ToastrService,
+    public auditService: AuditService
   ) {
 
   }
@@ -69,6 +76,11 @@ export class DetailDialogComponent implements OnInit {
       this.upTimeSince = (this.notebook.time) ? new Date(this.notebook.time).toString() : '';
       this.initFormModel();
       this.getClusterConfiguration();
+    if (this.notebook.edgeNodeStatus === 'terminated' ||
+      this.notebook.edgeNodeStatus === 'terminating' ||
+      this.notebook.edgeNodeStatus === 'failed') {
+      this.isBucketAllowed = false;
+    }
     }
   }
 
@@ -125,9 +137,31 @@ export class DetailDialogComponent implements OnInit {
   }
 
   public bucketBrowser(bucketName, endpoint, permition): void {
-    permition && this.dialog.open(BucketBrowserComponent, { data:
-        {bucket: bucketName, endpoint: endpoint, bucketStatus: this.bucketStatus},
+    if (!permition) {
+      return;
+    }
+    bucketName = this.isBucketAllowed ? this.notebook.bucket_name : this.data.buckets[0].children[0].name;
+    // bucketName = 'ofuks-1304-pr2-local-bucket';
+    this.dialog.open(BucketBrowserComponent, { data:
+        {bucket: bucketName, endpoint: endpoint, bucketStatus: this.bucketStatus, buckets: this.data.buckets},
       panelClass: 'modal-fullscreen' })
     .afterClosed().subscribe();
   }
+
+  protected showCopyIcon() {
+    this.isCopyIconVissible = true;
+  }
+  protected hideCopyIcon() {
+    this.isCopyIconVissible = false;
+    this.isCopied = true;
+  }
+
+  protected copyBucketName(copyValue) {
+    CopyPathUtils.copyPath(copyValue);
+  }
+
+  private logAction(name: any, description: string) {
+    this.auditService.sendDataToAudit({resource_name: name, info: ['User opened link' + description]}).subscribe();
+    console.log(`${name}: ${description}`);
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts
index 2f2d733..0445b4c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts
@@ -255,6 +255,7 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy {
             lib.version = 'v.' +  lib.version;
           }
         );
+        this.filterLibs();
         this.changeDetector.markForCheck();
         this.filterConfiguration.group = this.createFilterList(this.notebookLibs.map(v => this.groupsListMap[v.group]));
         this.filterConfiguration.resource = this.createFilterList(this.notebookLibs.map(lib => lib.status.map(status => status.resource)));
@@ -363,12 +364,17 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy {
     <h4 class="modal-title">Library installation error</h4>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </div>
-  <div class="content">{{ data }}</div>
+  <div class="content lib-error" >
+    {{ data }}
+  </div>
   <div class="text-center">
     <button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">Close</button>
   </div>
   `,
-  styles: []
+  styles: [    `
+      .lib-error { max-height: 200px; overflow-x: auto; word-break: break-all; padding: 20px 30px !important; margin: 20px 0;}
+  `
+  ]
 })
 export class ErrorMessageDialogComponent {
   constructor(
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
index 738059a..70cf223 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
@@ -23,7 +23,7 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
 import { ToastrService } from 'ngx-toastr';
 import { MatDialog } from '@angular/material/dialog';
 
-import { UserResourceService } from '../../core/services';
+import {ProjectService, UserResourceService} from '../../core/services';
 
 import { ExploratoryModel, Exploratory } from './resources-grid.model';
 import { FilterConfigurationModel } from './filter-configuration.model';
@@ -89,6 +89,9 @@ export class ResourcesGridComponent implements OnInit {
 
   public displayedColumns: string[] = this.filteringColumns.map(item => item.name);
   public displayedFilterColumns: string[] = this.filteringColumns.map(item => item.filter_class);
+  public bucketsList;
+  public activeProjectsList: any;
+
 
 
   constructor(
@@ -96,10 +99,18 @@ export class ResourcesGridComponent implements OnInit {
     private userResourceService: UserResourceService,
     private dialog: MatDialog,
     private progressBarService: ProgressBarService,
+    private projectService: ProjectService,
   ) { }
 
   ngOnInit(): void {
     this.buildGrid();
+    this.getUserProjects();
+  }
+
+  public getUserProjects() {
+    this.projectService.getUserProjectsList(true).subscribe((projects: any) => {
+      this.activeProjectsList = projects;
+    });
   }
 
   public buildGrid(): void {
@@ -107,6 +118,7 @@ export class ResourcesGridComponent implements OnInit {
     this.userResourceService.getUserProvisionedResources()
       .subscribe((result: any) => {
         this.environments = ExploratoryModel.loadEnvironments(result);
+        this.getBuckets();
         this.getDefaultFilterConfiguration();
         (this.environments.length) ? this.getUserPreferences() : this.filteredEnvironments = [];
         this.healthStatus && !this.healthStatus.billingEnabled && this.modifyGrid();
@@ -169,7 +181,7 @@ export class ResourcesGridComponent implements OnInit {
 
   public printDetailEnvironmentModal(data): void {
     this.dialog.open(DetailDialogComponent, { data:
-        {notebook: data, bucketStatus: this.healthStatus.bucketBrowser},
+        {notebook: data, bucketStatus: this.healthStatus.bucketBrowser, buckets: this.bucketsList, type: 'resource'},
       panelClass: 'modal-lg'
     })
       .afterClosed().subscribe(() => this.buildGrid());
@@ -330,6 +342,30 @@ export class ResourcesGridComponent implements OnInit {
       if (filterConfig[index].length) this.activeFiltering = true;
   }
 
+  public getBuckets() {
+    const bucketsList = this.environments.map(project => {
+      return Object.keys(project.projectEndpoints).map(key => {
+        if (project.endpoints.length === 0) {
+          return;
+        }
+        const currEndpoint = project.projectEndpoints[key];
+        const provider: string =  project.endpoints.filter(endpoint => endpoint['name'] === key)[0]['cloudProvider'];
+        const edgeItem = {name: `${project.project} (${key})`, children: []};
+        const projectBucket = currEndpoint[this.DICTIONARY[provider.toLowerCase()].bucket_name];
+        const sharedBucket = currEndpoint[this.DICTIONARY[provider.toLowerCase()].shared_bucket_name];
+        if (projectBucket && currEndpoint.status !== 'terminated' && currEndpoint.status !== 'terminating' && currEndpoint.status !== 'failed') {
+          edgeItem.children.push({name: projectBucket, endpoint: key});
+        }
+        if (sharedBucket) {
+          edgeItem.children.push({name: sharedBucket, endpoint: key});
+        }
+        return edgeItem;
+      }).filter(v => v);
+    });
+
+    this.bucketsList = SortUtils.flatDeep(bucketsList, 1).filter(v => v.children.length);
+  }
+
   private getUserPreferences(): void {
     this.userResourceService.getUserPreferences()
       .subscribe((result: FilterConfigurationModel) => {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
index e769dbe..c4ad5800 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
@@ -58,6 +58,8 @@ export class ExploratoryModel {
       return data.map((value) => {
         return {
           project: value.project,
+          projectEndpoints: value.shared,
+          endpoints: value.endpoints,
           exploratory: value.exploratory.map(el => {
             const provider = el.cloud_provider.toLowerCase();
             const billing = value.exploratoryBilling.filter(res => res.name === el.exploratory_name)[0];
@@ -101,5 +103,7 @@ export class ExploratoryModel {
 
 export interface Exploratory {
   project: string;
+  endpoints: [];
+  projectEndpoints: [];
   exploratory: ExploratoryModel[];
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
index 545412d..d47559f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
@@ -20,11 +20,18 @@
 <div class="base-retreat">
   <div class="sub-nav">
     <div class="selection">
+      <span
+        matTooltip="{{!healthStatus?.projectAssigned ? 'You are not assigned to any project' : 'You have not any active project'}}"
+        matTooltipPosition="above"
+        [matTooltipClass]="'bucket-item-tooltip'"
+        [matTooltipDisabled]="healthStatus?.projectAssigned && resourcesGrid.activeProjectsList?.length !== 0"
+      >
+        <span>{{resourcesGrid.activeProject}}</span>
       <button mat-raised-button class="butt butt-create" (click)="createEnvironment()"
-        [disabled]="!healthStatus?.projectAssigned">
+        [disabled]="!healthStatus?.projectAssigned || !resourcesGrid.activeProjectsList?.length">
         <i class="material-icons">add</i>Create new
       </button>
-
+      </span>
       <div class="mat-reset">
         <div class="control selector-wrapper" *ngIf="projects.length">
           <mat-form-field>
@@ -46,9 +53,16 @@
     </div>
 
     <div>
-<!--      <button mat-raised-button class="butt butt-tool" (click)="bucketBrowser()">-->
-<!--        <i class="material-icons"></i>Bucket browser-->
-<!--      </button>-->
+      <span  matTooltip="{{!this.bucketStatus?.view ? 'You have not permission to open bucket browser' : 'You have not any bucket'}}"
+             matTooltipPosition="above"
+             matTooltipDisabled="{{resourcesGrid.bucketsList?.length > 0 && this.bucketStatus?.view}}"
+             [matTooltipClass]="'bucket-item-tooltip'"
+      >
+        <button mat-raised-button class="butt butt-tool" (click)="bucketBrowser(this.bucketStatus?.view)"
+                [disabled]="!this.bucketStatus?.view || resourcesGrid.bucketsList?.length === 0">
+        <i class="material-icons"></i>Bucket browser
+      </button>
+      </span>
       <button mat-raised-button class="butt butt-tool" (click)="manageUngit()">
         <i class="material-icons"></i>Git credentials
       </button>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
index 7933fec..301509d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
@@ -42,6 +42,7 @@ export class ResourcesComponent implements OnInit {
 
   @ViewChild(ResourcesGridComponent, { static: true }) resourcesGrid: ResourcesGridComponent;
 
+  public bucketStatus;
   constructor(
     public toastr: ToastrService,
     private healthStatusService: HealthStatusService,
@@ -65,6 +66,8 @@ export class ResourcesComponent implements OnInit {
     this.exploratoryEnvironments = this.resourcesGrid.environments;
   }
 
+
+
   public toggleFiltering(): void {
     if (this.resourcesGrid.activeFiltering) {
       this.resourcesGrid.resetFilterConfigurations();
@@ -78,9 +81,17 @@ export class ResourcesComponent implements OnInit {
       .afterClosed().subscribe(() => this.refreshGrid());
   }
 
-  public bucketBrowser(): void {
-    this.dialog.open(BucketBrowserComponent, { panelClass: 'modal-fullscreen' })
-      .afterClosed().subscribe(() => this.refreshGrid());
+  public bucketBrowser(permition): void {
+    const defaultBucket = this.resourcesGrid.bucketsList[0].children[0];
+      permition && this.dialog.open(BucketBrowserComponent, { data:
+        {
+          bucket: defaultBucket.name,
+          endpoint: defaultBucket.endpoint,
+          bucketStatus: this.bucketStatus,
+          buckets: this.resourcesGrid.bucketsList
+        },
+      panelClass: 'modal-fullscreen' })
+      .afterClosed().subscribe();
   }
 
   public setActiveProject(project): void {
@@ -106,6 +117,7 @@ export class ResourcesComponent implements OnInit {
       (result: any) => {
         this.healthStatus = result;
         this.resourcesGrid.healthStatus = this.healthStatus;
+        this.bucketStatus = this.healthStatus.bucketBrowser;
       },
       error => this.toastr.error(error.message, 'Oops!'));
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
index c87d12c..04a412e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
@@ -27,30 +27,30 @@ import { ResourcesGridModule } from './resources-grid';
 import { ExploratoryEnvironmentCreateModule } from './exploratory/create-environment';
 import { ManageUngitComponent } from './manage-ungit/manage-ungit.component';
 import { ConfirmDeleteAccountDialog } from './manage-ungit/manage-ungit.component';
-import {BucketBrowserComponent} from './bucket-browser/bucket-browser.component';
-import {FolderTreeComponent} from './bucket-browser/folder-tree/folder-tree.component';
 import {MatTreeModule} from '@angular/material/tree';
 import {BucketDataService} from './bucket-browser/bucket-data.service';
-
+import {ConvertFileSizePipeModule} from '../core/pipes/convert-file-size';
+import {BucketBrowserModule} from './bucket-browser/bucket-browser.module';
 
 @NgModule({
-  imports: [
-    CommonModule,
-    FormsModule,
-    ReactiveFormsModule,
-    ResourcesGridModule,
-    ExploratoryEnvironmentCreateModule,
-    MaterialModule,
-    MatTreeModule
-  ],
+    imports: [
+        CommonModule,
+        FormsModule,
+        ReactiveFormsModule,
+        ResourcesGridModule,
+        ExploratoryEnvironmentCreateModule,
+        MaterialModule,
+        MatTreeModule,
+        ConvertFileSizePipeModule,
+        BucketBrowserModule
+    ],
   declarations: [
     ResourcesComponent,
     ManageUngitComponent,
     ConfirmDeleteAccountDialog,
-    BucketBrowserComponent,
-    FolderTreeComponent
+
   ],
-  entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialog, BucketBrowserComponent, FolderTreeComponent],
+  entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialog],
   providers: [BucketDataService],
   exports: [ResourcesComponent]
 })
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/form-controls/multi-level-select-dropdown/multi-level-select-dropdown.component.ts b/services/self-service/src/main/resources/webapp/src/app/shared/form-controls/multi-level-select-dropdown/multi-level-select-dropdown.component.ts
index e4cca34..3033acc 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/form-controls/multi-level-select-dropdown/multi-level-select-dropdown.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/form-controls/multi-level-select-dropdown/multi-level-select-dropdown.component.ts
@@ -44,7 +44,7 @@ export class MultiLevelSelectDropdownComponent {
     COMPUTATIONAL_SHAPE: 'Compute shapes',
     NOTEBOOK_SHAPE: 'Notebook shapes',
     COMPUTATIONAL: 'Compute',
-    BUCKET_BROWSER: 'Bucket browser'
+    BUCKET_BROWSER: 'Bucket browser actions'
   };
 
   constructor() {
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/material.module.ts b/services/self-service/src/main/resources/webapp/src/app/shared/material.module.ts
index c677b05..0eb5554 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/material.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/material.module.ts
@@ -19,7 +19,6 @@
 
 import { NgModule } from '@angular/core';
 import { CdkTableModule } from '@angular/cdk/table';
-
 import { MatAutocompleteModule } from '@angular/material/autocomplete';
 import { MatButtonModule } from '@angular/material/button';
 import { MatButtonToggleModule } from '@angular/material/button-toggle';
@@ -51,7 +50,7 @@ import { MatTableModule } from '@angular/material/table';
 import { MatTabsModule } from '@angular/material/tabs';
 import { MatToolbarModule } from '@angular/material/toolbar';
 import { MatTooltipModule } from '@angular/material/tooltip';
-import {STEPPER_GLOBAL_OPTIONS} from "@angular/cdk/stepper";
+import {STEPPER_GLOBAL_OPTIONS} from '@angular/cdk/stepper';
 
 @NgModule({
   exports: [
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
index f2d715f..8f6a8ca 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
@@ -95,29 +95,37 @@
               <ng-template #env><i class="material-icons">settings</i></ng-template>
             </a>
           </a>
-          <a *ngIf="healthStatus?.billingEnabled" class="nav-item" [routerLink]="['/billing_report']"
-            [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact:true}">
-            <span *ngIf="isExpanded; else billing">Billing Report</span>
-            <ng-template #billing><i class="material-icons">account_balance_wallet</i></ng-template>
-          </a>
-        </div>
-        <div>
-          <a class="nav-item" [routerLink]="['/swagger']" [routerLinkActive]="['active']"
-            [routerLinkActiveOptions]="{exact:true}">
-            <span *ngIf="isExpanded; else endpoint">Cloud Endpoint API</span>
-            <ng-template #endpoint>
-              <span>
-                <svg width="30px" height="27px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
-                  <g>
-                    <path d="M127.059657,255.996921 C58.8506544,255.526472 -0.457073619,198.918442 0.00265506057,126.998303 C0.444649399,57.7958628 57.9516598,-0.468967577 129.11002,0.00284555012 C198.267128,0.462386081 256.613109,57.8667711 255.995136,128.194199 C256.568091,197.883453 197.934268,256.489189 127.059657,255.996921 Z M127.059657,255.996921 C58.8506544,255.526472 -0.457073619,198.918442 0.00265506057,126.998303 C0.444649399,57.7958628 57.9516598,-0.468967577 129.11002,0.0028 [...]
-                    <path id="swager-bgr" d="M127.184644,238.997327 C68.0323765,238.589271 16.6036091,189.498744 17.0023028,127.131428 C17.3860285,67.1185953 67.2554,16.5917106 128.963117,17.0024872 C188.934544,17.4010221 239.531905,67.1825241 238.995778,128.169251 C239.492444,188.602381 188.64743,239.424426 127.184644,238.997327 Z M127.184644,238.997327 C68.0323765,238.589271 16.6036091,189.498744 17.0023028,127.131428 C17.3860285,67.1185953 67.2554,16.5917106 128.963117,17.0024872 C188 [...]
-                    <path d="M169.327319,127.956161 C169.042723,133.246373 164.421106,137.639224 159.866213,136.872586 C159.844426,136.872586 159.821277,136.872586 159.798128,136.872586 C154.753021,136.879395 150.658383,132.794288 150.652936,127.749182 C150.824511,122.690458 155.019915,118.703395 160.08,118.789182 C165.125106,118.813692 169.59966,123.077182 169.327319,127.956161 Z M88.2011915,179.220161 C90.1034894,179.27599 92.0071489,179.235139 94.2008511,179.235139 L94.2008511,193.021 [...]
-                  </g>
-                </svg>
-              </span>
-            </ng-template>
+          <a class="nav-item has-children" *ngIf="healthStatus?.billingEnabled || true">
+            <span *ngIf="isExpanded">Reports</span>
+            <a *ngIf="healthStatus?.billingEnabled" class="sub-nav-item" [routerLink]="['/billing_report']"
+              [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact:true}" [style.margin-left.px]="isExpanded ? '30' : '0'">
+              <span *ngIf="isExpanded; else billing">Billing</span>
+              <ng-template #billing><i class="material-icons">account_balance_wallet</i></ng-template>
+            </a>
+            <a  class="sub-nav-item" [routerLink]="['/audit']" [style.margin-left.px]="isExpanded ? '30' : '0'"
+               [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact:true}">
+              <span *ngIf="isExpanded; else audit">Audit</span>
+              <ng-template #audit><i class="material-icons">library_books</i></ng-template>
+            </a>
           </a>
         </div>
+<!--        <div>-->
+<!--          <a class="nav-item" [routerLink]="['/swagger']" [routerLinkActive]="['active']"-->
+<!--            [routerLinkActiveOptions]="{exact:true}">-->
+<!--            <span *ngIf="isExpanded; else endpoint">Cloud Endpoint API</span>-->
+<!--            <ng-template #endpoint>-->
+<!--              <span>-->
+<!--                <svg width="30px" height="27px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">-->
+<!--                  <g>-->
+<!--                    <path d="M127.059657,255.996921 C58.8506544,255.526472 -0.457073619,198.918442 0.00265506057,126.998303 C0.444649399,57.7958628 57.9516598,-0.468967577 129.11002,0.00284555012 C198.267128,0.462386081 256.613109,57.8667711 255.995136,128.194199 C256.568091,197.883453 197.934268,256.489189 127.059657,255.996921 Z M127.059657,255.996921 C58.8506544,255.526472 -0.457073619,198.918442 0.00265506057,126.998303 C0.444649399,57.7958628 57.9516598,-0.468967577 129.11002,0. [...]
+<!--                    <path id="swager-bgr" d="M127.184644,238.997327 C68.0323765,238.589271 16.6036091,189.498744 17.0023028,127.131428 C17.3860285,67.1185953 67.2554,16.5917106 128.963117,17.0024872 C188.934544,17.4010221 239.531905,67.1825241 238.995778,128.169251 C239.492444,188.602381 188.64743,239.424426 127.184644,238.997327 Z M127.184644,238.997327 C68.0323765,238.589271 16.6036091,189.498744 17.0023028,127.131428 C17.3860285,67.1185953 67.2554,16.5917106 128.963117,17.0024872  [...]
+<!--                    <path d="M169.327319,127.956161 C169.042723,133.246373 164.421106,137.639224 159.866213,136.872586 C159.844426,136.872586 159.821277,136.872586 159.798128,136.872586 C154.753021,136.879395 150.658383,132.794288 150.652936,127.749182 C150.824511,122.690458 155.019915,118.703395 160.08,118.789182 C165.125106,118.813692 169.59966,123.077182 169.327319,127.956161 Z M88.2011915,179.220161 C90.1034894,179.27599 92.0071489,179.235139 94.2008511,179.235139 L94.2008511,193 [...]
+<!--                  </g>-->
+<!--                </svg>-->
+<!--              </span>-->
+<!--            </ng-template>-->
+<!--          </a>-->
+<!--        </div>-->
       </nav>
     </mat-nav-list>
   </mat-sidenav>
diff --git a/services/self-service/src/main/resources/webapp/src/assets/img/blank-file-svgrepo-com.svg b/services/self-service/src/main/resources/webapp/src/assets/img/blank-file-svgrepo-com.svg
new file mode 100644
index 0000000..4c88fd9
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/assets/img/blank-file-svgrepo-com.svg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 26 26" style="enable-background:new 0 0 26 26;" xml:space="preserve">
+<g>
+	<path style="fill:lightgray;" d="M20.266,4.207c-0.244-0.24-0.494-0.484-0.74-0.732c-0.248-0.246-0.492-0.496-0.732-0.74
+		C17.082,0.988,16.063,0,15,0H7C4.795,0,3,1.795,3,4v18c0,2.205,1.795,4,4,4h12c2.205,0,4-1.795,4-4V8
+		C23,6.938,22.012,5.918,20.266,4.207z M21,22c0,1.104-0.896,2-2,2H7c-1.104,0-2-0.896-2-2V4c0-1.104,0.896-2,2-2l7.289-0.004
+		C15.01,2.18,15,3.066,15,3.953V7c0,0.551,0.449,1,1,1h3c0.998,0,2,0.005,2,1V22z"/>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
diff --git a/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss b/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss
index c5ac335..ccde6f7 100644
--- a/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss
+++ b/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss
@@ -263,6 +263,7 @@ mat-dialog-container {
   p {
     font-weight: 400;
     margin-bottom: 10px;
+    display: flex;
 
     small {
       font-size: 12px;
@@ -275,6 +276,13 @@ mat-dialog-container {
     overflow: hidden;
     text-overflow: ellipsis;
     display: block;
+    max-width: 450px;
+    white-space: nowrap;
+    &.link-icon{
+      //margin-left: 10px;
+      cursor: pointer;
+      padding-top: 3px;
+    }
   }
 
   .links_block {
@@ -343,6 +351,60 @@ mat-dialog-container {
   overflow: auto;
 }
 
+.bucket-browser{
+  .mat-form-field-appearance-legacy .mat-form-field-subscript-wrapper {
+    width: calc(100% + 77px);
+  }
+}
+
+.bucket-item-tooltip{
+  max-width: 1100px  !important;
+  white-space: pre-line;
+}
+
+.mat-list-base .mat-list-item.delete-item{
+  height: 30px;
+}
+
+.d-none{
+  display: none !important;
+}
+
+.progres{
+  border: 1px solid rgba(0,0,0,.12);
+  height: 17px;
+  position: relative;
+  width: 155px;
+
+  .bar{
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    width: 0;
+    background-color:  #00bcd4;
+  }
+
+  .progress-bar-text{
+    position: absolute;
+    left: 5px;
+    top: 0;
+    bottom: 0;
+    font-size: 11px;
+    line-height: 13px;
+    color: rgba(0, 0, 0, 0.87);
+    z-index: 10;
+  }
+}
+
+.not-allow{
+  cursor: not-allowed !important;
+  &.open-bucket{
+    color: $blue-grey-color !important;
+  }
+}
+
+
 @media screen and (max-width: 1280px) {
   .modal-fullscreen {
     max-width: 100vw !important;
diff --git a/services/self-service/src/main/resources/webapp/src/styles.scss b/services/self-service/src/main/resources/webapp/src/styles.scss
index e1bbe94..bf22293 100644
--- a/services/self-service/src/main/resources/webapp/src/styles.scss
+++ b/services/self-service/src/main/resources/webapp/src/styles.scss
@@ -155,6 +155,10 @@ mat-chip.mat-chip strong {
   pointer-events: none;
 }
 
+.cursor-not-allow{
+  cursor: not-allowed !important;
+}
+
 .not-active {
   cursor: not-allowed !important;
   opacity: .6;
@@ -311,12 +315,20 @@ input[type='number'] {
   margin-top: 30px;
 }
 
+.m-top-40 {
+  margin-top: 40px;
+}
+
 .m-bott-10 {
   margin-bottom: 10px;
 }
 
+.m-bott-20 {
+  margin-bottom: 20px;
+}
+
 .m-bott-30 {
-  margin-bottom: 10px;
+  margin-bottom: 30px;
 }
 
 .m-top-10p {


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