mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 12:19:24 +00:00
Fixing logout
This commit is contained in:
parent
07dcce6a3c
commit
29d6ca4f35
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -147,6 +147,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate OpenAPI documentation
|
- name: Generate OpenAPI documentation
|
||||||
run: ./gradlew :stirling-pdf:generateOpenApiDocs
|
run: ./gradlew :stirling-pdf:generateOpenApiDocs
|
||||||
|
env:
|
||||||
|
DISABLE_ADDITIONAL_FEATURES: true
|
||||||
|
|
||||||
- name: Upload OpenAPI Documentation
|
- name: Upload OpenAPI Documentation
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
@ -46,7 +46,7 @@ public class InstallationPathConfig {
|
|||||||
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
|
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
|
||||||
TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator;
|
TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator;
|
||||||
SIGNATURES_PATH = CUSTOM_FILES_PATH + "signatures" + File.separator;
|
SIGNATURES_PATH = CUSTOM_FILES_PATH + "signatures" + File.separator;
|
||||||
PRIVATE_KEY_PATH = CUSTOM_FILES_PATH + "keys" + File.separator;
|
PRIVATE_KEY_PATH = CONFIG_PATH + "keys" + File.separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String initializeBasePath() {
|
private static String initializeBasePath() {
|
||||||
|
@ -304,8 +304,8 @@ public class ApplicationProperties {
|
|||||||
private boolean enableKeystore = true;
|
private boolean enableKeystore = true;
|
||||||
private boolean enableKeyRotation = false;
|
private boolean enableKeyRotation = false;
|
||||||
private boolean enableKeyCleanup = true;
|
private boolean enableKeyCleanup = true;
|
||||||
private int keyRetentionDays;
|
private int keyRetentionDays = 7;
|
||||||
private int cleanupBatchSize;
|
private int cleanupBatchSize = 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ security:
|
|||||||
google:
|
google:
|
||||||
clientId: '' # client ID for Google OAuth2
|
clientId: '' # client ID for Google OAuth2
|
||||||
clientSecret: '' # client secret for Google OAuth2
|
clientSecret: '' # client secret for Google OAuth2
|
||||||
scopes: https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile # scopes for Google OAuth2
|
scopes: email, profile # scopes for Google OAuth2
|
||||||
useAsUsername: email # field to use as the username for Google OAuth2. Available options are: [email | name | given_name | family_name]
|
useAsUsername: email # field to use as the username for Google OAuth2. Available options are: [email | name | given_name | family_name]
|
||||||
github:
|
github:
|
||||||
clientId: '' # client ID for GitHub OAuth2
|
clientId: '' # client ID for GitHub OAuth2
|
||||||
@ -51,20 +51,21 @@ security:
|
|||||||
provider: '' # The name of your Provider
|
provider: '' # The name of your Provider
|
||||||
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users
|
||||||
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
|
||||||
registrationId: stirlingpdf-dario-saml # The name of your Service Provider (SP) app name. Should match the name in the path for your SSO & SLO URLs
|
registrationId: stirling # The name of your Service Provider (SP) app name. Should match the name in the path for your SSO & SLO URLs
|
||||||
idpMetadataUri: https://authentik.dev.stirlingpdf.com/api/v3/providers/saml/5/metadata/ # The uri for your Provider's metadata
|
idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata # The uri for your Provider's metadata
|
||||||
idpSingleLoginUrl: https://authentik.dev.stirlingpdf.com/application/saml/stirlingpdf-dario-saml/sso/binding/post/ # The URL for initiating SSO. Provided by your Provider
|
idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml # The URL for initiating SSO. Provided by your Provider
|
||||||
idpSingleLogoutUrl: https://authentik.dev.stirlingpdf.com/application/saml/stirlingpdf-dario-saml/slo/binding/post/ # The URL for initiating SLO. Provided by your Provider
|
idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml # The URL for initiating SLO. Provided by your Provider
|
||||||
idpIssuer: authentik # The ID of your Provider
|
idpIssuer: '' # The ID of your Provider
|
||||||
idpCert: classpath:authentik-Self-signed_Certificate_certificate.pem # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider
|
idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider
|
||||||
privateKey: classpath:private_key.key # Your private key. Generated from your keypair
|
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
||||||
spCert: classpath:certificate.crt # Your signing certificate. Generated from your keypair
|
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
||||||
jwt:
|
jwt:
|
||||||
enableKeyStore: true # Set to 'true' to enable JWT key store
|
persistence: true # Set to 'true' to enable JWT key store
|
||||||
enableKeyRotation: true # Set to 'true' to enable JWT key rotation
|
enableKeyRotation: true # Set to 'true' to enable key pair rotation
|
||||||
enableKeyCleanup: true # Set to 'true' to enable JWT key cleanup
|
enableKeyCleanup: true # Set to 'true' to enable key pair cleanup
|
||||||
keyRetentionDays: 7 # Number of days to retain old keys
|
keyRetentionDays: 7 # Number of days to retain old keys. The default is 7 days.
|
||||||
cleanupBatchSize: 100 # Number of keys to clean up in each batch
|
cleanupBatchSize: 100 # Number of keys to clean up in each batch. The default is 100.
|
||||||
|
secureCookie: false # Set to 'true' to use secure cookies for JWTs
|
||||||
|
|
||||||
premium:
|
premium:
|
||||||
key: 00000000-0000-0000-0000-000000000000
|
key: 00000000-0000-0000-0000-000000000000
|
||||||
|
@ -62,11 +62,15 @@ window.JWTManager = {
|
|||||||
fetch('/logout', {
|
fetch('/logout', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
}).then(() => {
|
}).then(response => {
|
||||||
window.location.href = '/login';
|
if (response.redirected) {
|
||||||
|
window.location.href = response.url;
|
||||||
|
} else {
|
||||||
|
window.location.href = '/login?logout=true';
|
||||||
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Even if logout fails, redirect to login
|
// If logout fails, let server handle it
|
||||||
window.location.href = '/login';
|
window.location.href = '/logout';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -47,11 +47,14 @@
|
|||||||
console.log('User is not authenticated or token expired');
|
console.log('User is not authenticated or token expired');
|
||||||
// Only redirect to login if we're not already on login/register pages
|
// Only redirect to login if we're not already on login/register pages
|
||||||
const currentPath = window.location.pathname;
|
const currentPath = window.location.pathname;
|
||||||
|
const currentSearch = window.location.search;
|
||||||
|
// Don't redirect if we're on logout page or already being logged out
|
||||||
if (!currentPath.includes('/login') &&
|
if (!currentPath.includes('/login') &&
|
||||||
!currentPath.includes('/register') &&
|
!currentPath.includes('/register') &&
|
||||||
!currentPath.includes('/oauth') &&
|
!currentPath.includes('/oauth') &&
|
||||||
!currentPath.includes('/saml') &&
|
!currentPath.includes('/saml') &&
|
||||||
!currentPath.includes('/error')) {
|
!currentPath.includes('/error') &&
|
||||||
|
!currentSearch.includes('logout=true')) {
|
||||||
// Redirect to login after a short delay to allow other scripts to load
|
// Redirect to login after a short delay to allow other scripts to load
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
|
@ -43,7 +43,6 @@ public class InitialSecuritySetup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userService.migrateOauth2ToSSO();
|
|
||||||
assignUsersToDefaultTeamIfMissing();
|
assignUsersToDefaultTeamIfMissing();
|
||||||
initializeInternalApiUser();
|
initializeInternalApiUser();
|
||||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||||
|
@ -77,8 +77,11 @@ public class AccountWebController {
|
|||||||
|
|
||||||
@GetMapping("/login")
|
@GetMapping("/login")
|
||||||
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
||||||
// If the user is already authenticated, redirect them to the home page.
|
// If the user is already authenticated and it's not a logout scenario, redirect them to the
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
// home page.
|
||||||
|
if (authentication != null
|
||||||
|
&& authentication.isAuthenticated()
|
||||||
|
&& request.getParameter("logout") == null) {
|
||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ public class SecurityConfiguration {
|
|||||||
// Configure session management based on JWT setting
|
// Configure session management based on JWT setting
|
||||||
http.sessionManagement(
|
http.sessionManagement(
|
||||||
sessionManagement -> {
|
sessionManagement -> {
|
||||||
if (v2Enabled && !securityProperties.isSaml2Active()) {
|
if (v2Enabled) {
|
||||||
sessionManagement.sessionCreationPolicy(
|
sessionManagement.sessionCreationPolicy(
|
||||||
SessionCreationPolicy.STATELESS);
|
SessionCreationPolicy.STATELESS);
|
||||||
} else {
|
} else {
|
||||||
|
@ -69,10 +69,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String jwtToken = jwtService.extractToken(request);
|
String jwtToken = jwtService.extractToken(request);
|
||||||
|
// todo: X-API-KEY
|
||||||
if (jwtToken == null) {
|
if (jwtToken == null) {
|
||||||
// If they are unauthenticated and navigating to '/', redirect to '/login' instead of
|
// If they are unauthenticated and navigating to '/', redirect to '/login' instead of
|
||||||
// sending a 401
|
// sending a 401
|
||||||
|
// todo: any unauthenticated requests should redirect to login
|
||||||
if ("/".equals(request.getRequestURI())
|
if ("/".equals(request.getRequestURI())
|
||||||
&& "GET".equalsIgnoreCase(request.getMethod())) {
|
&& "GET".equalsIgnoreCase(request.getMethod())) {
|
||||||
response.sendRedirect("/login");
|
response.sendRedirect("/login");
|
||||||
@ -130,11 +131,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
|
|
||||||
log.info(
|
|
||||||
"JWT authentication successful for user: {} - Authentication set in SecurityContext",
|
|
||||||
username);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new UsernameNotFoundException("User not found: " + username);
|
throw new UsernameNotFoundException("User not found: " + username);
|
||||||
}
|
}
|
||||||
|
@ -65,13 +65,6 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
|
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
log.info(
|
|
||||||
"UserAuthenticationFilter - Authentication from SecurityContext: {}",
|
|
||||||
authentication != null
|
|
||||||
? authentication.getClass().getSimpleName()
|
|
||||||
+ " for "
|
|
||||||
+ authentication.getName()
|
|
||||||
: "null");
|
|
||||||
|
|
||||||
// Check for session expiration (unsure if needed)
|
// Check for session expiration (unsure if needed)
|
||||||
// if (authentication != null && authentication.isAuthenticated()) {
|
// if (authentication != null && authentication.isAuthenticated()) {
|
||||||
@ -117,7 +110,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
String method = request.getMethod();
|
String method = request.getMethod();
|
||||||
String contextPath = request.getContextPath();
|
String contextPath = request.getContextPath();
|
||||||
|
|
||||||
if ("GET".equalsIgnoreCase(method) && !(contextPath + "/login").equals(requestURI)) {
|
if ("GET".equalsIgnoreCase(method) && !requestURI.startsWith(contextPath + "/login")) {
|
||||||
response.sendRedirect(contextPath + "/login"); // redirect to the login page
|
response.sendRedirect(contextPath + "/login"); // redirect to the login page
|
||||||
} else {
|
} else {
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
|
@ -2,7 +2,8 @@ package stirling.software.proprietary.security.model;
|
|||||||
|
|
||||||
public enum AuthenticationType {
|
public enum AuthenticationType {
|
||||||
WEB,
|
WEB,
|
||||||
@Deprecated(since = "1.0.2") SSO,
|
@Deprecated(since = "1.0.2")
|
||||||
|
SSO,
|
||||||
OAUTH2,
|
OAUTH2,
|
||||||
SAML2
|
SAML2
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ public class CustomSaml2AuthenticationSuccessHandler
|
|||||||
username, saml2Properties.getAutoCreateUser(), SAML2);
|
username, saml2Properties.getAutoCreateUser(), SAML2);
|
||||||
log.debug("Successfully processed authentication for user: {}", username);
|
log.debug("Successfully processed authentication for user: {}", username);
|
||||||
|
|
||||||
generateJWT(response, authentication);
|
generateJwt(response, authentication);
|
||||||
response.sendRedirect(contextPath + "/");
|
response.sendRedirect(contextPath + "/");
|
||||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||||
log.debug(
|
log.debug(
|
||||||
@ -136,7 +136,7 @@ public class CustomSaml2AuthenticationSuccessHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateJWT(HttpServletResponse response, Authentication authentication) {
|
private void generateJwt(HttpServletResponse response, Authentication authentication) {
|
||||||
if (jwtService.isJwtEnabled()) {
|
if (jwtService.isJwtEnabled()) {
|
||||||
String jwt =
|
String jwt =
|
||||||
jwtService.generateToken(
|
jwtService.generateToken(
|
||||||
|
@ -10,6 +10,7 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseCookie;
|
import org.springframework.http.ResponseCookie;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@ -41,15 +42,17 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||||
private static final String BEARER_PREFIX = "Bearer ";
|
private static final String BEARER_PREFIX = "Bearer ";
|
||||||
private static final String ISSUER = "Stirling PDF";
|
private static final String ISSUER = "Stirling PDF";
|
||||||
private static final long EXPIRATION = 300000; // 5 minutes in milliseconds
|
private static final long EXPIRATION = 3600000;
|
||||||
|
|
||||||
private final JwtKeystoreServiceInterface keystoreService;
|
@Value("${stirling.security.jwt.secureCookie:true}")
|
||||||
|
private boolean secureCookie;
|
||||||
|
|
||||||
|
private final KeystoreServiceInterface keystoreService;
|
||||||
private final boolean v2Enabled;
|
private final boolean v2Enabled;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public JwtService(
|
public JwtService(
|
||||||
@Qualifier("v2Enabled") boolean v2Enabled,
|
@Qualifier("v2Enabled") boolean v2Enabled, KeystoreServiceInterface keystoreService) {
|
||||||
JwtKeystoreServiceInterface keystoreService) {
|
|
||||||
this.v2Enabled = v2Enabled;
|
this.v2Enabled = v2Enabled;
|
||||||
this.keystoreService = keystoreService;
|
this.keystoreService = keystoreService;
|
||||||
}
|
}
|
||||||
@ -127,13 +130,13 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
|
|
||||||
private Claims extractAllClaims(String token) {
|
private Claims extractAllClaims(String token) {
|
||||||
try {
|
try {
|
||||||
// Extract key ID from token header if present
|
|
||||||
String keyId = extractKeyId(token);
|
String keyId = extractKeyId(token);
|
||||||
KeyPair keyPair;
|
KeyPair keyPair;
|
||||||
|
|
||||||
if (keyId != null) {
|
if (keyId != null) {
|
||||||
log.debug("Looking up key pair for key ID: {}", keyId);
|
log.debug("Looking up key pair for key ID: {}", keyId);
|
||||||
Optional<KeyPair> specificKeyPair = keystoreService.getKeyPairByKeyId(keyId);
|
Optional<KeyPair> specificKeyPair =
|
||||||
|
keystoreService.getKeyPairByKeyId(keyId); // todo: move to in-memory cache
|
||||||
|
|
||||||
if (specificKeyPair.isPresent()) {
|
if (specificKeyPair.isPresent()) {
|
||||||
keyPair = specificKeyPair.get();
|
keyPair = specificKeyPair.get();
|
||||||
@ -179,13 +182,8 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String extractToken(HttpServletRequest request) {
|
public String extractToken(HttpServletRequest request) {
|
||||||
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
|
|
||||||
|
|
||||||
if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) {
|
|
||||||
return authHeader.substring(BEARER_PREFIX.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
Cookie[] cookies = request.getCookies();
|
Cookie[] cookies = request.getCookies();
|
||||||
|
|
||||||
if (cookies != null) {
|
if (cookies != null) {
|
||||||
for (Cookie cookie : cookies) {
|
for (Cookie cookie : cookies) {
|
||||||
if (JWT_COOKIE_NAME.equals(cookie.getName())) {
|
if (JWT_COOKIE_NAME.equals(cookie.getName())) {
|
||||||
@ -204,8 +202,8 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
ResponseCookie cookie =
|
ResponseCookie cookie =
|
||||||
ResponseCookie.from(JWT_COOKIE_NAME, Newlines.stripAll(token))
|
ResponseCookie.from(JWT_COOKIE_NAME, Newlines.stripAll(token))
|
||||||
.httpOnly(true)
|
.httpOnly(true)
|
||||||
.secure(true)
|
.secure(secureCookie)
|
||||||
.sameSite("None")
|
.sameSite("Strict")
|
||||||
.maxAge(EXPIRATION / 1000)
|
.maxAge(EXPIRATION / 1000)
|
||||||
.path("/")
|
.path("/")
|
||||||
.build();
|
.build();
|
||||||
@ -220,7 +218,7 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
ResponseCookie cookie =
|
ResponseCookie cookie =
|
||||||
ResponseCookie.from(JWT_COOKIE_NAME, "")
|
ResponseCookie.from(JWT_COOKIE_NAME, "")
|
||||||
.httpOnly(true)
|
.httpOnly(true)
|
||||||
.secure(true)
|
.secure(secureCookie)
|
||||||
.sameSite("None")
|
.sameSite("None")
|
||||||
.maxAge(0)
|
.maxAge(0)
|
||||||
.path("/")
|
.path("/")
|
||||||
|
@ -10,12 +10,15 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.configuration.InstallationPathConfig;
|
import stirling.software.common.configuration.InstallationPathConfig;
|
||||||
@ -25,16 +28,17 @@ import stirling.software.proprietary.security.model.JwtSigningKey;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class JwtKeyCleanupService {
|
@ConditionalOnBooleanProperty("v2")
|
||||||
|
public class KeyPairCleanupService {
|
||||||
|
|
||||||
private final JwtSigningKeyRepository signingKeyRepository;
|
private final JwtSigningKeyRepository signingKeyRepository;
|
||||||
private final JwtKeystoreService keystoreService;
|
private final KeystoreService keystoreService;
|
||||||
private final ApplicationProperties.Security.Jwt jwtProperties;
|
private final ApplicationProperties.Security.Jwt jwtProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public JwtKeyCleanupService(
|
public KeyPairCleanupService(
|
||||||
JwtSigningKeyRepository signingKeyRepository,
|
JwtSigningKeyRepository signingKeyRepository,
|
||||||
JwtKeystoreService keystoreService,
|
KeystoreService keystoreService,
|
||||||
ApplicationProperties applicationProperties) {
|
ApplicationProperties applicationProperties) {
|
||||||
this.signingKeyRepository = signingKeyRepository;
|
this.signingKeyRepository = signingKeyRepository;
|
||||||
this.keystoreService = keystoreService;
|
this.keystoreService = keystoreService;
|
||||||
@ -42,6 +46,7 @@ public class JwtKeyCleanupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@PostConstruct
|
||||||
@Scheduled(fixedDelay = 24, timeUnit = TimeUnit.HOURS)
|
@Scheduled(fixedDelay = 24, timeUnit = TimeUnit.HOURS)
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
if (!jwtProperties.isEnableKeyCleanup() || !keystoreService.isKeystoreEnabled()) {
|
if (!jwtProperties.isEnableKeyCleanup() || !keystoreService.isKeystoreEnabled()) {
|
||||||
@ -111,7 +116,7 @@ public class JwtKeyCleanupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Path privateKeyDirectory = Paths.get(InstallationPathConfig.getPrivateKeyPath());
|
Path privateKeyDirectory = Paths.get(InstallationPathConfig.getPrivateKeyPath());
|
||||||
Path keyFile = privateKeyDirectory.resolve(keyId + JwtKeystoreService.KEY_SUFFIX);
|
Path keyFile = privateKeyDirectory.resolve(keyId + KeystoreService.KEY_SUFFIX);
|
||||||
|
|
||||||
if (Files.exists(keyFile)) {
|
if (Files.exists(keyFile)) {
|
||||||
Files.delete(keyFile);
|
Files.delete(keyFile);
|
@ -33,7 +33,7 @@ import stirling.software.proprietary.security.model.JwtSigningKey;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class JwtKeystoreService implements JwtKeystoreServiceInterface {
|
public class KeystoreService implements KeystoreServiceInterface {
|
||||||
|
|
||||||
public static final String KEY_SUFFIX = ".key";
|
public static final String KEY_SUFFIX = ".key";
|
||||||
private final JwtSigningKeyRepository signingKeyRepository;
|
private final JwtSigningKeyRepository signingKeyRepository;
|
||||||
@ -43,7 +43,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
|
|||||||
private volatile String currentKeyId;
|
private volatile String currentKeyId;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public JwtKeystoreService(
|
public KeystoreService(
|
||||||
JwtSigningKeyRepository signingKeyRepository,
|
JwtSigningKeyRepository signingKeyRepository,
|
||||||
ApplicationProperties applicationProperties) {
|
ApplicationProperties applicationProperties) {
|
||||||
this.signingKeyRepository = signingKeyRepository;
|
this.signingKeyRepository = signingKeyRepository;
|
||||||
@ -53,7 +53,6 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
|
|||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void initializeKeystore() {
|
public void initializeKeystore() {
|
||||||
if (!isKeystoreEnabled()) {
|
if (!isKeystoreEnabled()) {
|
||||||
log.info("Keystore is disabled, using in-memory key generation");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
|
|||||||
ensurePrivateKeyDirectoryExists();
|
ensurePrivateKeyDirectoryExists();
|
||||||
loadOrGenerateKeypair();
|
loadOrGenerateKeypair();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to initialize keystore, falling back to in-memory generation", e);
|
log.error("Failed to initialize keystore, using in-memory generation", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +152,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private KeyPair generateRSAKeypair() {
|
private KeyPair generateRSAKeypair() {
|
||||||
KeyPairGenerator keyPairGenerator = null;
|
KeyPairGenerator keyPairGenerator;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||||
@ -213,6 +212,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
|
|||||||
byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
|
byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
|
||||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
|
||||||
return keyFactory.generatePrivate(keySpec);
|
return keyFactory.generatePrivate(keySpec);
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ package stirling.software.proprietary.security.service;
|
|||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface JwtKeystoreServiceInterface {
|
public interface KeystoreServiceInterface {
|
||||||
|
|
||||||
KeyPair getActiveKeyPair();
|
KeyPair getActiveKeyPair();
|
||||||
|
|
@ -1,8 +1,5 @@
|
|||||||
package stirling.software.proprietary.security.service;
|
package stirling.software.proprietary.security.service;
|
||||||
|
|
||||||
import static stirling.software.proprietary.security.model.AuthenticationType.OAUTH2;
|
|
||||||
import static stirling.software.proprietary.security.model.AuthenticationType.SSO;
|
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -63,17 +60,6 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
private final ApplicationProperties.Security.OAUTH2 oAuth2;
|
private final ApplicationProperties.Security.OAUTH2 oAuth2;
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public void migrateOauth2ToSSO() {
|
|
||||||
userRepository
|
|
||||||
.findByAuthenticationTypeIgnoreCase(OAUTH2.toString())
|
|
||||||
.forEach(
|
|
||||||
user -> {
|
|
||||||
user.setAuthenticationType(SSO);
|
|
||||||
userRepository.save(user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle OAUTH2 login and user auto creation.
|
// Handle OAUTH2 login and user auto creation.
|
||||||
public void processSSOPostLogin(
|
public void processSSOPostLogin(
|
||||||
String username, boolean autoCreateUser, AuthenticationType type)
|
String username, boolean autoCreateUser, AuthenticationType type)
|
||||||
|
@ -11,6 +11,8 @@ import java.util.Optional;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -55,7 +57,7 @@ class JwtServiceTest {
|
|||||||
private HttpServletResponse response;
|
private HttpServletResponse response;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private JwtKeystoreServiceInterface keystoreService;
|
private KeystoreServiceInterface keystoreService;
|
||||||
|
|
||||||
private JwtService jwtService;
|
private JwtService jwtService;
|
||||||
private KeyPair testKeyPair;
|
private KeyPair testKeyPair;
|
||||||
@ -201,19 +203,10 @@ class JwtServiceTest {
|
|||||||
assertThrows(AuthenticationFailureException.class, () -> jwtService.extractClaims("invalid-token"));
|
assertThrows(AuthenticationFailureException.class, () -> jwtService.extractClaims("invalid-token"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testExtractTokenWithAuthorizationHeader() {
|
|
||||||
String token = "test-token";
|
|
||||||
when(request.getHeader("Authorization")).thenReturn("Bearer " + token);
|
|
||||||
|
|
||||||
assertEquals(token, jwtService.extractToken(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithCookie() {
|
void testExtractTokenWithCookie() {
|
||||||
String token = "test-token";
|
String token = "test-token";
|
||||||
Cookie[] cookies = { new Cookie("stirling_jwt", token) };
|
Cookie[] cookies = { new Cookie("stirling_jwt", token) };
|
||||||
when(request.getHeader("Authorization")).thenReturn(null);
|
|
||||||
when(request.getCookies()).thenReturn(cookies);
|
when(request.getCookies()).thenReturn(cookies);
|
||||||
|
|
||||||
assertEquals(token, jwtService.extractToken(request));
|
assertEquals(token, jwtService.extractToken(request));
|
||||||
@ -221,7 +214,6 @@ class JwtServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithNoCookies() {
|
void testExtractTokenWithNoCookies() {
|
||||||
when(request.getHeader("Authorization")).thenReturn(null);
|
|
||||||
when(request.getCookies()).thenReturn(null);
|
when(request.getCookies()).thenReturn(null);
|
||||||
|
|
||||||
assertNull(jwtService.extractToken(request));
|
assertNull(jwtService.extractToken(request));
|
||||||
@ -230,7 +222,6 @@ class JwtServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithWrongCookie() {
|
void testExtractTokenWithWrongCookie() {
|
||||||
Cookie[] cookies = {new Cookie("OTHER_COOKIE", "value")};
|
Cookie[] cookies = {new Cookie("OTHER_COOKIE", "value")};
|
||||||
when(request.getHeader("Authorization")).thenReturn(null);
|
|
||||||
when(request.getCookies()).thenReturn(cookies);
|
when(request.getCookies()).thenReturn(cookies);
|
||||||
|
|
||||||
assertNull(jwtService.extractToken(request));
|
assertNull(jwtService.extractToken(request));
|
||||||
@ -238,22 +229,30 @@ class JwtServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithInvalidAuthorizationHeader() {
|
void testExtractTokenWithInvalidAuthorizationHeader() {
|
||||||
when(request.getHeader("Authorization")).thenReturn("Basic token");
|
|
||||||
when(request.getCookies()).thenReturn(null);
|
when(request.getCookies()).thenReturn(null);
|
||||||
|
|
||||||
assertNull(jwtService.extractToken(request));
|
assertNull(jwtService.extractToken(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testAddToken() {
|
@ValueSource(booleans = {true, false})
|
||||||
|
void testAddToken(boolean secureCookie) throws Exception {
|
||||||
String token = "test-token";
|
String token = "test-token";
|
||||||
|
|
||||||
jwtService.addToken(response, token);
|
// Create new JwtService instance with the secureCookie parameter
|
||||||
|
JwtService testJwtService = createJwtServiceWithSecureCookie(secureCookie);
|
||||||
|
|
||||||
|
testJwtService.addToken(response, token);
|
||||||
|
|
||||||
verify(response).setHeader("Authorization", "Bearer " + token);
|
verify(response).setHeader("Authorization", "Bearer " + token);
|
||||||
verify(response).addHeader(eq("Set-Cookie"), contains("stirling_jwt=" + token));
|
verify(response).addHeader(eq("Set-Cookie"), contains("stirling_jwt=" + token));
|
||||||
verify(response).addHeader(eq("Set-Cookie"), contains("HttpOnly"));
|
verify(response).addHeader(eq("Set-Cookie"), contains("HttpOnly"));
|
||||||
|
|
||||||
|
if (secureCookie) {
|
||||||
verify(response).addHeader(eq("Set-Cookie"), contains("Secure"));
|
verify(response).addHeader(eq("Set-Cookie"), contains("Secure"));
|
||||||
|
} else {
|
||||||
|
verify(response, org.mockito.Mockito.never()).addHeader(eq("Set-Cookie"), contains("Secure"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -327,4 +326,16 @@ class JwtServiceTest {
|
|||||||
// Verify fallback to active keypair was used (called multiple times during token operations)
|
// Verify fallback to active keypair was used (called multiple times during token operations)
|
||||||
verify(keystoreService, atLeast(1)).getActiveKeyPair();
|
verify(keystoreService, atLeast(1)).getActiveKeyPair();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JwtService createJwtServiceWithSecureCookie(boolean secureCookie) throws Exception {
|
||||||
|
// Use reflection to create JwtService with custom secureCookie value
|
||||||
|
JwtService testService = new JwtService(true, keystoreService);
|
||||||
|
|
||||||
|
// Set the secureCookie field using reflection
|
||||||
|
java.lang.reflect.Field secureCookieField = JwtService.class.getDeclaredField("secureCookie");
|
||||||
|
secureCookieField.setAccessible(true);
|
||||||
|
secureCookieField.set(testService, secureCookie);
|
||||||
|
|
||||||
|
return testService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,13 @@ import stirling.software.proprietary.security.database.repository.JwtSigningKeyR
|
|||||||
import stirling.software.proprietary.security.model.JwtSigningKey;
|
import stirling.software.proprietary.security.model.JwtSigningKey;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class JwtKeyCleanupServiceTest {
|
class KeyPairCleanupServiceTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private JwtSigningKeyRepository signingKeyRepository;
|
private JwtSigningKeyRepository signingKeyRepository;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private JwtKeystoreService keystoreService;
|
private KeystoreService keystoreService;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ApplicationProperties applicationProperties;
|
private ApplicationProperties applicationProperties;
|
||||||
@ -47,7 +47,7 @@ class JwtKeyCleanupServiceTest {
|
|||||||
@TempDir
|
@TempDir
|
||||||
private Path tempDir;
|
private Path tempDir;
|
||||||
|
|
||||||
private JwtKeyCleanupService cleanupService;
|
private KeyPairCleanupService cleanupService;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
@ -59,7 +59,7 @@ class JwtKeyCleanupServiceTest {
|
|||||||
lenient().when(jwtConfig.getCleanupBatchSize()).thenReturn(100);
|
lenient().when(jwtConfig.getCleanupBatchSize()).thenReturn(100);
|
||||||
lenient().when(keystoreService.isKeystoreEnabled()).thenReturn(true);
|
lenient().when(keystoreService.isKeystoreEnabled()).thenReturn(true);
|
||||||
|
|
||||||
cleanupService = new JwtKeyCleanupService(signingKeyRepository, keystoreService, applicationProperties);
|
cleanupService = new KeyPairCleanupService(signingKeyRepository, keystoreService, applicationProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class JwtKeystoreServiceInterfaceTest {
|
class KeystoreServiceInterfaceTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private JwtSigningKeyRepository repository;
|
private JwtSigningKeyRepository repository;
|
||||||
@ -49,7 +49,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
@TempDir
|
@TempDir
|
||||||
Path tempDir;
|
Path tempDir;
|
||||||
|
|
||||||
private JwtKeystoreService keystoreService;
|
private KeystoreService keystoreService;
|
||||||
private KeyPair testKeyPair;
|
private KeyPair testKeyPair;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@ -70,7 +70,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
|
|
||||||
assertEquals(keystoreEnabled, keystoreService.isKeystoreEnabled());
|
assertEquals(keystoreEnabled, keystoreService.isKeystoreEnabled());
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
|
|
||||||
KeyPair result = keystoreService.getActiveKeyPair();
|
KeyPair result = keystoreService.getActiveKeyPair();
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
keystoreService.initializeKeystore();
|
keystoreService.initializeKeystore();
|
||||||
|
|
||||||
KeyPair result = keystoreService.getActiveKeyPair();
|
KeyPair result = keystoreService.getActiveKeyPair();
|
||||||
@ -122,7 +122,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
keystoreService.initializeKeystore();
|
keystoreService.initializeKeystore();
|
||||||
|
|
||||||
KeyPair result = keystoreService.getActiveKeyPair();
|
KeyPair result = keystoreService.getActiveKeyPair();
|
||||||
@ -146,7 +146,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
|
|
||||||
Optional<KeyPair> result = keystoreService.getKeyPairByKeyId(keyId);
|
Optional<KeyPair> result = keystoreService.getKeyPairByKeyId(keyId);
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
|
|
||||||
Optional<KeyPair> result = keystoreService.getKeyPairByKeyId(keyId);
|
Optional<KeyPair> result = keystoreService.getKeyPairByKeyId(keyId);
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
|
|
||||||
Optional<KeyPair> result = keystoreService.getKeyPairByKeyId("any-key");
|
Optional<KeyPair> result = keystoreService.getKeyPairByKeyId("any-key");
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
keystoreService.initializeKeystore();
|
keystoreService.initializeKeystore();
|
||||||
|
|
||||||
assertTrue(Files.exists(tempDir));
|
assertTrue(Files.exists(tempDir));
|
||||||
@ -209,7 +209,7 @@ class JwtKeystoreServiceInterfaceTest {
|
|||||||
|
|
||||||
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
|
||||||
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
|
||||||
keystoreService = new JwtKeystoreService(repository, applicationProperties);
|
keystoreService = new KeystoreService(repository, applicationProperties);
|
||||||
keystoreService.initializeKeystore();
|
keystoreService.initializeKeystore();
|
||||||
|
|
||||||
KeyPair result = keystoreService.getActiveKeyPair();
|
KeyPair result = keystoreService.getActiveKeyPair();
|
@ -20,6 +20,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||||
SECURITY_ENABLELOGIN: "true"
|
SECURITY_ENABLELOGIN: "true"
|
||||||
|
V2: "false"
|
||||||
PUID: 1002
|
PUID: 1002
|
||||||
PGID: 1002
|
PGID: 1002
|
||||||
UMASK: "022"
|
UMASK: "022"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user