Correctly displaying no authentication warning #32

This commit is contained in:
Francesco 2023-11-07 15:09:52 +01:00
parent da8fc10414
commit 6eb572f72c
4 changed files with 40 additions and 18 deletions

View File

@ -19,11 +19,8 @@
package tech.ailef.snapadmin.external; package tech.ailef.snapadmin.external;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.HttpURLConnection;
import java.net.http.HttpClient; import java.net.URL;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -33,6 +30,8 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import tech.ailef.snapadmin.external.exceptions.DbAdminException;
/** /**
* Runs at startup to determine if SnapAdmin is protected with authentication. * Runs at startup to determine if SnapAdmin is protected with authentication.
* If this is not the case, it sets a flag that will display a warning in the * If this is not the case, it sets a flag that will display a warning in the
@ -49,30 +48,31 @@ public class StartupAuthCheckRunner {
@Autowired @Autowired
private SnapAdminProperties properties; private SnapAdminProperties properties;
@Bean @Bean
ApplicationListener<ServletWebServerInitializedEvent> serverPortListenerBean() { ApplicationListener<ServletWebServerInitializedEvent> serverPortListenerBean() {
return event -> { return event -> {
int serverPort = event.getWebServer().getPort(); int serverPort = event.getWebServer().getPort();
String url = "http://localhost:" + serverPort + "/" + properties.getBaseUrl(); String link = "http://localhost:" + serverPort + "/" + properties.getBaseUrl();
logger.info("Checking if SnapAdmin is protected with authentication at " + link);
logger.info("Checking if SnapAdmin is protected with authentication at " + url);
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
try { try {
HttpResponse<String> response = HttpClient.newBuilder().build().send(request, BodyHandlers.ofString()); URL url = new URL(link);
int statusCode = response.statusCode(); HttpURLConnection openConnection = (HttpURLConnection)url.openConnection();
openConnection.setInstanceFollowRedirects(false);
int statusCode = openConnection.getResponseCode();
snapAdmin.setAuthenticated(statusCode != 200);
if (statusCode == 200) { if (statusCode == 200) {
logger.warn("It seems SnapAdmin routes are not protected with authentication. The URL " logger.warn("It seems SnapAdmin routes are not protected with authentication. The URL "
+ url + " is publicly accessible: be careful!"); + url + " is publicly accessible: be careful!");
snapAdmin.setAuthenticated(false);
} }
} catch (IOException | InterruptedException e) {
logger.warn("Unable to connect to server at " + url); } catch (IOException e) {
throw new DbAdminException(e);
} }
}; };
} }
} }

View File

@ -126,5 +126,10 @@ public class GlobalController {
return props; return props;
} }
@ModelAttribute("snapadmin_authenticated")
public boolean isAuthenticated() {
return dbAdmin.isAuthenticated();
}
} }

View File

@ -26,6 +26,7 @@ import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import tech.ailef.snapadmin.external.exceptions.DbAdminException;
import tech.ailef.snapadmin.internal.model.UserSetting; import tech.ailef.snapadmin.internal.model.UserSetting;
import tech.ailef.snapadmin.internal.repository.UserSettingsRepository; import tech.ailef.snapadmin.internal.repository.UserSettingsRepository;
@ -47,7 +48,12 @@ public class UserConfiguration {
Optional<UserSetting> setting = repo.findById(settingName); Optional<UserSetting> setting = repo.findById(settingName);
if (setting.isPresent()) if (setting.isPresent())
return setting.get().getSettingValue(); return setting.get().getSettingValue();
return defaultValues().getOrDefault(settingName, ""); String settingDefaultValue = defaultValues().get(settingName);
if (settingDefaultValue == null)
throw new DbAdminException("Trying to access setting `" + settingName + "` but it has no default value");
return settingDefaultValue;
} }
/** /**
@ -57,6 +63,7 @@ public class UserConfiguration {
private Map<String, String> defaultValues() { private Map<String, String> defaultValues() {
Map<String, String> values = new HashMap<>(); Map<String, String> values = new HashMap<>();
values.put("brandName", "SnapAdmin"); values.put("brandName", "SnapAdmin");
values.put("additionalCss", "");
return values; return values;
} }
} }

View File

@ -9,6 +9,16 @@
<div th:replace="~{fragments/resources :: sidebar('entities')}"></div> <div th:replace="~{fragments/resources :: sidebar('entities')}"></div>
<div class="main-content bg-lighter"> <div class="main-content bg-lighter">
<h1 class="fw-bold mb-4"><i class="align-middle bi bi-database"></i><span class="align-middle"> Entities</span></h1> <h1 class="fw-bold mb-4"><i class="align-middle bi bi-database"></i><span class="align-middle"> Entities</span></h1>
<th:block th:if="${!snapadmin_authenticated}">
<div class="alert alert-danger alert-dismissible fade show" role="alert" id="auth-warning">
<h4 class="alert-heading fw-bold"><i class="bi bi-exclamation-triangle"></i> SnapAdmin is not protected</h4>
<p class="mb-0">It appears that you have not enabled security so SnapAdmin is publicly accessible. Please refer to the
documentation to learn how to <a href="https://www.snapadmin.dev/docs/#security">protect SnapAdmin with Spring Security.
</a>
</p>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</th:block>
<form th:action="|/${snapadmin_baseUrl}|" method="GET"> <form th:action="|/${snapadmin_baseUrl}|" method="GET">
<div class="input-group"> <div class="input-group">
<input type="text" th:value="${query}" <input type="text" th:value="${query}"