datasafe icon indicating copy to clipboard operation
datasafe copied to clipboard

Example with partial path encryption

Open valb3r opened this issue 5 years ago • 4 comments

This is a complete example of how to create partial path encryption, one starting path segment will be unencrypted. Like this: C:\Projects\Server\Adorsys\users\root-user\private\files\inner-user\SIV\X-ABgFmR0RJuiVsR0S5iUZEgadMamHU Here, when writing to inner-user\file.txt inner-user is unencrypted and the other part of the segment (file.txt) is encrypted.

Code example:

package de.adorsys.datasafe.examples.business.filesystem;

import de.adorsys.datasafe.business.impl.service.DaggerDefaultDatasafeServices;
import de.adorsys.datasafe.business.impl.service.DefaultDatasafeServices;
import de.adorsys.datasafe.directory.impl.profile.config.DefaultDFSConfig;
import de.adorsys.datasafe.encrypiton.api.types.UserIDAuth;
import de.adorsys.datasafe.encrypiton.impl.pathencryption.PathEncryptionImpl;
import de.adorsys.datasafe.encrypiton.impl.pathencryption.PathEncryptionImplRuntimeDelegatable;
import de.adorsys.datasafe.storage.impl.fs.FileSystemStorageService;
import de.adorsys.datasafe.types.api.actions.ListRequest;
import de.adorsys.datasafe.types.api.actions.ReadRequest;
import de.adorsys.datasafe.types.api.actions.WriteRequest;
import de.adorsys.datasafe.types.api.context.BaseOverridesRegistry;
import de.adorsys.datasafe.types.api.context.overrides.OverridesRegistry;
import de.adorsys.datasafe.types.api.resource.PrivateResource;
import de.adorsys.datasafe.types.api.resource.Uri;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Function;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

class RuntimeOverrideOperationsTest {

    @Test
    @SneakyThrows
    void testPathEncryptionOverridden(@TempDir Path root) {
        // BEGIN_SNIPPET:Create overridable Datasafe services without recompilation
        // This shows how to override path encryption service, in particular we are going to disable it
        OverridesRegistry registry = new BaseOverridesRegistry();

        // PathEncryptionImpl now will have completely different functionality
        // instead of calling PathEncryptionImpl methods we will call PathEncryptionImplOverridden methods
        PathEncryptionImplRuntimeDelegatable.overrideWith(registry, PathEncryptionImplOverridden::new);

        // Customized service, without creating complete module and building it:
        DefaultDatasafeServices datasafeServices = DaggerDefaultDatasafeServices.builder()
                .config(new DefaultDFSConfig(root.toAbsolutePath().toUri(), "secret"::toCharArray))
                .storage(new FileSystemStorageService(root))
                .overridesRegistry(registry)
                .build();

        // registering user
        UserIDAuth user = new UserIDAuth("user", "passwrd"::toCharArray);
        datasafeServices.userProfile().registerUsingDefaults(user);
        // writing into user privatespace, note that with default implementation `file.txt` would be encrypted
        OutputStream os = datasafeServices.privateService().write(WriteRequest.forDefaultPrivate(user, "folder/file.txt"));
        os.write("HELLO".getBytes());
        os.close();
        // we can read file by its path
        assertThat(datasafeServices.privateService().read(ReadRequest.forDefaultPrivate(user, "folder/file.txt"))).hasContent("HELLO");
        // we can list file
        assertThat(datasafeServices.privateService().list(ListRequest.forDefaultPrivate(user, "folder/")))
                .extracting(it -> it.getResource().asPrivate().decryptedPath().asString())
                .contains("folder/file.txt");
        // but we see raw folder name here:
        assertThat(Files.walk(root)).asString().contains("folder");
        // but filename is encrypted:
        assertThat(Files.walk(root)).asString().doesNotContain("file.txt");
        // END_SNIPPET
    }

    // Path encryption that does not encrypt paths
    class PathEncryptionImplOverridden extends PathEncryptionImpl {

        PathEncryptionImplOverridden(PathEncryptionImplRuntimeDelegatable.ArgumentsCaptor captor) {
            super(captor.getSymmetricPathEncryptionService(), captor.getPrivateKeyService());
        }

        @Override
        public Uri encrypt(UserIDAuth forUser, Uri path) {
            if (path.asString().contains("/")) {
                String[] rootAndInRoot = path.asString().split("/", 2);
                return new Uri(rootAndInRoot[0] + "/" + super.encrypt(forUser, new Uri(rootAndInRoot[1])).asString());
            }
            // encryption disabled for root folder:
            return path;
        }

        @Override
        public Function<Uri, Uri> decryptor(UserIDAuth forUser) {
            return rootWithEncrypted -> {
                if (rootWithEncrypted.asString().contains("/")) {
                    String[] rootAndInRoot = rootWithEncrypted.asString().split("/", 2);
                    return new Uri(rootAndInRoot[0] + "/" + super.decryptor(forUser).apply(new Uri(rootAndInRoot[1])).asString());
                }
                // encryption disabled for root folder:
                return rootWithEncrypted;
            };
        }
    }
}

valb3r avatar May 07 '20 16:05 valb3r

Example for moved file:

package de.adorsys.datasafe.examples.business.filesystem;

import de.adorsys.datasafe.business.impl.service.DaggerDefaultDatasafeServices;
import de.adorsys.datasafe.business.impl.service.DefaultDatasafeServices;
import de.adorsys.datasafe.directory.impl.profile.config.DefaultDFSConfig;
import de.adorsys.datasafe.encrypiton.api.types.UserIDAuth;
import de.adorsys.datasafe.encrypiton.impl.pathencryption.PathEncryptionImpl;
import de.adorsys.datasafe.encrypiton.impl.pathencryption.PathEncryptionImplRuntimeDelegatable;
import de.adorsys.datasafe.storage.impl.fs.FileSystemStorageService;
import de.adorsys.datasafe.types.api.actions.ListRequest;
import de.adorsys.datasafe.types.api.actions.ReadRequest;
import de.adorsys.datasafe.types.api.actions.WriteRequest;
import de.adorsys.datasafe.types.api.context.BaseOverridesRegistry;
import de.adorsys.datasafe.types.api.context.overrides.OverridesRegistry;
import de.adorsys.datasafe.types.api.resource.PrivateResource;
import de.adorsys.datasafe.types.api.resource.Uri;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Function;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

class RuntimeOverrideOperationsTest {

    @Test
    @SneakyThrows
    void testPathEncryptionOverridden(@TempDir Path root) {
        // BEGIN_SNIPPET:Create overridable Datasafe services without recompilation
        // This shows how to override path encryption service, in particular we are going to disable it
        OverridesRegistry registry = new BaseOverridesRegistry();

        // PathEncryptionImpl now will have completely different functionality
        // instead of calling PathEncryptionImpl methods we will call PathEncryptionImplOverridden methods
        PathEncryptionImplRuntimeDelegatable.overrideWith(registry, PathEncryptionImplOverridden::new);

        // registering user
        UserIDAuth user = new UserIDAuth("user", "passwrd"::toCharArray);

        DefaultDatasafeServices defaultDatasafeServices = DaggerDefaultDatasafeServices.builder()
                .config(new DefaultDFSConfig(root.toAbsolutePath().toUri(), "secret"::toCharArray))
                .storage(new FileSystemStorageService(root))
                .build();

        defaultDatasafeServices.userProfile().registerUsingDefaults(user);
        OutputStream defOs = defaultDatasafeServices.privateService().write(WriteRequest.forDefaultPrivate(user, "file-old.txt"));
        defOs.write("HELLO".getBytes(UTF_8));
        defOs.close();
        Path file = Files.walk(root.resolve("users/user/private/files/SIV/")).filter(it -> !it.toFile().isDirectory()).findFirst().get();

        root.resolve("users/user/private/files/folder/SIV").toFile().mkdirs();
        Files.move(file, root.resolve("users/user/private/files/folder/SIV").resolve(file.getFileName()));
        root.resolve("users/user/private/files/SIV").toFile().delete();

        // Customized service, without creating complete module and building it:
        DefaultDatasafeServices datasafeServices = DaggerDefaultDatasafeServices.builder()
                .config(new DefaultDFSConfig(root.toAbsolutePath().toUri(), "secret"::toCharArray))
                .storage(new FileSystemStorageService(root))
                .overridesRegistry(registry)
                .build();


        datasafeServices.userProfile().registerUsingDefaults(user);
        // writing into user privatespace, note that with default implementation `file.txt` would be encrypted
        OutputStream os = datasafeServices.privateService().write(WriteRequest.forDefaultPrivate(user, "folder/file.txt"));
        os.write("HELLO".getBytes());
        os.close();
        // we can read file by its path
        assertThat(datasafeServices.privateService().read(ReadRequest.forDefaultPrivate(user, "folder/file.txt"))).hasContent("HELLO");
        assertThat(datasafeServices.privateService().read(ReadRequest.forDefaultPrivate(user, "folder/file-old.txt"))).hasContent("HELLO");
        // we can list file
        assertThat(datasafeServices.privateService().list(ListRequest.forDefaultPrivate(user, "folder/")))
                .extracting(it -> it.getResource().asPrivate().decryptedPath().asString())
                .contains("folder/file.txt", "folder/file-old.txt");
        // but we see raw folder name here:
        assertThat(Files.walk(root)).asString().contains("folder");
        // but filename is encrypted:
        assertThat(Files.walk(root)).asString().doesNotContain("file.txt");
        // END_SNIPPET
    }

    // Path encryption that does not encrypt paths
    class PathEncryptionImplOverridden extends PathEncryptionImpl {

        PathEncryptionImplOverridden(PathEncryptionImplRuntimeDelegatable.ArgumentsCaptor captor) {
            super(captor.getSymmetricPathEncryptionService(), captor.getPrivateKeyService());
        }

        @Override
        public Uri encrypt(UserIDAuth forUser, Uri path) {
            if (path.asString().contains("/")) {
                String[] rootAndInRoot = path.asString().split("/", 2);
                return new Uri(URI.create(rootAndInRoot[0] + "/" + super.encrypt(forUser, new Uri(rootAndInRoot[1])).asString()));
            }
            // encryption disabled for root folder:
            return path;
        }

        @Override
        public Function<Uri, Uri> decryptor(UserIDAuth forUser) {
            return rootWithEncrypted -> {
                if (rootWithEncrypted.asString().contains("/")) {
                    String[] rootAndInRoot = rootWithEncrypted.asString().split("/", 2);
                    return new Uri(rootAndInRoot[0] + "/" + super.decryptor(forUser).apply(new Uri(URI.create(rootAndInRoot[1]))).asString());
                }
                // encryption disabled for root folder:
                return rootWithEncrypted;
            };
        }
    }
}

valb3r avatar May 18 '20 10:05 valb3r

low priority

max402 avatar Jul 29 '24 14:07 max402

Hey @francis-pouatcha, @max402, it looks like there's already a functional version of an example with partial path encryption. Should we close this ticket, or does it require further refinement?

  • https://github.com/adorsys/datasafe/blob/develop/datasafe-examples/datasafe-examples-business/src/test/java/de/adorsys/datasafe/examples/business/filesystem/RuntimeOverrideOperationsTest.java

AssahBismarkabah avatar Sep 20 '24 09:09 AssahBismarkabah

Please understand how it works and add documentation in the application.

francis-pouatcha avatar Sep 24 '24 08:09 francis-pouatcha