conditionalCert

This commit is contained in:
Anthony Stirling 2025-09-16 14:22:49 +01:00
parent cd71075f79
commit a949019d5d
4 changed files with 57 additions and 29 deletions

View File

@ -18,17 +18,30 @@ import lombok.RequiredArgsConstructor;
import stirling.software.SPDF.config.EndpointConfiguration;
import stirling.software.common.configuration.AppConfig;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.ServerCertificateServiceInterface;
@RestController
@Tag(name = "Config", description = "Configuration APIs")
@RequestMapping("/api/v1/config")
@RequiredArgsConstructor
@Hidden
public class ConfigController {
private final ApplicationProperties applicationProperties;
private final ApplicationContext applicationContext;
private final EndpointConfiguration endpointConfiguration;
private final ServerCertificateServiceInterface serverCertificateService;
public ConfigController(
ApplicationProperties applicationProperties,
ApplicationContext applicationContext,
EndpointConfiguration endpointConfiguration,
@org.springframework.beans.factory.annotation.Autowired(required = false)
ServerCertificateServiceInterface serverCertificateService) {
this.applicationProperties = applicationProperties;
this.applicationContext = applicationContext;
this.endpointConfiguration = endpointConfiguration;
this.serverCertificateService = serverCertificateService;
}
@GetMapping("/app-config")
public ResponseEntity<Map<String, Object>> getAppConfig() {
@ -62,6 +75,10 @@ public class ConfigController {
// Premium/Enterprise settings
configData.put("premiumEnabled", applicationProperties.getPremium().isEnabled());
// Server certificate settings
configData.put("serverCertificateEnabled",
serverCertificateService != null && serverCertificateService.isEnabled());
// Legal settings
configData.put(
"termsAndConditions", applicationProperties.getLegal().getTermsAndConditions());

View File

@ -14,7 +14,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.service.ServerCertificateService;
import stirling.software.common.service.ServerCertificateServiceInterface;
@RestController
@RequestMapping("/api/v1/admin/server-certificate")
@ -26,16 +26,16 @@ import stirling.software.common.service.ServerCertificateService;
@PreAuthorize("hasRole('ADMIN')")
public class ServerCertificateController {
private final ServerCertificateService serverCertificateService;
private final ServerCertificateServiceInterface serverCertificateService;
@GetMapping("/info")
@Operation(
summary = "Get server certificate information",
description = "Returns information about the current server certificate")
public ResponseEntity<ServerCertificateService.ServerCertificateInfo>
public ResponseEntity<ServerCertificateServiceInterface.ServerCertificateInfo>
getServerCertificateInfo() {
try {
ServerCertificateService.ServerCertificateInfo info =
ServerCertificateServiceInterface.ServerCertificateInfo info =
serverCertificateService.getServerCertificateInfo();
return ResponseEntity.ok(info);
} catch (Exception e) {
@ -109,27 +109,27 @@ public class ServerCertificateController {
}
}
@GetMapping("/public-key")
@GetMapping("/certificate")
@Operation(
summary = "Download server certificate public key",
summary = "Download server certificate",
description =
"Download the public key of the server certificate for validation purposes")
public ResponseEntity<byte[]> getServerCertificatePublicKey() {
"Download the server certificate in DER format for validation purposes")
public ResponseEntity<byte[]> getServerCertificate() {
try {
if (!serverCertificateService.hasServerCertificate()) {
return ResponseEntity.notFound().build();
}
byte[] publicKey = serverCertificateService.getServerCertificatePublicKey();
byte[] certificate = serverCertificateService.getServerCertificatePublicKey();
return ResponseEntity.ok()
.header(
HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"server-cert.crt\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(publicKey);
"attachment; filename=\"server-cert.cer\"")
.contentType(MediaType.valueOf("application/pkix-cert"))
.body(certificate);
} catch (Exception e) {
log.error("Failed to get server certificate public key", e);
log.error("Failed to get server certificate", e);
return ResponseEntity.internalServerError().build();
}
}

View File

@ -1,5 +1,6 @@
import { Stack, Button } from "@mantine/core";
import { ManageSignaturesParameters } from "../../../hooks/tools/manageSignatures/useManageSignaturesParameters";
import { useAppConfig } from "../../../hooks/useAppConfig";
interface CertificateTypeSettingsProps {
parameters: ManageSignaturesParameters;
@ -8,6 +9,13 @@ interface CertificateTypeSettingsProps {
}
const CertificateTypeSettings = ({ parameters, onParameterChange, disabled = false }: CertificateTypeSettingsProps) => {
const { config } = useAppConfig();
const isServerCertificateEnabled = config?.serverCertificateEnabled ?? false;
// Reset to MANUAL if AUTO is selected but feature is disabled
if (parameters.signMode === 'AUTO' && !isServerCertificateEnabled) {
onParameterChange('signMode', 'MANUAL');
}
return (
<Stack gap="md">
@ -29,21 +37,23 @@ const CertificateTypeSettings = ({ parameters, onParameterChange, disabled = fal
Manual
</div>
</Button>
<Button
variant={parameters.signMode === 'AUTO' ? 'filled' : 'outline'}
color={parameters.signMode === 'AUTO' ? 'green' : 'var(--text-muted)'}
onClick={() => {
onParameterChange('signMode', 'AUTO');
// Clear cert type and files when switching to auto
onParameterChange('certType', '');
}}
disabled={disabled}
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
Auto
</div>
</Button>
{isServerCertificateEnabled && (
<Button
variant={parameters.signMode === 'AUTO' ? 'filled' : 'outline'}
color={parameters.signMode === 'AUTO' ? 'green' : 'var(--text-muted)'}
onClick={() => {
onParameterChange('signMode', 'AUTO');
// Clear cert type and files when switching to auto
onParameterChange('certType', '');
}}
disabled={disabled}
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
Auto (server)
</div>
</Button>
)}
</div>
</Stack>
);

View File

@ -23,6 +23,7 @@ export interface AppConfig {
license?: string;
GoogleDriveEnabled?: boolean;
SSOAutoLogin?: boolean;
serverCertificateEnabled?: boolean;
error?: string;
}