Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Public method for decrypting ciphertext name. #263

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from

Conversation

infeo
Copy link
Member

@infeo infeo commented Dec 10, 2024

This PR adds the method getCleartextName(Path p) to the CryptoFileSystem interface, which decrypts the filename of the given path and return the cleartext name.

Note: The method does not compute the whole cleartext path, since for this a recursive directory listing of the cryptoFileSystem is needed.

The feature is encapsulated in its own class FileNameDecryptor, which first validates the input path and second tries to decrypt it by using the DirId backup file. If the directory does not have such a file, an UnsupportedOperationException is thrown.

@infeo infeo self-assigned this Dec 10, 2024
Copy link

coderabbitai bot commented Dec 10, 2024

Walkthrough

The pull request introduces significant modifications across several classes within the org.cryptomator.cryptofs package. A new abstract method, getCleartextName(Path ciphertextNode), has been added to the CryptoFileSystem class, which is designed to compute the cleartext name from a valid encrypted node. Corresponding implementations have been made in the CryptoFileSystemImpl class, where a new member variable fileNameDecryptor is introduced to facilitate filename decryption. The constructor of CryptoFileSystemImpl has been updated to include this new parameter. Additionally, a new class, FileNameDecryptor, has been created to handle the decryption of file names, complete with validation and error handling methods. The test suite has also been expanded, with the introduction of FileNameDecryptorTest, which rigorously tests the new decryption functionality. Other tests have been updated to replace custom exception classes with a new TestCryptoException for improved clarity and consistency in error handling.

Possibly related PRs

  • Feature: Extend dir id backup class #260: The changes in the DirectoryIdBackup class, particularly the introduction of the write method and the new read method, are directly related to the modifications in the CryptoFileSystemImpl class where the method for backing up the directory ID has been updated from backupManually to write. This indicates a strong connection in functionality regarding directory ID management.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (7)
src/main/java/org/cryptomator/cryptofs/FileNameDecryptor.java (2)

63-63: Correct typo in exception message

There is a typo in the exception message: "Filname" should be "Filename".

Apply this diff to correct the typo:

-throw new FileSystemException(ciphertextNode.toString(), null, "Filname decryption failed:" + e);
+throw new FileSystemException(ciphertextNode.toString(), null, "Filename decryption failed:" + e);

76-76: Clarify error message in validatePath method

The error message in the validatePath method could be clearer. Since both conditions (correct extension and minimum filename length) need to be met, consider updating "or" to "and/or" to avoid confusion.

Apply this diff to improve the error message:

-throw new IllegalArgumentException("Node %s does not end with %s or %s or filename is shorter than %d characters.".formatted(absolutePath, Constants.CRYPTOMATOR_FILE_SUFFIX, Constants.DEFLATED_FILE_SUFFIX, Constants.MIN_CIPHER_NAME_LENGTH));
+throw new IllegalArgumentException("Node %s does not end with %s or %s and/or filename is shorter than %d characters.".formatted(absolutePath, Constants.CRYPTOMATOR_FILE_SUFFIX, Constants.DEFLATED_FILE_SUFFIX, Constants.MIN_CIPHER_NAME_LENGTH));
src/test/java/org/cryptomator/cryptofs/FileNameDecryptorTest.java (1)

116-116: Handle CryptoException specifically in inflateThrows test

In the inflateThrows test, the mock throws a TestCryptoException, but the catch block in the production code only handles IOException. This mismatch may lead to the exception not being correctly tested.

Consider adjusting the test to throw an IOException or update the production code to handle CryptoException appropriately.

src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java (4)

Line range hint 108-131: Consider reducing the number of dependencies and using constructor injection

The large number of mocked dependencies (20+) suggests that CryptoFileSystemImpl might have too many responsibilities. Consider:

  1. Breaking down the class into smaller components
  2. Using constructor injection instead of field injection
  3. Implementing a builder pattern for test setup

This would improve:

  • Testability by making dependencies explicit
  • Maintainability by reducing coupling
  • Readability by encapsulating test setup

Example refactor:

@Builder
class CryptoFileSystemImpl {
    private final FileSystemProvider provider;
    private final CryptoFileSystems cryptoFileSystems;
    // ... other dependencies
    
    public CryptoFileSystemImpl(FileSystemProvider provider, 
                               CryptoFileSystems cryptoFileSystems,
                               // ... other dependencies
                               ) {
        this.provider = provider;
        this.cryptoFileSystems = cryptoFileSystems;
        // ...
    }
}

// In test:
@Test
void testMethod() {
    var sut = CryptoFileSystemImpl.builder()
        .provider(mock(FileSystemProvider.class))
        .cryptoFileSystems(mock(CryptoFileSystems.class))
        // ...
        .build();
    // test logic
}

Line range hint 1037-1077: Simplify mock chains using test doubles

The current implementation uses complex mock chains which can make tests brittle and harder to maintain. Consider:

  1. Creating test-specific implementations for simpler interfaces
  2. Using ArgumentCaptor to verify complex interactions
  3. Extracting common mock setup into helper methods

Example:

class TestCryptoPath implements CryptoPath {
    private final String path;
    
    TestCryptoPath(String path) {
        this.path = path;
    }
    
    @Override
    public String toString() {
        return path;
    }
    // implement other methods
}

@Test
void testMethod() {
    var path = new TestCryptoPath("/test/path");
    // test logic using path
}

Line range hint 1164-1187: Improve error handling test organization and messages

The error handling tests could be improved by:

  1. Grouping related error cases using @Nested classes
  2. Using more descriptive error messages
  3. Adding test cases for concurrent access scenarios

Example:

@Nested
class ErrorHandling {
    @Nested
    class AccessDenied {
        @Test
        void throwsAccessDeniedWithDescriptiveMessage() {
            var e = assertThrows(AccessDeniedException.class, 
                () -> inTest.checkAccess(path, AccessMode.WRITE));
            assertEquals("Cannot write to read-only file: " + path, 
                e.getMessage());
        }
    }
}

Line range hint 1279-1307: Split multi-behavior tests into focused test methods

Some test methods verify multiple behaviors. Consider:

  1. Splitting into smaller, focused tests
  2. Using more descriptive test names following the given/when/then pattern
  3. Extracting common setup into helper methods

Example refactor:

@Test
void givenReadOnlyFile_whenCheckingWriteAccess_thenThrowsAccessDenied() {
    // test logic
}

@Test
void givenReadOnlyFile_whenCheckingReadAccess_thenSucceeds() {
    // test logic
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4806744 and 0915cb8.

📒 Files selected for processing (8)
  • src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java (2 hunks)
  • src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java (4 hunks)
  • src/main/java/org/cryptomator/cryptofs/FileNameDecryptor.java (1 hunks)
  • src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/DirectoryIdBackupTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/FileNameDecryptorTest.java (1 hunks)
  • src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanContentDirTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/util/TestCryptoException.java (1 hunks)
🔇 Additional comments (8)
src/test/java/org/cryptomator/cryptofs/util/TestCryptoException.java (1)

1-11: LGTM! Well-structured test utility class.

The implementation is clean and follows test utility best practices:

  • Properly placed in the test util package
  • Extends the appropriate base exception
  • Minimal implementation sufficient for test purposes
src/test/java/org/cryptomator/cryptofs/DirectoryIdBackupTest.java (1)

108-108: LGTM! Consistent use of TestCryptoException.

Good refactoring to use the standardized TestCryptoException instead of a custom exception class.

src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanContentDirTest.java (1)

230-230: LGTM! Consistent exception handling update.

Good update to use TestCryptoException in the expected exceptions list, maintaining consistency with the new test exception handling approach.

src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java (1)

46-64: Method getCleartextName is well-documented and aligns with class responsibilities

The addition of the abstract method getCleartextName(Path ciphertextNode) enhances the functionality of CryptoFileSystem. The method is appropriately documented with clear parameters, return value, and exceptions.

src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java (3)

Line range hint 103-133: Update constructor to match new dependencies

The constructor of CryptoFileSystemImpl has been updated to include FileNameDecryptor. Ensure that all dependencies are properly injected and initialized.


156-159: getCleartextName method implementation uses fileNameDecryptor correctly

The implementation of getCleartextName(Path ciphertextNode) correctly delegates to fileNameDecryptor.decryptFilename(ciphertextNode), aligning with the design.


160-160: Consider handling potential exceptions from decryptFilename

While decryptFilename throws IOException and UnsupportedOperationException, consider if additional exception handling is needed in getCleartextName to provide more context or to handle unexpected exceptions.

Ensure that any exceptions are appropriately propagated or handled.

src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java (1)

Line range hint 582-582: Address TODO comment regarding behavior verification

The TODO comment questions whether the current behavior is correct. This needs to be:

  1. Verified against the specification
  2. Documented with the reasoning
  3. Tested appropriately

Would you like me to help create additional test cases after the behavior is verified?

}

boolean isAtCipherNodeLevel(Path p) {
return vaultPath.relativize(p).getNameCount() == 4; //TODO: relativize is defined for two relative Paths. For two absolute paths, the result depends on the OS
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure OS-independent behavior when using Path.relativize

The use of vaultPath.relativize(p) may not behave consistently across different operating systems when both paths are absolute, as noted in the TODO comment. This could lead to incorrect path depth calculations.

Consider adjusting the implementation to ensure OS-independent behavior. One approach is to convert both paths to relative paths before calling relativize, or manually calculate the depth using getNameCount() after verifying that p starts with vaultPath.

Comment on lines +98 to +99
var name = "toDecrypt";
var ciphertextNode = tmpPath.resolve(name + ".c9s");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Incorrect file extension in test case

In the notDecryptableCiphertext test, the file extension used is ".c9s", which may not match the expected file extensions defined in Constants.

Apply this diff to correct the file extension:

 var ciphertextNode = tmpPath.resolve(name + ".c9s");
+// Assuming the correct extension is Constants.CRYPTOMATOR_FILE_SUFFIX or Constants.DEFLATED_FILE_SUFFIX
+var ciphertextNode = tmpPath.resolve(name + Constants.CRYPTOMATOR_FILE_SUFFIX);

Ensure that test cases use valid file extensions to accurately reflect production scenarios.

Committable suggestion skipped: line range outside the PR's diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant