forked from Hithomelabs/CFTunnels
[ISSUE-114] Document CFTunnels codebase with JavaDoc comments #1
@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.http.*;
|
import org.springframework.http.*;
|
||||||
import org.springframework.http.*;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
@ -36,6 +35,41 @@ import java.util.Map;
|
|||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST Controller for managing Cloudflare Tunnels.
|
||||||
|
*
|
||||||
|
* <p>This controller provides the public API for managing Cloudflare Tunnels
|
||||||
|
* and their ingress mappings. All endpoints require authentication via OIDC
|
||||||
|
* and are protected by role-based access control.</p>
|
||||||
|
*
|
||||||
|
* <p><b>Base URL:</b> {@code /cloudflare}</p>
|
||||||
|
*
|
||||||
|
* <p><b>Authentication:</b> OIDC-based with role-based access</p>
|
||||||
|
*
|
||||||
|
* <p><b>Available Roles:</b></p>
|
||||||
|
* <ul>
|
||||||
|
* <li>USER - View tunnels and requests</li>
|
||||||
|
* <li>DEVELOPER - Create/modify/delete mappings</li>
|
||||||
|
* <li>APPROVER - Approve/reject requests</li>
|
||||||
|
* <li>ADMIN - Full tunnel configuration access</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p><b>Example Usage:</b></p>
|
||||||
|
* <pre>
|
||||||
|
* # Get all tunnels (requires USER role)
|
||||||
|
* curl -H "Authorization: Bearer <token>" \
|
||||||
|
* https://api.example.com/cloudflare/tunnels
|
||||||
|
*
|
||||||
|
* # Add a mapping (requires ADMIN role)
|
||||||
|
* curl -X POST -H "Authorization: Bearer <token>" \
|
||||||
|
* -H "Content-Type: application/json" \
|
||||||
|
* -d '{"hostname":"api.example.com","service":"http://localhost:8080"}' \
|
||||||
|
* https://api.example.com/cloudflare/tunnels/{tunnelId}/mappings
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see CloudflareAPIService
|
||||||
|
* @see MappingRequestService
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/cloudflare")
|
@RequestMapping("/cloudflare")
|
||||||
public class TunnelController implements ErrorController {
|
public class TunnelController implements ErrorController {
|
||||||
@ -63,9 +97,23 @@ public class TunnelController implements ErrorController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current environment (loaded from spring.profiles.active).
|
||||||
|
*/
|
||||||
@Value("${spring.profiles.active}")
|
@Value("${spring.profiles.active}")
|
||||||
private String environment;
|
private String environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current user information.
|
||||||
|
*
|
||||||
|
* <p>Returns the authenticated user's username and roles.</p>
|
||||||
|
*
|
||||||
|
* @param oidcUser The authenticated OIDC user
|
||||||
|
* @return Map containing username and roles
|
||||||
|
*
|
||||||
|
* @security Requires USER role
|
||||||
|
* @response 200 OK
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('USER')")
|
@PreAuthorize("hasAnyRole('USER')")
|
||||||
@GetMapping("/whoami")
|
@GetMapping("/whoami")
|
||||||
public Map<String,Object> whoAmI(@AuthenticationPrincipal OidcUser oidcUser) {
|
public Map<String,Object> whoAmI(@AuthenticationPrincipal OidcUser oidcUser) {
|
||||||
@ -79,6 +127,19 @@ public class TunnelController implements ErrorController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all tunnels from Cloudflare API.
|
||||||
|
*
|
||||||
|
* <p>Fetches the complete list of tunnels from Cloudflare,
|
||||||
|
* including their status and configuration from the Cloudflare API.</p>
|
||||||
|
*
|
||||||
|
* @return Map containing list of all tunnels
|
||||||
|
*
|
||||||
|
* @security Requires USER role
|
||||||
|
* @response 200 OK with tunnel list
|
||||||
|
* @response 500 Internal Server Error if API call fails
|
||||||
|
* @see <a href="https://api.cloudflare.com/#cfd_tunnel-get-tunnels">Cloudflare API</a>
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('USER')")
|
@PreAuthorize("hasAnyRole('USER')")
|
||||||
@GetMapping("/tunnels")
|
@GetMapping("/tunnels")
|
||||||
@Operation( security = { @SecurityRequirement(name = "oidcAuth") } )
|
@Operation( security = { @SecurityRequirement(name = "oidcAuth") } )
|
||||||
@ -92,6 +153,19 @@ public class TunnelController implements ErrorController {
|
|||||||
return ResponseEntity.ok(jsonResponse);
|
return ResponseEntity.ok(jsonResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get locally configured tunnels.
|
||||||
|
*
|
||||||
|
* <p>Returns the tunnels that have been configured locally
|
||||||
|
* with environment associations.</p>
|
||||||
|
*
|
||||||
|
* @return Map containing list of configured tunnels
|
||||||
|
*
|
||||||
|
* @security Requires USER role
|
||||||
|
* @response 200 OK with tunnel list
|
||||||
|
* @response 500 Internal Server Error if database access fails
|
||||||
|
* @see CloudflareAPIService#getAllConfiguredTunnels()
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('USER')")
|
@PreAuthorize("hasAnyRole('USER')")
|
||||||
@GetMapping("/configured/tunnels")
|
@GetMapping("/configured/tunnels")
|
||||||
public ResponseEntity<Map<String,Object>> getConfiguredTunnels(){
|
public ResponseEntity<Map<String,Object>> getConfiguredTunnels(){
|
||||||
@ -106,6 +180,17 @@ public class TunnelController implements ErrorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all mapping requests.
|
||||||
|
*
|
||||||
|
* <p>Returns all pending, approved, and rejected mapping requests.</p>
|
||||||
|
*
|
||||||
|
* @return Map containing list of all requests
|
||||||
|
*
|
||||||
|
* @security Requires USER role
|
||||||
|
* @response 200 OK with request list
|
||||||
|
* @response 500 Internal Server Error if database access fails
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('USER')")
|
@PreAuthorize("hasAnyRole('USER')")
|
||||||
@GetMapping("/requests")
|
@GetMapping("/requests")
|
||||||
public ResponseEntity<Map<String,Object>> getAllRequests() {
|
public ResponseEntity<Map<String,Object>> getAllRequests() {
|
||||||
@ -120,6 +205,20 @@ public class TunnelController implements ErrorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tunnel configuration from Cloudflare.
|
||||||
|
*
|
||||||
|
* <p>Fetches the complete configuration for a specific tunnel,
|
||||||
|
* including all ingress rules.</p>
|
||||||
|
*
|
||||||
|
* @param tunnelId The Cloudflare tunnel ID (UUID)
|
||||||
|
* @return Map containing tunnel configuration
|
||||||
|
*
|
||||||
|
* @security Requires DEVELOPER role
|
||||||
|
* @response 200 OK with configuration
|
||||||
|
* @response 500 Internal Server Error
|
||||||
|
* @see <a href="https://api.cloudflare.com/#cfd_tunnel-get-tunnel-config">Cloudflare API</a>
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('DEVELOPER')")
|
@PreAuthorize("hasAnyRole('DEVELOPER')")
|
||||||
@GetMapping("/tunnels/{tunnelId}/mappings")
|
@GetMapping("/tunnels/{tunnelId}/mappings")
|
||||||
public ResponseEntity<Map<String,Object>> getTunnelConfigurations(@PathVariable String tunnelId) {
|
public ResponseEntity<Map<String,Object>> getTunnelConfigurations(@PathVariable String tunnelId) {
|
||||||
@ -132,22 +231,44 @@ public class TunnelController implements ErrorController {
|
|||||||
return ResponseEntity.ok(jsonResponse);
|
return ResponseEntity.ok(jsonResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 50df9101-f625-4618-b7c5-100338a57124
|
/**
|
||||||
|
* Add an ingress mapping to a tunnel.
|
||||||
|
*
|
||||||
|
* <p>Adds a new ingress rule to the tunnel configuration.
|
||||||
|
* The new rule is inserted at the second-to-last position,
|
||||||
|
* before any catch-all rule.</p>
|
||||||
|
*
|
||||||
|
* @param tunnelId The Cloudflare tunnel ID (UUID)
|
||||||
|
* @param ingress The ingress rule to add
|
||||||
|
* @return Map containing the updated configuration
|
||||||
|
*
|
||||||
|
* @security Requires ADMIN role
|
||||||
|
* @response 200 OK with updated configuration
|
||||||
|
* @response 400 Bad Request if ingress is invalid
|
||||||
|
* @response 500 Internal Server Error
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* {
|
||||||
|
* "hostname": "api.example.com",
|
||||||
|
* "service": "http://localhost:8080",
|
||||||
|
* "originRequest": {"noTLSVerify": true}
|
||||||
|
* }
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('ADMIN')")
|
@PreAuthorize("hasAnyRole('ADMIN')")
|
||||||
@PostMapping("/tunnels/{tunnelId}/mappings")
|
@PostMapping("/tunnels/{tunnelId}/mappings")
|
||||||
public ResponseEntity<Map<String, Object>> addTunnelconfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
|
public ResponseEntity<Map<String, Object>> addTunnelconfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
|
||||||
|
|
||||||
ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class);
|
ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class);
|
||||||
|
|
||||||
// * * Inserting new ingress value at second-to last position in list
|
// Inserting new ingress value at second-to last position in list
|
||||||
Config config = responseEntity.getBody().getResult().getConfig();
|
Config config = responseEntity.getBody().getResult().getConfig();
|
||||||
List<Ingress> response_ingress = config.getIngress();
|
List<Ingress> response_ingress = config.getIngress();
|
||||||
response_ingress.add(response_ingress.size()-1, ingress);
|
response_ingress.add(response_ingress.size()-1, ingress);
|
||||||
|
|
||||||
// * * Hitting put endpoint
|
// Hitting put endpoint
|
||||||
ResponseEntity<TunnelResponse> response = cloudflareAPIService.putCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class, config);
|
ResponseEntity<TunnelResponse> response = cloudflareAPIService.putCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class, config);
|
||||||
|
|
||||||
// * * Displaying response
|
// Displaying response
|
||||||
Map<String, Object> jsonResponse = new HashMap<>();
|
Map<String, Object> jsonResponse = new HashMap<>();
|
||||||
jsonResponse.put("status", response.getStatusCode().toString());
|
jsonResponse.put("status", response.getStatusCode().toString());
|
||||||
jsonResponse.put("data", response.getBody());
|
jsonResponse.put("data", response.getBody());
|
||||||
@ -155,21 +276,34 @@ public class TunnelController implements ErrorController {
|
|||||||
return ResponseEntity.ok(jsonResponse);
|
return ResponseEntity.ok(jsonResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an ingress mapping from a tunnel.
|
||||||
|
*
|
||||||
|
* <p>Removes an ingress rule by hostname from the tunnel configuration.</p>
|
||||||
|
*
|
||||||
|
* @param tunnelId The Cloudflare tunnel ID (UUID)
|
||||||
|
* @param ingress Ingress containing hostname to delete (only hostname field is used)
|
||||||
|
* @return Map containing the result
|
||||||
|
*
|
||||||
|
* @security Requires DEVELOPER role
|
||||||
|
* @response 200 OK with updated configuration
|
||||||
|
* @response 409 Conflict if hostname not found
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('DEVELOPER')")
|
@PreAuthorize("hasAnyRole('DEVELOPER')")
|
||||||
@DeleteMapping("/tunnels/{tunnelId}/mappings")
|
@DeleteMapping("/tunnels/{tunnelId}/mappings")
|
||||||
public ResponseEntity<Map<String, Object>> deleteTunnelConfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
|
public ResponseEntity<Map<String, Object>> deleteTunnelConfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
|
||||||
|
|
||||||
ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class);
|
ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class);
|
||||||
|
|
||||||
// * * Deleting the selected ingress value
|
// Deleting the selected ingress value
|
||||||
Config config = responseEntity.getBody().getResult().getConfig();
|
Config config = responseEntity.getBody().getResult().getConfig();
|
||||||
List<Ingress> response_ingress = config.getIngress();
|
List<Ingress> response_ingress = config.getIngress();
|
||||||
Boolean result = Ingress.deleteByHostName(response_ingress, ingress.getHostname());
|
Boolean result = Ingress.deleteByHostName(response_ingress, ingress.getHostname());
|
||||||
|
|
||||||
// * * Hitting put endpoint
|
// Hitting put endpoint
|
||||||
ResponseEntity<TunnelResponse> response = cloudflareAPIService.putCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class, config);
|
ResponseEntity<TunnelResponse> response = cloudflareAPIService.putCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class, config);
|
||||||
|
|
||||||
// * * Displaying response
|
// Displaying response
|
||||||
Map<String, Object> jsonResponse = new HashMap<>();
|
Map<String, Object> jsonResponse = new HashMap<>();
|
||||||
|
|
||||||
if (result){
|
if (result){
|
||||||
@ -184,6 +318,24 @@ public class TunnelController implements ErrorController {
|
|||||||
return ResponseEntity.ok(jsonResponse);
|
return ResponseEntity.ok(jsonResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a mapping change request.
|
||||||
|
*
|
||||||
|
* <p>Creates a new request for changing tunnel ingress mappings.
|
||||||
|
* The request starts in PENDING status and must be approved
|
||||||
|
* before the changes are applied.</p>
|
||||||
|
*
|
||||||
|
* @param tunnelId The Cloudflare tunnel ID (UUID)
|
||||||
|
* @param oidcUser The authenticated user
|
||||||
|
* @param ingess The ingress configuration to request
|
||||||
|
* @return The created request with PENDING status
|
||||||
|
*
|
||||||
|
* @security Requires DEVELOPER role
|
||||||
|
* @response 201 Created with request
|
||||||
|
* @response 400 Bad Request if invalid
|
||||||
|
*
|
||||||
|
* @see MappingRequestService#createMappingRequest(String, Ingress, OidcUser)
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('DEVELOPER')")
|
@PreAuthorize("hasAnyRole('DEVELOPER')")
|
||||||
@PostMapping("/tunnels/configure/{tunnelId}/requests")
|
@PostMapping("/tunnels/configure/{tunnelId}/requests")
|
||||||
public ResponseEntity<Request> createTunnelMappingRequest(@PathVariable String tunnelId, @AuthenticationPrincipal OidcUser oidcUser, @RequestBody Ingress ingess){
|
public ResponseEntity<Request> createTunnelMappingRequest(@PathVariable String tunnelId, @AuthenticationPrincipal OidcUser oidcUser, @RequestBody Ingress ingess){
|
||||||
@ -193,6 +345,21 @@ public class TunnelController implements ErrorController {
|
|||||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
|
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approve a mapping request.
|
||||||
|
*
|
||||||
|
* <p>Approves a pending mapping request. If approved, the
|
||||||
|
* mapping will be applied to the Cloudflare tunnel.</p>
|
||||||
|
*
|
||||||
|
* @param requestId The ID of the request to approve
|
||||||
|
* @param oidcUser The approver (must have APPROVER role)
|
||||||
|
* @return The updated request with APPROVED status
|
||||||
|
*
|
||||||
|
* @security Requires APPROVER role
|
||||||
|
* @response 200 OK with approved request
|
||||||
|
* @response 404 Not Found if request doesn't exist
|
||||||
|
* @response 409 Conflict if request already processed
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('APPROVER')")
|
@PreAuthorize("hasAnyRole('APPROVER')")
|
||||||
@PutMapping("/requests/{requestId}/approve")
|
@PutMapping("/requests/{requestId}/approve")
|
||||||
public ResponseEntity<Request> approveMappingRequest(@PathVariable UUID requestId, @AuthenticationPrincipal OidcUser oidcUser) {
|
public ResponseEntity<Request> approveMappingRequest(@PathVariable UUID requestId, @AuthenticationPrincipal OidcUser oidcUser) {
|
||||||
@ -210,6 +377,21 @@ public class TunnelController implements ErrorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject a mapping request.
|
||||||
|
*
|
||||||
|
* <p>Rejects a pending mapping request. No changes
|
||||||
|
* will be made to the tunnel.</p>
|
||||||
|
*
|
||||||
|
* @param requestId The ID of the request to reject
|
||||||
|
* @param oidcUser The rejecter (must have APPROVER role)
|
||||||
|
* @return The updated request with REJECTED status
|
||||||
|
*
|
||||||
|
* @security Requires APPROVER role
|
||||||
|
* @response 200 OK with rejected request
|
||||||
|
* @response 404 Not Found
|
||||||
|
* @response 409 Conflict
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('APPROVER')")
|
@PreAuthorize("hasAnyRole('APPROVER')")
|
||||||
@PutMapping("/requests/{requestId}/reject")
|
@PutMapping("/requests/{requestId}/reject")
|
||||||
public ResponseEntity<Request> rejectMappingRequest(@PathVariable UUID requestId, @AuthenticationPrincipal OidcUser oidcUser) {
|
public ResponseEntity<Request> rejectMappingRequest(@PathVariable UUID requestId, @AuthenticationPrincipal OidcUser oidcUser) {
|
||||||
@ -227,13 +409,36 @@ public class TunnelController implements ErrorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure a tunnel for the current environment.
|
||||||
|
*
|
||||||
|
* <p>Creates a local configuration entry for a tunnel,
|
||||||
|
* associating it with the current environment (from spring.profiles.active).</p>
|
||||||
|
*
|
||||||
|
* <p><b>Response Codes:</b></p>
|
||||||
|
* <ul>
|
||||||
|
* <li>200 - Created/updated with new tunnel</li>
|
||||||
|
* <li>204 - No changes needed</li>
|
||||||
|
* <li>404 - Tunnel not found in Cloudflare</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param tunnelId The Cloudflare tunnel ID (UUID)
|
||||||
|
* @param user The authenticated user
|
||||||
|
* @return The tunnel configuration
|
||||||
|
*
|
||||||
|
* @security Requires ADMIN role
|
||||||
|
* @response 200 OK with tunnel
|
||||||
|
* @response 204 No Content
|
||||||
|
* @response 404 Not Found
|
||||||
|
* @response 500 Internal Server Error
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyRole('ADMIN')")
|
@PreAuthorize("hasAnyRole('ADMIN')")
|
||||||
@PutMapping("/tunnels/configure/{tunnelId}")
|
@PutMapping("/tunnels/configure/{tunnelId}")
|
||||||
public ResponseEntity<Tunnel> configureTunnelForEnvironment(@PathVariable String tunnelId, @AuthenticationPrincipal OidcUser user) {
|
public ResponseEntity<Tunnel> configureTunnelForEnvironment(@PathVariable String tunnelId, @AuthenticationPrincipal OidcUser user) {
|
||||||
/*
|
/*
|
||||||
* * Returns 200 if an object is created or updated with a new representation of the object
|
* Returns 200 if an object is created or updated with a new representation of the object
|
||||||
* * Returns 204 if the object state did not need any changing.
|
* Returns 204 if the object state did not need any changing.
|
||||||
* * Returns 404 if the tunnelId is not valid
|
* Returns 404 if the tunnelId is not valid
|
||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -249,4 +454,4 @@ public class TunnelController implements ErrorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user