diff --git a/.env.example b/.env.example index af849f6..b33c3d5 100644 --- a/.env.example +++ b/.env.example @@ -18,6 +18,10 @@ APP_BASE_URL=https://example.com JWT_SECRET=change_me_to_a_secure_random_string_in_production JWT_EXPIRATION_MS=86400000 +# Registration Configuration +# Set to false to disable user registration +REGISTRATION_ENABLED=true + # ActivityPub Configuration ACTIVITYPUB_ENABLED=true diff --git a/Dockerfile b/Dockerfile index aa36d04..e452021 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,6 +46,9 @@ USER fitpub # Expose application port EXPOSE 8080 +# Environment variables +ENV REGISTRATION_ENABLED=true + # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health || exit 1 diff --git a/src/main/java/org/operaton/fitpub/controller/AuthController.java b/src/main/java/org/operaton/fitpub/controller/AuthController.java index f06c1e0..8d6497e 100644 --- a/src/main/java/org/operaton/fitpub/controller/AuthController.java +++ b/src/main/java/org/operaton/fitpub/controller/AuthController.java @@ -7,6 +7,7 @@ import org.operaton.fitpub.model.dto.AuthResponse; import org.operaton.fitpub.model.dto.LoginRequest; import org.operaton.fitpub.model.dto.RegisterRequest; import org.operaton.fitpub.service.UserService; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.BadCredentialsException; @@ -24,6 +25,9 @@ public class AuthController { private final UserService userService; + @Value("${fitpub.registration.enabled:true}") + private boolean registrationEnabled; + /** * Register a new user account. * @@ -32,6 +36,13 @@ public class AuthController { */ @PostMapping("/register") public ResponseEntity register(@Valid @RequestBody RegisterRequest request) { + // Check if registration is enabled + if (!registrationEnabled) { + log.warn("Registration attempt blocked - registration is disabled"); + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(null); + } + log.info("Registration request received for username: {}", request.getUsername()); try { @@ -43,6 +54,16 @@ public class AuthController { } } + /** + * Get registration status. + * + * @return Registration status response + */ + @GetMapping("/registration-status") + public ResponseEntity getRegistrationStatus() { + return ResponseEntity.ok(new RegistrationStatusResponse(registrationEnabled)); + } + /** * Authenticate user and generate JWT token. * @@ -84,4 +105,9 @@ public class AuthController { * Error response DTO. */ record ErrorResponse(String error, String message) {} + + /** + * Registration status response DTO. + */ + record RegistrationStatusResponse(boolean enabled) {} } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 168504e..0408f4b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -62,6 +62,10 @@ fitpub: secret: ${JWT_SECRET:change-this-secret-key-in-production-must-be-at-least-32-characters-long} expiration: 86400000 # 24 hours in milliseconds + # Registration settings + registration: + enabled: ${REGISTRATION_ENABLED:true} + # Storage settings storage: fit-files: diff --git a/src/main/resources/static/js/auth.js b/src/main/resources/static/js/auth.js index d1a704b..1636277 100644 --- a/src/main/resources/static/js/auth.js +++ b/src/main/resources/static/js/auth.js @@ -173,6 +173,9 @@ const FitPubAuth = { // Update navigation UI based on auth status this.updateNavigationUI(); + // Check registration status and update UI + this.checkRegistrationStatus(); + // Check authentication status on page load this.checkAuthStatus(); @@ -180,6 +183,30 @@ const FitPubAuth = { this.setupExpirationWarning(); }, + /** + * Check if registration is enabled and update navigation UI + */ + checkRegistrationStatus: async function() { + try { + const response = await fetch('/api/auth/registration-status'); + const data = await response.json(); + + if (!data.enabled) { + // Hide registration link in navigation + const registerLinks = document.querySelectorAll('a[href="/register"]'); + registerLinks.forEach(link => { + const parent = link.parentElement; + if (parent && parent.tagName === 'LI') { + parent.style.display = 'none'; + } + }); + } + } catch (error) { + console.error('Error checking registration status:', error); + // Continue without hiding registration link if check fails + } + }, + /** * Update navigation UI based on authentication status */ diff --git a/src/main/resources/templates/auth/register.html b/src/main/resources/templates/auth/register.html index b24bd09..20954dc 100644 --- a/src/main/resources/templates/auth/register.html +++ b/src/main/resources/templates/auth/register.html @@ -174,7 +174,7 @@