Skip to content

Commit

Permalink
fixed exceptions on startup when configuration file is missing, and a…
Browse files Browse the repository at this point in the history
…lso fixed bug where Para would not save/update secret in config file
  • Loading branch information
albogdano committed Dec 1, 2023
1 parent fb62068 commit e579d7c
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 41 deletions.
2 changes: 1 addition & 1 deletion para-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.erudika.para.core.App;
import com.erudika.para.core.annotations.Documented;
import static com.erudika.para.core.utils.Config.PARA;
import com.typesafe.config.ConfigList;
import com.typesafe.config.ConfigObject;
import java.nio.file.Paths;
import java.util.HashMap;
Expand Down Expand Up @@ -95,7 +96,7 @@ public String environment() {
description = "Selects the `DAO` implementation at runtime. Can be `AWSDynamoDAO`, `MongoDBDAO`, "
+ "`CassandraDAO`, etc. Each implementation has its own configuration properties.")
public String daoPlugin() {
return getConfigParam("dao", "");
return getConfigParam("dao", "H2DAO");
}

@Documented(position = 60,
Expand All @@ -105,7 +106,7 @@ public String daoPlugin() {
tags = {"requires restart"},
description = "Selects the `Search` implementation at runtime. Can be `LuceneSearch`, `ElasticSearch`, etc.")
public String searchPlugin() {
return getConfigParam("search", "");
return getConfigParam("search", "LuceneSearch");
}

@Documented(position = 70,
Expand All @@ -115,7 +116,7 @@ public String searchPlugin() {
tags = {"requires restart"},
description = "Selects the `Cache` implementation at runtime. Can be one of `CaffeineSearch`, `HazelcastCache`.")
public String cachePlugin() {
return getConfigParam("cache", "");
return getConfigParam("cache", "CaffeineSearch");
}

@Documented(position = 80,
Expand All @@ -125,7 +126,7 @@ public String cachePlugin() {
tags = {"requires restart"},
description = "Selects the `Queue` implementation at runtime. Can be one of `LocalQueue`, `AWSQueue`.")
public String queuePlugin() {
return getConfigParam("q", "");
return getConfigParam("q", "LocalQueue");
}

@Documented(position = 90,
Expand All @@ -135,7 +136,7 @@ public String queuePlugin() {
tags = {"requires restart"},
description = "Selects the `FileStore` implementation at runtime. Can be one of `LocalFileStore`, `AWSFileStore`.")
public String fileStoragePlugin() {
return getConfigParam("fs", "");
return getConfigParam("fs", "LocalFileStore");
}

@Documented(position = 100,
Expand All @@ -145,7 +146,7 @@ public String fileStoragePlugin() {
description = "Selects the `Emailer` implementation at runtime. "
+ "Can be one of `AWSEmailer`, `JavaMailEmailer`, `NoopEmailer`. ")
public String emailerPlugin() {
return getConfigParam("emailer", "");
return getConfigParam("emailer", "NoopEmailer");
}

@Documented(position = 110,
Expand Down Expand Up @@ -676,6 +677,16 @@ public ConfigObject protectedPaths() {
return getConfig().hasPath("security.protected") ? getConfig().getObject("security.protected") : null;
}

@Documented(position = 611,
identifier = "security.ignored",
type = ConfigList.class,
category = "Security",
description = "A list of ignored paths which will not require authentication before accessing. "
+ "A list of paths like this `[\"/{path1}\", \"/{path2}/**\", ...]`.")
public ConfigList ignoredPaths() {
return getConfig().hasPath("security.ignored") ? getConfig().getList("security.ignored") : null;
}

@Documented(position = 620,
identifier = "security.signin",
value = "/signin",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ public final class IgnoredRequestMatcher implements RequestMatcher {
private final OrRequestMatcher orMatcher;

private IgnoredRequestMatcher() {
ConfigList c = Para.getConfig().getConfig().getList("security.ignored");
List<RequestMatcher> list = new ArrayList<>(c.size());
for (ConfigValue configValue : c) {
list.add(new AntPathRequestMatcher((String) configValue.unwrapped()));
ConfigList c = Para.getConfig().ignoredPaths();
List<RequestMatcher> list = new ArrayList<>();
if (c != null) {
for (ConfigValue configValue : c) {
list.add(new AntPathRequestMatcher((String) configValue.unwrapped()));
}
} else {
list.add(new AntPathRequestMatcher("/"));
}
orMatcher = new OrRequestMatcher(list);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import com.erudika.para.core.listeners.InitializeListener;
import com.erudika.para.core.utils.Para;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -96,49 +99,68 @@ public synchronized void performHealthCheck() {
@Override
public void onInitialize() {
performHealthCheck();
String confFile = Paths.get(Para.getConfig().getConfigFilePath()).toAbsolutePath().toString();
if (!isHealthy()) {
if (rootAppExists) {
logger.warn("Server is unhealthy - the search index may be corrupted and may have to be rebuilt.");
} else {
Map<String, String> rootAppCredentials = Para.setup();
String confFile = Paths.get(Para.getConfig().getConfigFilePath()).toAbsolutePath().toString();
if (rootAppCredentials.containsKey("secretKey")) {
try (InputStream ref = getClass().getClassLoader().getResourceAsStream("reference.conf");
InputStream config = Optional.ofNullable(Para.getFileStore().load(confFile)).orElse(ref)) {

String confString = new String(config.readAllBytes(), StandardCharsets.UTF_8);
String accessKey = "para.root_access_key = \"" + rootAppCredentials.get("accessKey") + "\"";
String secretKey = "para.root_secret_key = \"" + rootAppCredentials.get("secretKey") + "\"";
if (confString.contains("para.root_access_key")) {
confString = confString.replaceAll("para\\.root_access_key\\s*=\\s*\".*?\"", accessKey);
} else {
confString += "\n" + accessKey;
}
if (confString.contains("para.root_secret_key")) {
confString = confString.replaceAll("para\\.root_secret_key\\s*=\\s*\".*?\"", secretKey);
} else {
confString += "\n" + secretKey;
}
Para.getFileStore().store(confFile, new ByteArrayInputStream(confString.
getBytes(StandardCharsets.UTF_8)));
logger.info("Saved root app credentials to {}.", confFile);
} catch (Exception e) {
logger.info("Initialized root app with access key '{}' and secret '{}', "
+ "but could not write these to {}.",
rootAppCredentials.get("accessKey"), rootAppCredentials.get("secretKey"), confFile);
}
} else {
logger.warn("Server is unhealthy - failed to initialize root app. Open http://localhost:" +
Para.getConfig().serverPort() + "/v1/_setup in the browser to initialize Para manually.");
saveConfigFile(confFile, Para.setup());
}
} else if (isHealthy() && !Para.getConfig().inProduction()) {
App root = Para.getDAO().read(App.id(Para.getConfig().getRootAppIdentifier()));
try {
if (root != null && (!Files.exists(Path.of(confFile)) || !loadConfigFile(confFile).contains(root.getSecret()))) {
saveConfigFile(confFile, Map.of("accessKey", root.getId(), "secretKey", root.getSecret()));
}
} catch (IOException ex) {
logger.error(null, ex);
}
}

if (Para.getConfig().healthCheckEnabled() && scheduledHealthCheck == null) {
scheduledHealthCheck = Para.getScheduledExecutorService().
scheduleAtFixedRate(this, 30, Para.getConfig().healthCheckInvervalSec(), TimeUnit.SECONDS);
}
}

private void saveConfigFile(String confFile, Map<String, String> rootAppCredentials) {
if (rootAppCredentials.containsKey("secretKey")) {
String confString = "";
try {
confString = loadConfigFile(confFile);
} catch (IOException e) {
logger.info("Initialized root app with access key '{}' and secret '{}', "
+ "but could not write these to {}.",
rootAppCredentials.get("accessKey"), rootAppCredentials.get("secretKey"), confFile);
}
String accessKey = "para.root_access_key = \"" + rootAppCredentials.get("accessKey") + "\"";
String secretKey = "para.root_secret_key = \"" + rootAppCredentials.get("secretKey") + "\"";
if (confString.contains("para.root_access_key")) {
confString = confString.replaceAll("para\\.root_access_key\\s*=\\s*\".*?\"", accessKey);
} else {
confString += "\n" + accessKey;
}
if (confString.contains("para.root_secret_key")) {
confString = confString.replaceAll("para\\.root_secret_key\\s*=\\s*\".*?\"", secretKey);
} else {
confString += "\n" + secretKey;
}
Para.getFileStore().store(confFile, new ByteArrayInputStream(confString.
getBytes(StandardCharsets.UTF_8)));
logger.info("Saved root app credentials to {}.", confFile);
} else {
logger.warn("Server is unhealthy - failed to initialize root app. Open http://localhost:" +
Para.getConfig().serverPort() + "/v1/_setup in the browser to initialize Para manually.");
}
}

private String loadConfigFile(String confFile) throws IOException {
try (InputStream ref = getClass().getClassLoader().getResourceAsStream("reference.conf");
InputStream config = Optional.ofNullable(Para.getFileStore().load(confFile)).orElse(ref)) {
return new String(config.readAllBytes(), StandardCharsets.UTF_8);
}
}

@Override
public void run() {
performHealthCheck();
Expand Down

0 comments on commit e579d7c

Please sign in to comment.