Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af55458ad4 | |||
| 6712a26b2a |
@@ -6,6 +6,7 @@ import jakarta.servlet.ServletException;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
@@ -58,7 +59,7 @@ public class ApiKeyAuthFilter extends OncePerRequestFilter {
|
|||||||
new UsernamePasswordAuthenticationToken(
|
new UsernamePasswordAuthenticationToken(
|
||||||
"api-key-client",
|
"api-key-client",
|
||||||
null,
|
null,
|
||||||
List.of()
|
List.of(new SimpleGrantedAuthority("ROLE_API"))
|
||||||
);
|
);
|
||||||
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
@@ -27,11 +28,16 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
return http
|
return http
|
||||||
.csrf(csrf -> csrf.disable())
|
.csrf(csrf -> csrf.disable())
|
||||||
|
.cors(cors -> cors.disable())
|
||||||
.formLogin(form -> form.disable())
|
.formLogin(form -> form.disable())
|
||||||
.httpBasic(basic -> basic.disable())
|
.httpBasic(basic -> basic.disable())
|
||||||
|
.logout(logout -> logout.disable())
|
||||||
|
.sessionManagement(session -> session
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
)
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/actuator/health").permitAll()
|
.requestMatchers("/actuator/health").permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().permitAll()
|
||||||
)
|
)
|
||||||
.addFilterBefore(apiKeyAuthFilter, UsernamePasswordAuthenticationFilter.class)
|
.addFilterBefore(apiKeyAuthFilter, UsernamePasswordAuthenticationFilter.class)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -67,4 +67,12 @@ public class RouterController {
|
|||||||
public SyncRoutersFromVpsResponse syncFromVps() {
|
public SyncRoutersFromVpsResponse syncFromVps() {
|
||||||
return routerSyncService.syncFromVps();
|
return routerSyncService.syncFromVps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/provisioning-result")
|
||||||
|
public RouterResponse submitProvisioningResult(
|
||||||
|
@PathVariable UUID id,
|
||||||
|
@Valid @RequestBody RouterProvisioningResultRequest request
|
||||||
|
) {
|
||||||
|
return routerService.submitProvisioningResult(id, request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package com.litoralregas.vpnprovisioner.router.dto;
|
||||||
|
|
||||||
|
import com.litoralregas.vpnprovisioner.router.entity.RouterStatus;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public record RouterProvisioningResultRequest(
|
||||||
|
@NotNull
|
||||||
|
Boolean success,
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
RouterStatus finalStatus,
|
||||||
|
|
||||||
|
String message,
|
||||||
|
String firmwareVersion,
|
||||||
|
String validationSummary,
|
||||||
|
String lastError
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -19,6 +19,11 @@ public record RouterResponse(
|
|||||||
RouterVpnStatus vpnStatus,
|
RouterVpnStatus vpnStatus,
|
||||||
Instant vpnProvisionedAt,
|
Instant vpnProvisionedAt,
|
||||||
Instant createdAt,
|
Instant createdAt,
|
||||||
Instant updatedAt
|
Instant updatedAt,
|
||||||
|
|
||||||
|
Instant lastProvisionedAt,
|
||||||
|
Instant lastValidatedAt,
|
||||||
|
String validationSummary,
|
||||||
|
String lastProvisioningError
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,15 @@ public class Router {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Instant updatedAt;
|
private Instant updatedAt;
|
||||||
|
|
||||||
|
private Instant lastProvisionedAt;
|
||||||
|
private Instant lastValidatedAt;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String validationSummary;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String lastProvisioningError;
|
||||||
|
|
||||||
protected Router() {
|
protected Router() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,4 +174,43 @@ public class Router {
|
|||||||
this.vpnProvisionedAt = Instant.now();
|
this.vpnProvisionedAt = Instant.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerProvisioningResult(
|
||||||
|
boolean success,
|
||||||
|
RouterStatus finalStatus,
|
||||||
|
String message,
|
||||||
|
String firmwareVersion,
|
||||||
|
String validationSummary,
|
||||||
|
String lastError
|
||||||
|
) {
|
||||||
|
Instant now = Instant.now();
|
||||||
|
|
||||||
|
this.status = finalStatus;
|
||||||
|
this.lastValidatedAt = now;
|
||||||
|
this.firmwareVersion = firmwareVersion;
|
||||||
|
this.validationSummary = validationSummary;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
this.lastProvisionedAt = now;
|
||||||
|
this.lastProvisioningError = null;
|
||||||
|
} else {
|
||||||
|
this.lastProvisioningError = lastError != null ? lastError : message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getLastProvisionedAt() {
|
||||||
|
return lastProvisionedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getLastValidatedAt() {
|
||||||
|
return lastValidatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValidationSummary() {
|
||||||
|
return validationSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastProvisioningError() {
|
||||||
|
return lastProvisioningError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.litoralregas.vpnprovisioner.router.service;
|
package com.litoralregas.vpnprovisioner.router.service;
|
||||||
|
|
||||||
import com.litoralregas.vpnprovisioner.router.dto.CreateRouterRequest;
|
import com.litoralregas.vpnprovisioner.router.dto.CreateRouterRequest;
|
||||||
|
import com.litoralregas.vpnprovisioner.router.dto.RouterProvisioningResultRequest;
|
||||||
import com.litoralregas.vpnprovisioner.router.dto.RouterResponse;
|
import com.litoralregas.vpnprovisioner.router.dto.RouterResponse;
|
||||||
import com.litoralregas.vpnprovisioner.router.dto.UpdateRouterRequest;
|
import com.litoralregas.vpnprovisioner.router.dto.UpdateRouterRequest;
|
||||||
import com.litoralregas.vpnprovisioner.router.entity.Router;
|
import com.litoralregas.vpnprovisioner.router.entity.Router;
|
||||||
@@ -9,6 +10,7 @@ import com.litoralregas.vpnprovisioner.common.exception.ResourceNotFoundExceptio
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -84,7 +86,32 @@ public class RouterService {
|
|||||||
router.getVpnStatus(),
|
router.getVpnStatus(),
|
||||||
router.getVpnProvisionedAt(),
|
router.getVpnProvisionedAt(),
|
||||||
router.getCreatedAt(),
|
router.getCreatedAt(),
|
||||||
router.getUpdatedAt()
|
router.getUpdatedAt(),
|
||||||
|
|
||||||
|
router.getLastProvisionedAt(),
|
||||||
|
router.getLastValidatedAt(),
|
||||||
|
router.getValidationSummary(),
|
||||||
|
router.getLastProvisioningError()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public RouterResponse submitProvisioningResult(
|
||||||
|
UUID id,
|
||||||
|
RouterProvisioningResultRequest request
|
||||||
|
) {
|
||||||
|
Router router = routerRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Router not found: " + id));
|
||||||
|
|
||||||
|
router.registerProvisioningResult(
|
||||||
|
request.success(),
|
||||||
|
request.finalStatus(),
|
||||||
|
request.message(),
|
||||||
|
request.firmwareVersion(),
|
||||||
|
request.validationSummary(),
|
||||||
|
request.lastError()
|
||||||
|
);
|
||||||
|
|
||||||
|
return toResponse(router);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+6
-2
@@ -86,7 +86,6 @@ public class RouterVpnProvisioningService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RouterResponse toResponse(Router router) {
|
private RouterResponse toResponse(Router router) {
|
||||||
|
|
||||||
return new RouterResponse(
|
return new RouterResponse(
|
||||||
router.getId(),
|
router.getId(),
|
||||||
router.getName(),
|
router.getName(),
|
||||||
@@ -99,7 +98,12 @@ public class RouterVpnProvisioningService {
|
|||||||
router.getVpnStatus(),
|
router.getVpnStatus(),
|
||||||
router.getVpnProvisionedAt(),
|
router.getVpnProvisionedAt(),
|
||||||
router.getCreatedAt(),
|
router.getCreatedAt(),
|
||||||
router.getUpdatedAt()
|
router.getUpdatedAt(),
|
||||||
|
|
||||||
|
router.getLastProvisionedAt(),
|
||||||
|
router.getLastValidatedAt(),
|
||||||
|
router.getValidationSummary(),
|
||||||
|
router.getLastProvisioningError()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.litoralregas.vpnprovisioner.vps;
|
package com.litoralregas.vpnprovisioner.vps;
|
||||||
|
|
||||||
|
import com.litoralregas.vpnprovisioner.vps.dto.VpsHealthResponse;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -14,8 +15,8 @@ public class VpsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/health", produces = MediaType.APPLICATION_JSON_VALUE)
|
@GetMapping(value = "/health", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public String health() {
|
public VpsHealthResponse health() {
|
||||||
return wireGuardVpsService.getVpsHealthJson();
|
return wireGuardVpsService.getVpsHealth();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.litoralregas.vpnprovisioner.vps;
|
package com.litoralregas.vpnprovisioner.vps;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.litoralregas.vpnprovisioner.vps.dto.VpsHealthResponse;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -14,9 +17,11 @@ public class WireGuardVpsService {
|
|||||||
Pattern.compile("\\b198\\.19\\.\\d{1,3}\\.\\d{1,3}\\b");
|
Pattern.compile("\\b198\\.19\\.\\d{1,3}\\.\\d{1,3}\\b");
|
||||||
|
|
||||||
private final SshService sshService;
|
private final SshService sshService;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public WireGuardVpsService(SshService sshService) {
|
public WireGuardVpsService(SshService sshService, ObjectMapper objectMapper) {
|
||||||
this.sshService = sshService;
|
this.sshService = sshService;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> findUsedVpnIps() {
|
public Set<String> findUsedVpnIps() {
|
||||||
@@ -66,7 +71,7 @@ public class WireGuardVpsService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVpsHealthJson() {
|
public VpsHealthResponse getVpsHealth() {
|
||||||
SshCommandResult result = sshService.executeOnConfiguredVps(
|
SshCommandResult result = sshService.executeOnConfiguredVps(
|
||||||
"sudo /usr/local/sbin/lr-vps-health"
|
"sudo /usr/local/sbin/lr-vps-health"
|
||||||
);
|
);
|
||||||
@@ -77,7 +82,14 @@ public class WireGuardVpsService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.stdout();
|
try {
|
||||||
|
return objectMapper.readValue(result.stdout(), VpsHealthResponse.class);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Invalid VPS health JSON returned by script",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String restoreLastWireGuardBackup() {
|
public String restoreLastWireGuardBackup() {
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.litoralregas.vpnprovisioner.vps.dto;
|
||||||
|
|
||||||
|
public record VpsHealthResponse(
|
||||||
|
String wireGuardInterface,
|
||||||
|
boolean wireGuardRunning,
|
||||||
|
int wireGuardPeerCount,
|
||||||
|
boolean wireGuardConfigExists,
|
||||||
|
String udp2rawService,
|
||||||
|
boolean udp2rawActive,
|
||||||
|
String latestWireGuardBackup,
|
||||||
|
String systemUptime,
|
||||||
|
int diskUsagePercent,
|
||||||
|
int memoryUsagePercent,
|
||||||
|
String loadAverage,
|
||||||
|
String publicIp
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE routers
|
||||||
|
ADD COLUMN last_provisioned_at TIMESTAMP,
|
||||||
|
ADD COLUMN last_validated_at TIMESTAMP,
|
||||||
|
ADD COLUMN validation_summary TEXT,
|
||||||
|
ADD COLUMN last_provisioning_error TEXT;
|
||||||
Reference in New Issue
Block a user