All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.oliviercailloux.jaris.credentials.CredsReader Maven / Gradle / Ivy

package io.github.oliviercailloux.jaris.credentials;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

* This class permits to read a user’s credentials (authentication information), meaning, a username * and a password, from various sources. *

*

* Instances of this class will read from the following three sources of information, and return the * credentials from the first valid source it found (following the order of priority * displayed here). A source is valid iff it provides the two pieces of information required: * username and password. Be aware that a piece of information may be provided and empty. *

*
    *
  1. System properties {@code usernameKey} and {@code passwordKey}. Each property may be set, * including to the empty string, or not set. This source is valid iff both properties are set.
  2. *
  3. Environment variables {@code usernameKey} and {@code passwordKey}. Each variable may be set, * including to the empty string, or not set. This source is valid iff both environment variables * are set.
  4. *
  5. File {@code filePath}. This source is valid iff the file exists. The first line of the file * gives the username, the second one gives the password. If the file has only one line, the * password is considered to be the empty string. If the file is empty, the username and the * password are both considered to be the empty string. Empty lines are not considered at all. If * the file has non empty line content after the second line, it is an error. Both classical end of * line marks ({@code \r} and {@code \r\n}) are considered as end of lines, and the file is * considered as encoded in UTF-8.
  6. *
*

* This class also permits to configure the sources that it attempts to read from, at instance * creation time (using the factory methods). If not provided, the default value for * {@code usernameKey} is {@value #DEFAULT_USERNAME_KEY}; the default value for {@code passwordKey} * is {@value #DEFAULT_PASSWORD_KEY} (both for system properties and environment); and the default * value for {@code filePath} is {@code "API_login.txt"} in the current directory. *

*

* Instances of this class are immutable. *

*/ public class CredsReader { @SuppressWarnings("unused") private static final Logger LOGGER = LoggerFactory.getLogger(CredsReader.class); /** * The default value of the username key used in {@code CredsReader.defaultCreds()}. (All * uppercase to follow the usual convention when * used as an environment variable.) */ public static final String DEFAULT_USERNAME_KEY = "API_USERNAME"; /** * The default value of the password key used in {@code CredsReader.defaultCreds()}. (All * uppercase to follow the usual convention when * used as an environment variable.) */ public static final String DEFAULT_PASSWORD_KEY = "API_PASSWORD"; /** * The default value of the file path. */ public static final Path DEFAULT_FILE_PATH = Path.of("API_login.txt"); private final String usernameKey; private final String passwordKey; private final Path filePath; Map env = System.getenv(); /** * Returns an instance that will read from the sources configured with the given parameters. * * @param usernameKey the username key to use for reading from system properties and the * environment. * @param passwordKey the password key to use for reading from system properties and the * environment. * @param filePath the file path to use for reading from the file. * @return a configured instance. * @see #defaultCredsReader() */ public static CredsReader given(String usernameKey, String passwordKey, Path filePath) { CredsReader credsReader = new CredsReader(usernameKey, passwordKey, filePath); return credsReader; } /** * Returns an instance that will use the default values {@link #DEFAULT_USERNAME_KEY}, * {@link #DEFAULT_PASSWORD_KEY}, {@link #DEFAULT_FILE_PATH}. * * @return a default instance. * @see #given(String, String, Path) */ public static CredsReader defaultCredsReader() { CredsReader credsReader = new CredsReader(DEFAULT_USERNAME_KEY, DEFAULT_PASSWORD_KEY, DEFAULT_FILE_PATH); return credsReader; } private CredsReader(String usernameKey, String passwordKey, Path filePath) { this.usernameKey = checkNotNull(usernameKey); this.passwordKey = checkNotNull(passwordKey); this.filePath = checkNotNull(filePath); } /** * Returns the username key, used to read from the system properties and the environment. */ public String getUsernameKey() { return usernameKey; } /** * Returns the password key, used to read from the system properties and the environment. */ public String getPasswordKey() { return passwordKey; } /** * Returns the file path that is read from, considering the file source. */ public Path getFilePath() { return filePath; } /** * Returns the credentials found from the first valid source, unless an exception is raised. * * @throws IllegalStateException if no valid source is found, or if the file source has non empty * line content after the second line. * @throws IOException if an I/O error occurs reading from the file or a malformed or unmappable * byte sequence is read from the file. * @see CredsReader */ public Credentials getCredentials() throws IllegalStateException, IOException { final CredsOpt credsOpt = readCredentials(); if (credsOpt.getUsername().isEmpty() && credsOpt.getPassword().isEmpty()) { throw new IllegalStateException("Login information not found."); } if (credsOpt.getUsername().isEmpty()) { throw new IllegalStateException("Found password but no username."); } if (credsOpt.getPassword().isEmpty()) { throw new IllegalStateException("Found username but no password."); } final Credentials credentials = Credentials.given(credsOpt.getUsername().get(), credsOpt.getPassword().get()); return credentials; } /** *

* Returns the best credentials it could find, throwing no error if some is missing. *

* * @throws IllegalStateException if a file source is provided but has non empty line content after * the second line. * */ CredsOpt readCredentials() throws IOException, IllegalStateException { final CredsOpt propertyAuthentication; { final String username = System.getProperty(usernameKey); final String password = System.getProperty(passwordKey); propertyAuthentication = CredsOpt.given(Optional.ofNullable(username), Optional.ofNullable(password)); final int informationalValue = propertyAuthentication.getInformationalValue(); LOGGER.info( "Found {} piece" + (informationalValue == 2 ? "s" : "") + " of login information in properties {} and {}.", informationalValue, usernameKey, passwordKey); } final CredsOpt envAuthentication; { final String username = env.get(usernameKey); final String password = env.get(passwordKey); envAuthentication = CredsOpt.given(Optional.ofNullable(username), Optional.ofNullable(password)); final int informationalValue = envAuthentication.getInformationalValue(); LOGGER.info( "Found {} piece" + (informationalValue == 2 ? "s" : "") + " of login information in environment variables {} and {}.", informationalValue, usernameKey, passwordKey); } final CredsOpt fileAuthentication; { final Optional optUsername; final Optional optPassword; final Path path = filePath; if (!Files.exists(path)) { optUsername = Optional.empty(); optPassword = Optional.empty(); } else { final List lines = Files.readAllLines(path); final Iterator iterator = lines.iterator(); if (iterator.hasNext()) { optUsername = Optional.of(iterator.next()); } else { optUsername = Optional.of(""); } if (iterator.hasNext()) { optPassword = Optional.of(iterator.next()); } else { optPassword = Optional.of(""); } while (iterator.hasNext()) { if (!iterator.next().isEmpty()) { throw new IllegalStateException( "File " + filePath + " is too long: " + lines.size() + " lines"); } } } fileAuthentication = CredsOpt.given(optUsername, optPassword); final int informationalValue = fileAuthentication.getInformationalValue(); LOGGER.info("Found {} piece" + (informationalValue == 2 ? "s" : "") + " of login information in file.", informationalValue); } final TreeMap map = new TreeMap<>(); map.put(propertyAuthentication.getInformationalValue() * 1.2d, propertyAuthentication); map.put(envAuthentication.getInformationalValue() * 1.1d, envAuthentication); map.put(fileAuthentication.getInformationalValue() * 1.0d, fileAuthentication); return map.lastEntry().getValue(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy