-
Notifications
You must be signed in to change notification settings - Fork 24
Support for file store authentication (docker login) #56
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
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
9fc1ace
Add new method to fetch the credentials from the fileStore
AayushSaini101 607716b
Changed
AayushSaini101 4d4c8e5
Remove dependency of jackson and use jsonutil
AayushSaini101 553cb3f
Format the code
AayushSaini101 f5c7807
Fixed the improvements
AayushSaini101 783d186
Added the implementation of the file store
AayushSaini101 5a439e6
Remove the extra code use UserNamePasswordProvider class
AayushSaini101 89ec8bb
Optimize tests duration by using Junit 5 concurrency (#72)
jonesbusy bb259cc
Add stream API methods (#60)
vaidikcode 7bc68ce
Resolve conflicts
AayushSaini101 20d89d4
Resolve build error for code generation
AayushSaini101 4169fec
Remove ConfigOrasException
AayushSaini101 ccf460e
Update the logger file
AayushSaini101 79b0a8d
Use @tempdir
AayushSaini101 77c22c1
Bump org.wiremock:wiremock-standalone from 3.11.0 to 3.12.0 (#86)
dependabot[bot] 800e2d0
Remove unwanted file
AayushSaini101 5ec0901
Update with suggestions:
AayushSaini101 b3657bb
Add requirement for central publishing (#87)
jonesbusy 308fd7b
Sign snaphosts (#88)
jonesbusy fd82d3d
Pass sign-only profile (#89)
jonesbusy 3f8f515
Add central settings (#90)
jonesbusy 70bd660
Uncomment code
AayushSaini101 3fd4c90
Sign snaphosts (#88)
jonesbusy 1034001
Merge branch 'main' into 15
AayushSaini101 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
src/main/java/land/oras/auth/FileStoreAuthenticationProvider.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package land.oras.auth; | ||
|
|
||
| import land.oras.ContainerRef; | ||
| import land.oras.credentials.FileStore; | ||
| import land.oras.credentials.FileStore.Credential; | ||
| import land.oras.exception.OrasException; | ||
|
|
||
| /** | ||
| * FileStoreAuthenticationProvider is an implementation of the AuthProvider interface. | ||
| * It retrieves credentials from a FileStore and generates a Basic Authentication header. | ||
| */ | ||
| public class FileStoreAuthenticationProvider implements AuthProvider { | ||
|
|
||
| private final FileStore fileStore; | ||
| private final ContainerRef containerRef; | ||
| private final UsernamePasswordProvider usernamePasswordAuthProvider; | ||
|
|
||
| /** | ||
| * Constructor for FileStoreAuthenticationProvider. | ||
| * | ||
| * @param fileStore The FileStore instance to retrieve credentials from. | ||
| * @param containerRef The server address for which to retrieve credentials. | ||
| * @throws Exception If an error occurs during authentication initialization. | ||
| */ | ||
| public FileStoreAuthenticationProvider(FileStore fileStore, ContainerRef containerRef) throws Exception { | ||
| this.fileStore = fileStore; | ||
| this.containerRef = containerRef; | ||
| Credential credential = fileStore.get(containerRef); | ||
| if (credential == null) { | ||
| throw new OrasException("No credentials found for containerRef"); | ||
| } | ||
| this.usernamePasswordAuthProvider = | ||
| new UsernamePasswordProvider(credential.getUsername(), credential.getPassword()); | ||
| } | ||
|
|
||
| @Override | ||
| public String getAuthHeader() { | ||
| return usernamePasswordAuthProvider.getAuthHeader(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| package land.oras.credentials; | ||
|
|
||
| import java.io.FileReader; | ||
| import java.io.IOException; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import land.oras.ContainerRef; | ||
| import land.oras.exception.OrasException; | ||
| import land.oras.utils.JsonUtils; | ||
|
|
||
| /** | ||
| * FileStore implements a credentials store using a configuration file | ||
| * to keep the credentials in plain-text. | ||
| * | ||
| * Reference: https://docs.docker.com/engine/reference/commandline/cli/#docker-cli-configuration-file-configjson-properties | ||
| */ | ||
| public class FileStore { | ||
|
AayushSaini101 marked this conversation as resolved.
|
||
|
|
||
| private final boolean disablePut; | ||
| private final Config config; | ||
|
|
||
| /** | ||
| * Error message indicating that putting plaintext credentials is disabled. | ||
| * This is used to enforce security policies against storing sensitive credentials in plaintext format. | ||
| */ | ||
| public static final String ERR_PLAINTEXT_PUT_DISABLED = "Putting plaintext credentials is disabled"; | ||
|
|
||
| /** | ||
| * Error message indicating that the format of the provided credential is invalid. | ||
| * This is typically used when credentials do not match the expected structure or format. | ||
| */ | ||
| public static final String ERR_BAD_CREDENTIAL_FORMAT = "Bad credential format"; | ||
|
|
||
| /** | ||
| * Constructor for FileStore. | ||
| * | ||
| * @param disablePut boolean flag to disable putting credentials in plaintext. | ||
| * @param config configuration instance. | ||
| */ | ||
| public FileStore(boolean disablePut, Config config) { | ||
|
AayushSaini101 marked this conversation as resolved.
|
||
| this.disablePut = disablePut; | ||
| this.config = Objects.requireNonNull(config, "Config cannot be null"); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a new FileStore based on the given configuration file path. | ||
| * | ||
| * @param configPath Path to the configuration file. | ||
| * @return FileStore instance. | ||
| * @throws OrasException if loading the configuration fails. | ||
| */ | ||
| public static FileStore newFileStore(String configPath) throws OrasException { | ||
| Config cfg = Config.load(configPath); | ||
| return new FileStore(false, cfg); | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves credentials for the given containerRef. | ||
| * | ||
| * @param containerRef ContainerRef. | ||
| * @return Credential object. | ||
| * @throws OrasException if retrieval fails. | ||
| */ | ||
| public Credential get(ContainerRef containerRef) throws OrasException { | ||
| return config.getCredential(containerRef); | ||
| } | ||
|
|
||
| /** | ||
| * Saves credentials for the given ContainerRef. | ||
| * | ||
| * @param containerRef ContainerRef. | ||
| * @param credential Credential object. | ||
| * @throws Exception if saving fails. | ||
| */ | ||
| public void put(ContainerRef containerRef, Credential credential) throws Exception { | ||
| if (disablePut) { | ||
| throw new UnsupportedOperationException(ERR_PLAINTEXT_PUT_DISABLED); | ||
| } | ||
| validateCredentialFormat(credential); | ||
| config.putCredential(containerRef, credential); | ||
| } | ||
|
|
||
| /** | ||
| * Deletes credentials for the given container. | ||
| * | ||
| * @param containerRef . | ||
| * @throws OrasException if deletion fails. | ||
| */ | ||
| public void delete(ContainerRef containerRef) throws OrasException { | ||
| config.deleteCredential(containerRef); | ||
| } | ||
|
|
||
| /** | ||
| * Validates the format of the credential. | ||
| * | ||
| * @param credential Credential object. | ||
| * @throws Exception if the credential format is invalid. | ||
| */ | ||
| private void validateCredentialFormat(Credential credential) throws Exception { | ||
| if (credential.getUsername().contains(":")) { | ||
| throw new IllegalArgumentException(ERR_BAD_CREDENTIAL_FORMAT + ": colons(:) are not allowed in username"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Nested Config class for configuration management. | ||
| */ | ||
| public static class Config { | ||
| private final ConcurrentHashMap<String, Credential> credentialStore = new ConcurrentHashMap<>(); | ||
|
|
||
| /** | ||
| * Loads the configuration from a JSON file at the specified path and populates the credential store. | ||
| * | ||
| * @param configPath The path to the JSON configuration file. | ||
| * @return A {@code Config} object populated with the credentials from the JSON file. | ||
| * @throws OrasException If an error occurs while reading or parsing the JSON file. | ||
| */ | ||
| public static Config load(String configPath) throws OrasException { | ||
| Config config = new Config(); | ||
| try (FileReader reader = new FileReader(configPath)) { | ||
| // Deserialize the JSON file into a map of ContainerRef to Credential | ||
| Map<String, Map<String, String>> credentials = JsonUtils.fromJson(reader, Map.class); | ||
|
|
||
| // Populate the credential store with the parsed credentials | ||
| for (Map.Entry<String, Map<String, String>> entry : credentials.entrySet()) { | ||
| Map<String, String> values = entry.getValue(); | ||
| if (values != null) { | ||
| String username = values.get("username"); | ||
| String password = values.get("password"); | ||
| if (username != null && password != null) { | ||
| config.credentialStore.put(entry.getKey(), new Credential(username, password)); | ||
| } else { | ||
| throw new OrasException( | ||
| "Invalid credential entry: missing username or password for " + entry.getKey()); | ||
| } | ||
| } | ||
| } | ||
| } catch (IOException e) { | ||
| throw new OrasException("Failed to load configuration from path: " + configPath, e); | ||
| } catch (ClassCastException e) { | ||
| throw new OrasException("Invalid JSON structure in configuration file: " + configPath, e); | ||
| } | ||
| return config; | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves the {@code Credential} associated with the specified containerRef. | ||
| * | ||
| * @param containerRef The containerRef whose credential is to be retrieved. | ||
| * @return The {@code Credential} associated with the containerRef, or {@code null} if no credential is found. | ||
| */ | ||
| public Credential getCredential(ContainerRef containerRef) throws OrasException { | ||
| if (credentialStore.containsKey(containerRef)) { | ||
| return credentialStore.get(containerRef); | ||
| } else { | ||
| throw new OrasException("No credentials found for server address"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Associates the specified {@code Credential} with the given containerRef. | ||
| * If a credential already exists for the containerRef, it will be replaced. | ||
| * | ||
| * @param containerRef The containerRef to associate with the credential. | ||
| * @param credential The {@code Credential} to store. Must not be {@code null}. | ||
| * @throws NullPointerException If the provided credential is {@code null}. | ||
| */ | ||
| public void putCredential(ContainerRef containerRef, Credential credential) { | ||
| credentialStore.put(containerRef.toString(), credential); | ||
| } | ||
|
|
||
| /** | ||
| * Removes the {@code Credential} associated with the specified containerRef. | ||
| * If no credential is associated with the containerRef, this method does nothing. | ||
| * | ||
| * @param containerRef The containerRef whose credential is to be removed. | ||
| */ | ||
| public void deleteCredential(ContainerRef containerRef) { | ||
| credentialStore.remove(containerRef.toString()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Nested Credential class to represent username and password pairs. | ||
| */ | ||
| public static class Credential { | ||
| private String username; | ||
| private String password; | ||
|
|
||
| /** | ||
| * Constructs a new {@code Credential} object with the specified username and password. | ||
| * | ||
| * @param username The username for the credential. Must not be {@code null}. | ||
| * @param password The password for the credential. Must not be {@code null}. | ||
| */ | ||
| public Credential(String username, String password) { | ||
| this.username = Objects.requireNonNull(username, "Username cannot be null"); | ||
| this.password = Objects.requireNonNull(password, "Password cannot be null"); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the username associated with this credential. | ||
| * | ||
| * @return The username as a {@code String}. | ||
| */ | ||
| public String getUsername() { | ||
| return username; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the password associated with this credential. | ||
| * | ||
| * @return The password as a {@code String}. | ||
| */ | ||
| public String getPassword() { | ||
| return password; | ||
| } | ||
| } | ||
| } | ||
2 changes: 1 addition & 1 deletion
2
src/main/java/land/oras/Error.java → src/main/java/land/oras/exception/Error.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/main/java/land/oras/OrasException.java → ...va/land/oras/exception/OrasException.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.