Skip to content

Commit

Permalink
X-API-key to X-API-KEY
Browse files Browse the repository at this point in the history
  • Loading branch information
Frooodle committed Dec 10, 2024
1 parent c1c3eba commit 58c7d7b
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 69 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ To access your account settings, go to Account Settings in the settings cog menu

To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future.

For API usage, you must provide a header with `X-API-Key` and the associated API key for that user.
For API usage, you must provide a header with `X-API-KEY` and the associated API key for that user.

## FAQ

Expand Down
10 changes: 7 additions & 3 deletions cucumber/features/steps/step_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
import re
from PIL import Image, ImageDraw

API_HEADERS = {
'X-API-KEY': '123456789'
}

#########
# GIVEN #
#########
Expand Down Expand Up @@ -227,15 +231,15 @@ def save_generated_pdf(context, filename):
def step_send_get_request(context, endpoint):
base_url = "http://localhost:8080"
full_url = f"{base_url}{endpoint}"
response = requests.get(full_url)
response = requests.get(full_url, headers=API_HEADERS)
context.response = response

@when('I send a GET request to "{endpoint}" with parameters')
def step_send_get_request_with_params(context, endpoint):
base_url = "http://localhost:8080"
params = {row['parameter']: row['value'] for row in context.table}
full_url = f"{base_url}{endpoint}"
response = requests.get(full_url, params=params)
response = requests.get(full_url, params=params, headers=API_HEADERS)
context.response = response

@when('I send the API request to the endpoint "{endpoint}"')
Expand All @@ -256,7 +260,7 @@ def step_send_api_request(context, endpoint):
print(f"form_data {file.name} with {mime_type}")
form_data.append((key, (file.name, file, mime_type)))

response = requests.post(url, files=form_data)
response = requests.post(url, files=form_data, headers=API_HEADERS)
context.response = response

########
Expand Down
34 changes: 34 additions & 0 deletions exampleYmlFiles/test_cicd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
services:
stirling-pdf:
container_name: Stirling-PDF-Security-Fat
image: stirlingtools/stirling-pdf:latest-fat
deploy:
resources:
limits:
memory: 4G
healthcheck:
test: ["CMD-SHELL", "curl -f -H 'X-API-KEY: 123456789' http://localhost:8080/api/v1/info/status | grep -q 'UP'"]
interval: 5s
timeout: 10s
retries: 16
ports:
- 8080:8080
volumes:
- /stirling/latest/data:/usr/share/tessdata:rw
- /stirling/latest/config:/configs:rw
- /stirling/latest/logs:/logs:rw
environment:
DOCKER_ENABLE_SECURITY: "true"
SECURITY_ENABLELOGIN: "true"
PUID: 1002
PGID: 1002
UMASK: "022"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest-fat with Security
UI_APPNAMENAVBAR: Stirling-PDF Latest-fat
SYSTEM_MAXFILESIZE: "100"
METRICS_ENABLED: "true"
SYSTEM_GOOGLEVISIBILITY: "true"
SECURITY_CUSTOMGLOBALAPIKEY: "123456789"
restart: on-failure:5
49 changes: 24 additions & 25 deletions src/main/java/stirling/software/SPDF/config/InitialSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ public class InitialSetup {
@PostConstruct
public void init() throws IOException {
initUUIDKey();

initSecretKey();

initEnableCSRFSecurity();

initLegalUrls();

initSetAppVersion();
}

public void initUUIDKey() throws IOException {
String uuid = applicationProperties.getAutomaticallyGenerated().getUUID();
if (!GeneralUtils.isValidUUID(uuid)) {
Expand All @@ -57,17 +57,17 @@ public void initSecretKey() throws IOException {
}

public void initEnableCSRFSecurity() throws IOException {
if(GeneralUtils.isVersionHigher("0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled();
if (!csrf) {
GeneralUtils.saveKeyToConfig("security.csrfDisabled", false, false);
GeneralUtils.saveKeyToConfig("system.enableAnalytics", "true", false);
applicationProperties.getSecurity().setCsrfDisabled(false);
}
}
if (GeneralUtils.isVersionHigher(
"0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled();
if (!csrf) {
GeneralUtils.saveKeyToConfig("security.csrfDisabled", false, false);
GeneralUtils.saveKeyToConfig("system.enableAnalytics", "true", false);
applicationProperties.getSecurity().setCsrfDisabled(false);
}
}
}

public void initLegalUrls() throws IOException {
// Initialize Terms and Conditions
String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
Expand All @@ -85,20 +85,19 @@ public void initLegalUrls() throws IOException {
applicationProperties.getLegal().setPrivacyPolicy(defaultPrivacyUrl);
}
}

public void initSetAppVersion() throws IOException {
String appVersion = "0.0.0";
Resource resource = new ClassPathResource("version.properties");

String appVersion = "0.0.0";
Resource resource = new ClassPathResource("version.properties");
Properties props = new Properties();
try {
props.load(resource.getInputStream());
appVersion =props.getProperty("version");
} catch(Exception e) {
appVersion = props.getProperty("version");
} catch (Exception e) {

}
applicationProperties.getAutomaticallyGenerated().setAppVersion(appVersion);
GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.appVersion", appVersion,false);
}

GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.appVersion", appVersion, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,7 @@ private void initializeInternalApiUser() throws IllegalArgumentException, IOExce
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
}
userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey());
System.out.println(applicationProperties.getSecurity().getCustomGlobalAPIKey());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public PasswordEncoder passwordEncoder() {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
if (applicationProperties.getSecurity().getCsrfDisabled()) {
if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) {
http.csrf(csrf -> csrf.disable());
}

Expand All @@ -116,7 +116,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
csrf ->
csrf.ignoringRequestMatchers(
request -> {
String apiKey = request.getHeader("X-API-Key");
String apiKey = request.getHeader("X-API-KEY");

// If there's no API key, don't ignore CSRF
// (return false)
Expand Down Expand Up @@ -289,17 +289,17 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
}

} else {
if (!applicationProperties.getSecurity().getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo =
CookieCsrfTokenRepository.withHttpOnlyFalse();
CsrfTokenRequestAttributeHandler requestHandler =
new CsrfTokenRequestAttributeHandler();
requestHandler.setCsrfRequestAttributeName(null);
http.csrf(
csrf ->
csrf.csrfTokenRepository(cookieRepo)
.csrfTokenRequestHandler(requestHandler));
}
// if (!applicationProperties.getSecurity().getCsrfDisabled()) {
// CookieCsrfTokenRepository cookieRepo =
// CookieCsrfTokenRepository.withHttpOnlyFalse();
// CsrfTokenRequestAttributeHandler requestHandler =
// new CsrfTokenRequestAttributeHandler();
// requestHandler.setCsrfRequestAttributeName(null);
// http.csrf(
// csrf ->
// csrf.csrfTokenRepository(cookieRepo)
// .csrfTokenRequestHandler(requestHandler));
// }
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ protected void doFilterInternal(

// Check for API key in the request headers if no authentication exists
if (authentication == null || !authentication.isAuthenticated()) {
String apiKey = request.getHeader("X-API-Key");
String apiKey = request.getHeader("X-API-KEY");
if (apiKey != null && !apiKey.trim().isEmpty()) {
try {
// Use API key to authenticate. This requires you to have an authentication
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected void doFilterInternal(
String identifier = null;

// Check for API key in the request headers
String apiKey = request.getHeader("X-API-Key");
String apiKey = request.getHeader("X-API-KEY");
if (apiKey != null && !apiKey.trim().isEmpty()) {
identifier =
"API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames
Expand All @@ -79,7 +79,7 @@ protected void doFilterInternal(
Role userRole =
getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication());

if (request.getHeader("X-API-Key") != null) {
if (request.getHeader("X-API-KEY") != null) {
// It's an API call
processRequest(
userRole.getApiCallsPerDay(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,37 @@ public String getCurrentUsername() {
}
}

@Transactional
public void syncCustomApiUser(String customApiKey) throws IOException {
if (customApiKey == null || customApiKey.trim().length() == 0) {
return;
}
String username = "CUSTOM_API_USER";
Optional<User> existingUser = findByUsernameIgnoreCase(username);

if (!existingUser.isPresent()) {
// Create new user with API role
User user = new User();
user.setUsername(username);
user.setPassword(UUID.randomUUID().toString());
user.setEnabled(true);
user.setFirstLogin(false);
user.setAuthenticationType(AuthenticationType.WEB);
user.setApiKey(customApiKey);
user.addAuthority(new Authority(Role.INTERNAL_API_USER.getRoleId(), user));
userRepository.save(user);
databaseBackupHelper.exportDatabase();
} else {
// Update API key if it has changed
User user = existingUser.get();
if (!customApiKey.equals(user.getApiKey())) {
user.setApiKey(customApiKey);
userRepository.save(user);
databaseBackupHelper.exportDatabase();
}
}
}

@Override
public long getTotalUsersCount() {
return userRepository.count();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ private ResponseEntity<byte[]> sendWebRequest(String url, MultiValueMap<String,

HttpHeaders headers = new HttpHeaders();
String apiKey = getApiKeyForUser();
headers.add("X-API-Key", apiKey);
headers.add("X-API-KEY", apiKey);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);

// Create HttpEntity with the body and headers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public static class Security {
private int loginAttemptCount;
private long loginResetTimeMinutes;
private String loginMethod = "all";
private String customGlobalAPIKey;

public Boolean isAltLogin() {
return saml2.getEnabled() || oauth2.getEnabled();
Expand Down
42 changes: 20 additions & 22 deletions src/main/java/stirling/software/SPDF/utils/GeneralUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,10 @@ public static boolean isValidUUID(String uuid) {
public static void saveKeyToConfig(String id, String key) throws IOException {
saveKeyToConfig(id, key, true);
}

public static void saveKeyToConfig(String id, boolean key) throws IOException {
saveKeyToConfig(id, key, true);
}


public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
throws IOException {
Expand All @@ -310,25 +310,24 @@ public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
}
settingsYml.save();
}

public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated)
throws IOException {
Path path = Paths.get("configs", "settings.yml");

final YamlFile settingsYml = new YamlFile(path.toFile());
DumperOptions yamlOptionssettingsYml =
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
yamlOptionssettingsYml.setSplitLines(false);

settingsYml.loadWithComments();

YamlFileWrapper writer = settingsYml.path(id).set(key);
if (autoGenerated) {
writer.comment("# Automatically Generated Settings (Do Not Edit Directly)");
}
settingsYml.save();
}


public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated)
throws IOException {
Path path = Paths.get("configs", "settings.yml");

final YamlFile settingsYml = new YamlFile(path.toFile());
DumperOptions yamlOptionssettingsYml =
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
yamlOptionssettingsYml.setSplitLines(false);

settingsYml.loadWithComments();

YamlFileWrapper writer = settingsYml.path(id).set(key);
if (autoGenerated) {
writer.comment("# Automatically Generated Settings (Do Not Edit Directly)");
}
settingsYml.save();
}

public static String generateMachineFingerprint() {
try {
Expand Down Expand Up @@ -372,7 +371,7 @@ public static String generateMachineFingerprint() {
return "GenericID";
}
}

public static boolean isVersionHigher(String currentVersion, String compareVersion) {
if (currentVersion == null || compareVersion == null) {
return false;
Expand Down Expand Up @@ -401,5 +400,4 @@ public static boolean isVersionHigher(String currentVersion, String compareVersi
// If all components so far are equal, the longer version is considered higher
return current.length > compare.length;
}

}
2 changes: 1 addition & 1 deletion test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ main() {
# run_tests "Stirling-PDF-Security" "./exampleYmlFiles/docker-compose-latest-security.yml"
# docker-compose -f "./exampleYmlFiles/docker-compose-latest-security.yml" down

run_tests "Stirling-PDF-Security-Fat" "./exampleYmlFiles/docker-compose-latest-fat-security.yml"
run_tests "Stirling-PDF-Security-Fat" "./exampleYmlFiles/test_cicd.yml"
if [ $? -eq 0 ]; then
cd cucumber
if python -m behave; then
Expand Down

0 comments on commit 58c7d7b

Please sign in to comment.