Hithomelabs/CFTunnels#114: Add JavaDoc and API documentation to TunnelController
This commit is contained in:
parent
4197e645cd
commit
7e5d20e8ac
@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
@ -36,6 +35,41 @@ import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
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
|
||||
@RequestMapping("/cloudflare")
|
||||
public class TunnelController implements ErrorController {
|
||||
@ -63,9 +97,23 @@ public class TunnelController implements ErrorController {
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* Current environment (loaded from spring.profiles.active).
|
||||
*/
|
||||
@Value("${spring.profiles.active}")
|
||||
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')")
|
||||
@GetMapping("/whoami")
|
||||
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')")
|
||||
@GetMapping("/tunnels")
|
||||
@Operation( security = { @SecurityRequirement(name = "oidcAuth") } )
|
||||
@ -92,6 +153,19 @@ public class TunnelController implements ErrorController {
|
||||
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')")
|
||||
@GetMapping("/configured/tunnels")
|
||||
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')")
|
||||
@GetMapping("/requests")
|
||||
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')")
|
||||
@GetMapping("/tunnels/{tunnelId}/mappings")
|
||||
public ResponseEntity<Map<String,Object>> getTunnelConfigurations(@PathVariable String tunnelId) {
|
||||
@ -132,22 +231,44 @@ public class TunnelController implements ErrorController {
|
||||
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')")
|
||||
@PostMapping("/tunnels/{tunnelId}/mappings")
|
||||
public ResponseEntity<Map<String, Object>> addTunnelconfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
|
||||
|
||||
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();
|
||||
List<Ingress> response_ingress = config.getIngress();
|
||||
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);
|
||||
|
||||
// * * Displaying response
|
||||
// Displaying response
|
||||
Map<String, Object> jsonResponse = new HashMap<>();
|
||||
jsonResponse.put("status", response.getStatusCode().toString());
|
||||
jsonResponse.put("data", response.getBody());
|
||||
@ -155,21 +276,34 @@ public class TunnelController implements ErrorController {
|
||||
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')")
|
||||
@DeleteMapping("/tunnels/{tunnelId}/mappings")
|
||||
public ResponseEntity<Map<String, Object>> deleteTunnelConfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
|
||||
|
||||
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();
|
||||
List<Ingress> response_ingress = config.getIngress();
|
||||
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);
|
||||
|
||||
// * * Displaying response
|
||||
// Displaying response
|
||||
Map<String, Object> jsonResponse = new HashMap<>();
|
||||
|
||||
if (result){
|
||||
@ -184,6 +318,24 @@ public class TunnelController implements ErrorController {
|
||||
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')")
|
||||
@PostMapping("/tunnels/configure/{tunnelId}/requests")
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')")
|
||||
@PutMapping("/requests/{requestId}/approve")
|
||||
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')")
|
||||
@PutMapping("/requests/{requestId}/reject")
|
||||
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')")
|
||||
@PutMapping("/tunnels/configure/{tunnelId}")
|
||||
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 204 if the object state did not need any changing.
|
||||
* * Returns 404 if the tunnelId is not valid
|
||||
* 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 404 if the tunnelId is not valid
|
||||
*/
|
||||
|
||||
try {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user