Merge pull request #325 from Frooodle/testversions

test dynamic security
This commit is contained in:
Anthony Stirling 2023-08-27 13:47:19 +01:00 committed by GitHub
commit 0f8ab20db7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 211 additions and 110 deletions

View file

@ -21,6 +21,8 @@ jobs:
- uses: gradle/gradle-build-action@v2.4.2 - uses: gradle/gradle-build-action@v2.4.2
env:
ENABLE_SECURITY: false
with: with:
gradle-version: 7.6 gradle-version: 7.6
arguments: clean build arguments: clean build
@ -77,6 +79,8 @@ jobs:
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-args:
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8 platforms: linux/amd64,linux/arm64/v8
@ -105,6 +109,8 @@ jobs:
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
tags: ${{ steps.meta2.outputs.tags }} tags: ${{ steps.meta2.outputs.tags }}
labels: ${{ steps.meta2.outputs.labels }} labels: ${{ steps.meta2.outputs.labels }}
build-args:
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
platforms: linux/amd64,linux/arm64/v8 platforms: linux/amd64,linux/arm64/v8

View file

@ -1,10 +1,20 @@
name: Release Artifacts name: Release Artifacts
on: on:
release: release:
types: [created] types: [created]
jobs: jobs:
push: push:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
enable_security: [true, false]
include:
- enable_security: true
file_suffix: '-with-login'
- enable_security: false
file_suffix: ''
steps: steps:
- uses: actions/checkout@v3.5.2 - uses: actions/checkout@v3.5.2
@ -17,15 +27,17 @@ jobs:
- name: Grant execute permission for gradlew - name: Grant execute permission for gradlew
run: chmod +x gradlew run: chmod +x gradlew
- name: Generate jar - name: Generate jar (With Security=${{ matrix.enable_security }})
run: ./gradlew clean createExe run: ./gradlew clean createExe
env:
ENABLE_SECURITY: ${{ matrix.enable_security }}
- name: Upload binaries to release - name: Upload binaries to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./build/launch4j/Stirling-PDF.exe file: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe
asset_name: Stirling-PDF.exe asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe
tag: ${{ github.ref }} tag: ${{ github.ref }}
overwrite: true overwrite: true
@ -33,13 +45,11 @@ jobs:
id: versionNumber id: versionNumber
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)" run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
- name: Upload binaries to release - name: Upload jar binaries to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar file: ./build/libs/Stirling-PDF-${{ matrix.file_suffix }}-${{ steps.versionNumber.outputs.versionNumber }}.jar
asset_name: Stirling-PDF.jar asset_name: Stirling-PDF${{ matrix.file_suffix }}.jar
tag: ${{ github.ref }} tag: ${{ github.ref }}
overwrite: true overwrite: true

View file

@ -1,6 +1,15 @@
# Build jbig2enc in a separate stage # Build jbig2enc in a separate stage
FROM frooodle/stirling-pdf-base:beta4 FROM frooodle/stirling-pdf-base:beta4
ARG VERSION_TAG
ENV VERSION_TAG=$VERSION_TAG
ENV ENABLE_SECURITY=false
ARG ALPHA=false
ENV ALPHA=$ALPHA
# Create scripts folder and copy local scripts # Create scripts folder and copy local scripts
RUN mkdir /scripts RUN mkdir /scripts
COPY ./scripts/* /scripts/ COPY ./scripts/* /scripts/
@ -11,16 +20,23 @@ COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/ COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
RUN fc-cache -f -v RUN fc-cache -f -v
# Copy the application JAR file # Depending on the ENABLE_SECURITY flag, download the correct JAR
COPY build/libs/*.jar app.jar COPY build/libs/*.jar app-temp.jar
RUN if [ "$ALPHA" = "true" ]; then \
mv app-temp.jar app.jar; \
elif [ "$ENABLE_SECURITY" = "true" ]; then \
wget -O app.jar https://github.com/Frooodle/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login-$VERSION_TAG.jar; \
rm -f app-temp.jar; \
else \
wget -O app.jar https://github.com/Frooodle/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-$VERSION_TAG.jar; \
rm -f app-temp.jar; \
fi
# Expose the application port # Expose the application port
EXPOSE 8080 EXPOSE 8080
# Set environment variables # Set environment variables
ENV APP_HOME_NAME="Stirling PDF" ENV APP_HOME_NAME="Stirling PDF"
#ENV APP_HOME_DESCRIPTION="Personal PDF Website!"
#ENV APP_NAVBAR_NAME="Stirling PDF"
# Run the application # Run the application
RUN chmod +x /scripts/init.sh RUN chmod +x /scripts/init.sh

View file

@ -13,11 +13,14 @@ RUN apt-get update && \
# Copy the application JAR file # Copy the application JAR file
COPY build/libs/*.jar app.jar COPY build/libs/*.jar app.jar
# Expose the application port # Expose the application port
EXPOSE 8080 EXPOSE 8080
# Set environment variables # Set environment variables
ENV GROUPS_TO_REMOVE=Python,OpenCV,OCRmyPDF ENV GROUPS_TO_REMOVE=Python,OpenCV,OCRmyPDF
ENV ENABLE_SECURITY=false
# Run the application # Run the application
CMD ["java", "-jar", "/app.jar"] CMD ["java", "-jar", "/app.jar"]

View file

@ -7,8 +7,11 @@ COPY build/libs/*.jar app.jar
# Expose the application port # Expose the application port
EXPOSE 8080 EXPOSE 8080
# Set environment variables # Set environment variables
ENV GROUPS_TO_REMOVE=CLI ENV GROUPS_TO_REMOVE=CLI
ENV ENABLE_SECURITY=false
# Run the application # Run the application
CMD ["java", "-jar", "/app.jar"] CMD ["java", "-jar", "/app.jar"]

View file

@ -15,6 +15,24 @@ repositories {
mavenCentral() mavenCentral()
} }
sourceSets {
main {
java {
if (System.getenv('ENABLE_SECURITY') == 'false') {
exclude 'stirling/software/SPDF/config/security/**'
exclude 'stirling/software/SPDF/controller/api/UserController.java'
exclude 'stirling/software/SPDF/controller/web/AccountWebController.java'
exclude 'stirling/software/SPDF/model/ApiKeyAuthenticationToken.java'
exclude 'stirling/software/SPDF/model/Authority.java'
exclude 'stirling/software/SPDF/model/PersistentLogin.java'
exclude 'stirling/software/SPDF/model/User.java'
exclude 'stirling/software/SPDF/repository/**'
}
}
}
}
openApi { openApi {
apiDocsUrl = "http://localhost:8080/v3/api-docs" apiDocsUrl = "http://localhost:8080/v3/api-docs"
outputDir = file("$projectDir") outputDir = file("$projectDir")
@ -48,11 +66,17 @@ dependencies {
implementation 'org.yaml:snakeyaml:2.1' implementation 'org.yaml:snakeyaml:2.1'
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.2' implementation 'org.springframework.boot:spring-boot-starter-web:3.1.2'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.1.2' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.1.2'
implementation 'org.springframework.boot:spring-boot-starter-security:3.1.2'
if (System.getenv('ENABLE_SECURITY') != 'false') {
implementation 'org.springframework.boot:spring-boot-starter-security:3.1.2'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
implementation "com.h2database:h2"
}
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.2' testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.2'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
implementation "com.h2database:h2"
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio // https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4' implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'

View file

@ -8,15 +8,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import stirling.software.SPDF.config.ConfigInitializer; import stirling.software.SPDF.config.ConfigInitializer;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
@SpringBootApplication @SpringBootApplication
@EnableWebSecurity()
@EnableGlobalMethodSecurity(prePostEnabled = true)
//@EnableScheduling //@EnableScheduling
public class SPdfApplication { public class SPdfApplication {

View file

@ -11,11 +11,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.Role;
@Component @Component
public class InitialSetup { public class InitialSecuritySetup {
@Autowired @Autowired
private UserService userService; private UserService userService;

View file

@ -1,6 +1,9 @@
package stirling.software.SPDF.config.security; package stirling.software.SPDF.config.security;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -17,6 +20,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import stirling.software.SPDF.repository.JPATokenRepositoryImpl; import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@Configuration @Configuration
@EnableWebSecurity()
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration { public class SecurityConfiguration {
@Autowired @Autowired

View file

@ -1,4 +1,4 @@
package stirling.software.SPDF.config; package stirling.software.SPDF.config.security;
import java.io.IOException; import java.io.IOException;
import java.time.Duration; import java.time.Duration;

View file

@ -0,0 +1,121 @@
package stirling.software.SPDF.controller.web;
import java.io.IOException;
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.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.UserRepository;
@Controller
@Tag(name = "Account Security", description = "Account Security APIs")
public class AccountWebController {
@GetMapping("/login")
public String login(HttpServletRequest request, Model model, Authentication authentication) {
if (authentication != null && authentication.isAuthenticated()) {
return "redirect:/";
}
if (request.getParameter("error") != null) {
model.addAttribute("error", request.getParameter("error"));
}
if (request.getParameter("logout") != null) {
model.addAttribute("logoutMessage", "You have been logged out.");
}
return "login";
}
@Autowired
private UserRepository userRepository; // Assuming you have a repository for user operations
@Autowired
private UserService userService; // Assuming you have a repository for user operations
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/addUsers")
public String showAddUserForm(Model model) {
List<User> allUsers = userRepository.findAll();
model.addAttribute("users", allUsers);
return "addUsers";
}
@GetMapping("/account")
public String account(HttpServletRequest request, Model model, Authentication authentication) {
if (authentication == null || !authentication.isAuthenticated()) {
return "redirect:/";
}
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
// Cast the principal object to UserDetails
UserDetails userDetails = (UserDetails) principal;
// Retrieve username and other attributes
String username = userDetails.getUsername();
// Fetch user details from the database
Optional<User> user = userRepository.findByUsername(username); // Assuming findByUsername method exists
if (!user.isPresent()) {
// Handle error appropriately
return "redirect:/error"; // Example redirection in case of error
}
// Convert settings map to JSON string
ObjectMapper objectMapper = new ObjectMapper();
String settingsJson;
try {
settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
} catch (JsonProcessingException e) {
// Handle JSON conversion error
e.printStackTrace();
return "redirect:/error"; // Example redirection in case of error
}
// Add attributes to the model
model.addAttribute("username", username);
model.addAttribute("role", user.get().getRolesAsString());
model.addAttribute("settings", settingsJson);
}
} else {
return "redirect:/";
}
return "account";
}
}

View file

@ -9,7 +9,6 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -17,105 +16,20 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.UserRepository;
@Controller @Controller
@Tag(name = "General", description = "General APIs") @Tag(name = "General", description = "General APIs")
public class GeneralWebController { public class GeneralWebController {
@GetMapping("/login")
public String login(HttpServletRequest request, Model model, Authentication authentication) {
if (authentication != null && authentication.isAuthenticated()) {
return "redirect:/";
}
if (request.getParameter("error") != null) {
model.addAttribute("error", request.getParameter("error"));
}
if (request.getParameter("logout") != null) {
model.addAttribute("logoutMessage", "You have been logged out.");
}
return "login";
}
@Autowired
private UserRepository userRepository; // Assuming you have a repository for user operations
@Autowired
private UserService userService; // Assuming you have a repository for user operations
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/addUsers")
public String showAddUserForm(Model model) {
List<User> allUsers = userRepository.findAll();
model.addAttribute("users", allUsers);
return "addUsers";
}
@GetMapping("/account")
public String account(HttpServletRequest request, Model model, Authentication authentication) {
if (authentication == null || !authentication.isAuthenticated()) {
return "redirect:/";
}
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
// Cast the principal object to UserDetails
UserDetails userDetails = (UserDetails) principal;
// Retrieve username and other attributes
String username = userDetails.getUsername();
// Fetch user details from the database
Optional<User> user = userRepository.findByUsername(username); // Assuming findByUsername method exists
if (!user.isPresent()) {
// Handle error appropriately
return "redirect:/error"; // Example redirection in case of error
}
// Convert settings map to JSON string
ObjectMapper objectMapper = new ObjectMapper();
String settingsJson;
try {
settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
} catch (JsonProcessingException e) {
// Handle JSON conversion error
e.printStackTrace();
return "redirect:/error"; // Example redirection in case of error
}
// Add attributes to the model
model.addAttribute("username", username);
model.addAttribute("role", user.get().getRolesAsString());
model.addAttribute("settings", settingsJson);
}
} else {
return "redirect:/";
}
return "account";
}
@GetMapping("/pipeline") @GetMapping("/pipeline")