![JAR search and dependency download from the Maven repository](/logo.png)
com.sourceclear.plugins.config.ConfigServiceImpl Maven / Gradle / Ivy
/*
Copyright (c) 2015 SourceClear Inc.
Licensed 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.
*/
package com.sourceclear.plugins.config;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.sourceclear.api.client.Client;
import com.sourceclear.api.client.Response;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import static java.lang.String.format;
import java.lang.reflect.Field;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConfigServiceImpl implements ConfigService {
///////////////////////////// Class Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigServiceImpl.class);
private static final ObjectMapper MAPPER = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
////////////////////////////// Class Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//////////////////////////////// Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// this is named goofy to encourage use of the accessor,
// so we can lazy-init the configuration system.
private ConsoleConfig $config;
/** Indicates that we should not check the permissions on the config file. */
private boolean isInsecureConfigMode;
/** Files that will be parsed after the main one is. */
private List configFiles = new ArrayList<>();
/////////////////////////////// Constructors \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
public ConfigServiceImpl() {
this(null);
}
public ConfigServiceImpl(ConsoleConfig config) { this.$config = config; }
////////////////////////////////// Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//------------------------ Implements: ConfigService
@Override
public void addExtraConfigFiles(@Nullable List extras) {
if (null == extras || extras.isEmpty()) {
return;
}
for (File f : extras) {
if (!f.exists()) {
LOGGER.debug("Skipping 404 config file \"{}\"", f);
continue;
}
if (!f.isFile()) {
LOGGER.debug("Perhaps one day we will support non-file configs," +
" but today is not that day: \"{}\"", f);
continue;
}
if (!f.canRead()) {
LOGGER.debug("Unable to read file \"{}\"", f);
continue;
}
configFiles.add(f);
}
}
@Override
public boolean isConfigFilePresent() {
final File configFile = new File(getConfigFileName());
return configFile.exists() && configFile.isFile();
}
@Override
@Nonnull
public File createBlankConfig() throws ConfigException {
final File configFile;
try {
configFile = File.createTempFile("srcclr-terminal-config", ".json");
} catch (IOException e) {
final String msg = "Unable to create a temp file";
throw new ConfigException(msg, e);
}
try {
final ConsoleConfig value = new ConsoleConfig.Builder().withUserToken("").withOrgToken("").build();
MAPPER.writeValue(configFile, value);
} catch (IOException e) {
LOGGER.error("Unable to serialize JSON to {}", configFile, e);
throw new ConfigException("Unable to serialize config values to the temp file");
}
return configFile;
}
@Override
public void setInsecureConfigFileMode(boolean value) {
isInsecureConfigMode = value;
}
@Override
public ConsoleConfig getConfiguration() {
synchronized (ConsoleConfig.class) {
if (null == $config) {
try {
init();
} catch (IOException e) {
final String msg = "Unable to initialize config";
LOGGER.debug(msg, e);
throw new ConfigException(msg);
}
}
}
return $config;
}
@Override
public Object get(@Nonnull String key) {
try {
return getEx(key);
} catch (Exception e) {
final String msg = String.format(
"Unable to access config key \"%s\"", key);
if (!(e instanceof NoSuchFieldException)) {
LOGGER.warn(msg, e);
}
throw new ConfigException(msg);
}
}
Object getEx(String key) throws Exception {
final ConsoleConfig config = getConfiguration();
final java.lang.reflect.Field m = config.getClass().getDeclaredField(key);
m.setAccessible(true);
return m.get(config);
}
@Override
public void set(@Nonnull String key, Object value) {
try {
setEx(key, value);
} catch (Exception e) {
final String msg = String.format(
"Unable to write to config key \"%s\"", key);
if (!(e instanceof NoSuchFieldException)) {
LOGGER.warn(msg, e);
}
throw new ConfigException(msg);
}
}
void setEx(String key, Object value) throws Exception {
final ConsoleConfig config = getConfiguration();
final java.lang.reflect.Field m = config.getClass().getDeclaredField(key);
m.setAccessible(true);
m.set(config, value);
}
@Override
@Nonnull
public Map list() {
return list("/.*/");
}
@Override
@Nonnull
public Map list(String constraint) {
try {
return listEx(constraint);
} catch (Exception e) {
LOGGER.error("Kaboom", e);
throw new RuntimeException(e);
}
}
@Nonnull
Map listEx(String constraint) throws Exception {
Pattern filter;
if (null == constraint) {
filter = Pattern.compile(".*");
} else if (constraint.charAt(0) == '/') {
final String regex = constraint.substring(1, constraint.lastIndexOf('/'));
filter = Pattern.compile(regex);
} else {
filter = Pattern.compile("^\\Q"+constraint+"\\E.*");
}
final Map result = new TreeMap<>();
final Field[] fields = ConsoleConfig.class.getDeclaredFields();
checkNotNull(fields, "Unable to enumerate the fields of ConsoleConfig");
checkState(0 != fields.length, "ConsoleConfig lost all its Fields");
for (final Field f : fields) {
final JsonProperty jp = f.getAnnotation(JsonProperty.class);
if (null == jp) {
continue;
}
final String name = jp.value();
// it is far more likely they want /prox/ to match, not requiring /prox.*/
// so use "find" not "matches"
if (!filter.matcher(name).find()) {
continue;
}
final Object value = get(name);
result.put(name, value);
}
return result;
}
@Override
public String getUsername(Client client) {
final Response userResp;
try {
userResp = client.jsonAPI(Client.Method.GET, URI.create("user"));
} catch (IOException e) {
throw (ConfigException)new ConfigException(format(
"Unable to communicate with SRC:CLR API: %s", e.getMessage()))
.initCause(e);
}
final int statusCode = userResp.getStatusCode();
if (!(200 <= statusCode && statusCode < 300)) {
throw new ConfigException(format(
"Server did not fulfill the username request; code=%s; reason=\"%s\"",
statusCode, userResp.getStatusText()));
}
final String result = userResp.getPayload().path("username").textValue();
if (StringUtils.isEmpty(result)) {
throw new ConfigException("Response did not contain \"username\" as expected");
}
return result;
}
@Nullable
@Override
public String getSourceClearClientToken() {
final String envKey = EnvironmentProvider.getEnvProvider().getenv(API_TOKEN_ENV_VAR);
if (!StringUtils.isBlank(envKey)) {
return envKey;
}
return getConfiguration().getUserToken();
}
//------------------------ Overrides:
//---------------------------- Abstract Methods -----------------------------
//---------------------------- Utility Methods ------------------------------
/**
* Attempts to load the configuration or configuration envelope (in the case of
* encrypted configs).
* @throws IOException if the config file doesn't exist or can't be read.
*/
private void init() throws IOException {
final File standardConfigFile = new File(getConfigFileName());
init(standardConfigFile);
for (File extraConfig : configFiles) {
init(extraConfig);
}
}
private void init(File configFile) throws IOException {
if (!configFile.exists()) {
throw new IOException(format(
"Cannot find config file \"%s\"\neither it doesn't exist or cannot be read", configFile));
}
assertConfigFilePermissions(configFile);
LOGGER.info("Using config file at " + configFile.getAbsolutePath());
final ConsoleConfig value;
try (final InputStream fis = new FileInputStream(configFile)) {
value = MAPPER.readValue(fis, ConsoleConfig.class);
}
$config = mergeConfigs($config, value);
}
ConsoleConfig mergeConfigs(ConsoleConfig a, ConsoleConfig b) {
if (null == a) {
return b;
}
if (null == b) {
return a;
}
return new ConsoleConfig.Builder(a).withConsoleConfig(b).build();
}
/**
* Determine the location of the config file. This will use the value of SRCCLR_DIR/console.conf
* if defined by the environment, otherwise it will default to ~/.srcclr/console.conf.
*/
private String getConfigFileName() {
final String envFolder = EnvironmentProvider.getEnvProvider().getenv("SRCCLR_DIR");
final File folder;
if (StringUtils.isNotBlank(envFolder)) {
folder = new File(envFolder);
} else {
folder = CONFIG_DIRECTORY;
}
return new File(folder, CONFIG_FILENAME).getAbsolutePath();
}
private void assertConfigFilePermissions(File configFile) throws IOException {
if (isInsecureConfigMode) {
LOGGER.debug("Skipping file permission check due to insecure config mode");
return;
}
//
// Ensure that the permissions are 600 or 400
//
Set perms = Files.getPosixFilePermissions(configFile.toPath());
String permString = PosixFilePermissions.toString(perms);
if (!permString.equals("rw-------") && !permString.equals("r--------")) {
throw new ConfigException(String.format(
"Invalid permissions on %s.%n" +
"Expected %s but found %s.%n" +
"Run 'chmod 600 %s' to correct", configFile, "rw-------", permString, configFile));
}
}
@Nullable
@Override
public String getSourceClearOrgToken() {
final String envKey = EnvironmentProvider.getEnvProvider().getenv(ORG_TOKEN_ENV_VAR);
if (!StringUtils.isBlank(envKey)) {
return envKey;
}
return getConfiguration().getOrgToken();
}
//---------------------------- Property Methods -----------------------------
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy