summaryrefslogtreecommitdiff
path: root/pse-server/src/main/java/org/psesquared/server/authentication/api/controller
diff options
context:
space:
mode:
Diffstat (limited to 'pse-server/src/main/java/org/psesquared/server/authentication/api/controller')
-rw-r--r--pse-server/src/main/java/org/psesquared/server/authentication/api/controller/AuthenticationController.java251
-rw-r--r--pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ChangePasswordRequest.java15
-rw-r--r--pse-server/src/main/java/org/psesquared/server/authentication/api/controller/DeviceWrapper.java46
-rw-r--r--pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ForgotPasswordRequest.java13
-rw-r--r--pse-server/src/main/java/org/psesquared/server/authentication/api/controller/PasswordRequest.java13
-rw-r--r--pse-server/src/main/java/org/psesquared/server/authentication/api/controller/UserInfoRequest.java16
-rw-r--r--pse-server/src/main/java/org/psesquared/server/authentication/api/controller/package-info.java13
7 files changed, 367 insertions, 0 deletions
diff --git a/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/AuthenticationController.java b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/AuthenticationController.java
new file mode 100644
index 0000000..f580969
--- /dev/null
+++ b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/AuthenticationController.java
@@ -0,0 +1,251 @@
+package org.psesquared.server.authentication.api.controller;
+
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.List;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.psesquared.server.authentication.api.service.AuthenticationService;
+import org.psesquared.server.config.EmailConfigProperties;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * This is a controller class for the Authentication API that handles the
+ * requests from the client concerning login/logout and user account management.
+ */
+@RequestMapping("/api/2")
+@RestController
+@RequiredArgsConstructor
+public class AuthenticationController {
+
+ /**
+ * The name of the HTTP location header.
+ */
+ private static final String LOCATION_HEADER = "Location";
+
+ /**
+ * The service class that this controller calls to further process requests.
+ */
+ private final AuthenticationService authenticationService;
+
+ /**
+ * The properties class that is used to return some externally stored URLs.
+ */
+ private final EmailConfigProperties emailConfigProperties;
+
+ /**
+ * The API-endpoint for registering a new
+ * {@link org.psesquared.server.model.User} with a username, email address and
+ * password. In order for the account to be used, the registration process
+ * must be concluded with the verification of the email address. For this an
+ * email with a link for verification is sent to {@code userInfo.email()}.
+ *
+ * @param userInfo The request-wrapper containing username, email and
+ * password.
+ * @return {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#BAD_REQUEST} for invalid user information
+ * @see AuthenticationService#registerUser(UserInfoRequest)
+ */
+ @PostMapping("/auth/register.json")
+ public ResponseEntity<String> registerUser(
+ @RequestBody final UserInfoRequest userInfo) {
+ return new ResponseEntity<>(authenticationService.registerUser(userInfo));
+ }
+
+ /**
+ * The API-endpoint for verifying a newly created
+ * {@link org.psesquared.server.model.User}. This method is invoked via the
+ * link in the verification email that is sent in
+ * {@link #registerUser(UserInfoRequest)}.
+ * On success, it transfers the user to the dashboard and on failure it sets
+ * the following status codes:
+ * {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#BAD_REQUEST} user exists and is already verified,
+ * <br>
+ * {@link HttpStatus#UNAUTHORIZED} invalid token, <br>
+ * {@link HttpStatus#NOT_FOUND} user not found
+ *
+ * @param username The username of the user that needs to be verified
+ * @param token The JWT that indicates the authority of the request
+ * @param response The {@link HttpServletResponse} for setting up a
+ * redirection to the frontend
+ * @see AuthenticationService#verifyRegistration(String, String)
+ */
+ @GetMapping("/auth/{username}/verify.json")
+ public void verifyRegistration(
+ @PathVariable final String username,
+ @RequestParam("token") final String token,
+ @NonNull final HttpServletResponse response) {
+ HttpStatus status
+ = authenticationService.verifyRegistration(username, token);
+ if (status.equals(HttpStatus.OK)) {
+ response.setHeader(LOCATION_HEADER,
+ emailConfigProperties.dashboardBaseUrl());
+ response.setStatus(HttpStatus.FOUND.value());
+ } else {
+ response.setStatus(status.value());
+ }
+ }
+
+ /**
+ * The API-endpoint for setting a JWT access token with a lifespan of one hour
+ * as the "sessionid" cookie for authorization with further requests. <br>
+ * (This is a secured endpoint requiring authorization via HTTP basic or JWT.)
+ *
+ * @param username The username of the user who wants to log in
+ * @param response The {@link HttpServletResponse} for setting the "sessionid"
+ * cookie
+ * @return {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#NOT_FOUND} user not found
+ * @see AuthenticationService#login(String, HttpServletResponse)
+ */
+ @PostMapping("/auth/{username}/login.json")
+ public ResponseEntity<String> login(
+ @PathVariable final String username,
+ @NonNull final HttpServletResponse response) {
+ return new ResponseEntity<>(
+ authenticationService.login(username, response));
+ }
+
+ /**
+ * The API-endpoint for invalidating the "sessionid" cookie containing a JWT
+ * access token. Following authorized requests require HTTP basic
+ * authentication or a new login. <br>
+ * (This is a secured endpoint requiring authorization via HTTP basic or JWT.)
+ *
+ * @param username The username of the user who wants to log out
+ * @param response The {@link HttpServletResponse} for invalidating the
+ * "sessionid" cookie
+ * @return {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#NOT_FOUND} user not found
+ * @see AuthenticationService#logout(String, HttpServletResponse)
+ */
+ @PostMapping("/auth/{username}/logout.json")
+ public ResponseEntity<String> logout(
+ @PathVariable final String username,
+ @NonNull final HttpServletResponse response) {
+ return new ResponseEntity<>(
+ authenticationService.logout(username, response));
+ }
+
+ /**
+ * The API-endpoint for sending an email to the given address
+ * ({@link ForgotPasswordRequest#email()} with an url to reset the password of
+ * the user with that email address.
+ *
+ * @param email The email address of the user who wants to reset their
+ * password
+ * @return {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#NOT_FOUND} user not found
+ * @see AuthenticationService#forgotPassword(String)
+ */
+ @PostMapping("/auth/{email}/forgot.json")
+ public ResponseEntity<String> forgotPassword(
+ @PathVariable final String email) {
+ return new ResponseEntity<>(authenticationService.forgotPassword(email));
+ }
+
+ /**
+ * The API-endpoint for resetting the password of a
+ * {@link org.psesquared.server.model.User}. This method is invoked via the
+ * link in the verification email that is sent in
+ * {@link #forgotPassword(String)}.
+ *
+ * @param username The username of the user who wants to reset their
+ * password
+ * @param token The JWT that indicates the authority of the request
+ * @param requestBody The request-wrapper containing the new password
+ * @return {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#BAD_REQUEST} password doesn't meet requirements,
+ * <br>
+ * {@link HttpStatus#UNAUTHORIZED} invalid token, <br>
+ * {@link HttpStatus#NOT_FOUND} user not found
+ * @see AuthenticationService#resetPassword(String, String, PasswordRequest)
+ */
+ @PutMapping("/auth/{username}/resetpassword.json")
+ public ResponseEntity<String> resetPassword(
+ @PathVariable final String username,
+ @RequestParam("token") final String token,
+ @RequestBody final PasswordRequest requestBody) {
+ return new ResponseEntity<>(
+ authenticationService.resetPassword(username, token, requestBody));
+ }
+
+ /**
+ * The API-endpoint for changing the password of a
+ * {@link org.psesquared.server.model.User}, who is logged-in in the
+ * dashboard.
+ * (This is a secured endpoint requiring authorization via HTTP basic or JWT.)
+ *
+ * @param username The username of the user who wants to change their
+ * password
+ * @param requestBody The request-wrapper containing old and new password
+ * @return {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#BAD_REQUEST} old password is wrong, <br>
+ * {@link HttpStatus#NOT_FOUND} user not found
+ * @see AuthenticationService#changePassword(String, ChangePasswordRequest)
+ */
+ @PutMapping("/auth/{username}/changepassword.json")
+ public ResponseEntity<String> changePassword(
+ @PathVariable final String username,
+ @RequestBody final ChangePasswordRequest requestBody) {
+ return new ResponseEntity<>(
+ authenticationService.changePassword(username, requestBody));
+ }
+
+ /**
+ * The API-endpoint for deleting a {@link org.psesquared.server.model.User}.
+ * This action is performed by a logged-in user from the dashboard.
+ * The user must enter their password ({@link PasswordRequest#password()})
+ * and if correct, the user along with all associated data is deleted.
+ * (This is a secured endpoint requiring authorization via HTTP basic or JWT.)
+ *
+ * @param username The username of the user who wants to delete their
+ * account
+ * @param requestBody The request-wrapper containing the user's password
+ * @return {@link HttpStatus#OK} on success, <br>
+ * {@link HttpStatus#BAD_REQUEST} wrong password, <br>
+ * {@link HttpStatus#NOT_FOUND} user not found
+ * @see AuthenticationService#deleteUser(String, PasswordRequest)
+ */
+ @DeleteMapping("/auth/{username}/delete.json")
+ public ResponseEntity<String> deleteUser(
+ @PathVariable final String username,
+ @RequestBody final PasswordRequest requestBody) {
+ return new ResponseEntity<>(
+ authenticationService.deleteUser(username, requestBody));
+ }
+
+ /**
+ * This API-endpoint exists for compatibility with podcatchers, especially
+ * AntennaPod and Kasts, which initially call this endpoint instead of
+ * {@link #login(String, HttpServletResponse)}.
+ * Accordingly, a call to this endpoint is internally treated as a login.
+ * In particular, devices remain unsupported.
+ *
+ * @param username The username of the user to be synchronized
+ * @param response The {@link HttpServletResponse} for setting the "sessionid"
+ * cookie
+ * @return A dummy response with a single dummy device for the given user
+ * @see AuthenticationService#login(String, HttpServletResponse)
+ */
+ @GetMapping("/devices/{username}.json")
+ public ResponseEntity<List<DeviceWrapper>> getDeviceList(
+ @PathVariable final String username,
+ @NonNull final HttpServletResponse response) {
+ DeviceWrapper dummyDevice = new DeviceWrapper();
+ return new ResponseEntity<>(
+ List.of(dummyDevice),
+ authenticationService.login(username, response));
+ }
+
+}
diff --git a/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ChangePasswordRequest.java b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ChangePasswordRequest.java
new file mode 100644
index 0000000..d8b2357
--- /dev/null
+++ b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ChangePasswordRequest.java
@@ -0,0 +1,15 @@
+package org.psesquared.server.authentication.api.controller;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * A request for changing the password containing the old, i.e. current, and new
+ * password.
+ *
+ * @param oldPassword The user's current password
+ * @param newPassword The new password
+ */
+public record ChangePasswordRequest(
+ @JsonProperty(value = "password", required = true) String oldPassword,
+ @JsonProperty(value = "new_password", required = true) String newPassword) {
+}
diff --git a/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/DeviceWrapper.java b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/DeviceWrapper.java
new file mode 100644
index 0000000..35dae3d
--- /dev/null
+++ b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/DeviceWrapper.java
@@ -0,0 +1,46 @@
+package org.psesquared.server.authentication.api.controller;
+
+/**
+ * This record wraps a dummy device that is required to be returned by <br>
+ * {@link AuthenticationController#getDeviceList(String,
+ * jakarta.servlet.http.HttpServletResponse)}.
+ *
+ * @param id The device id
+ * @param caption The caption, i.e. name, of the device
+ * @param type The device type
+ * @param subscriptions The number of subscriptions of the device
+ */
+public record DeviceWrapper(
+ String id,
+ String caption,
+ String type,
+ int subscriptions) {
+
+ /**
+ * The id of the dummy device.
+ */
+ private static final String DUMMY_ID = "dummy";
+
+ /**
+ * The name of the dummy device.
+ */
+ private static final String DUMMY_DEVICE = "device";
+
+ /**
+ * The type of the dummy device.
+ */
+ private static final String DUMMY_TYPE = "other";
+
+ /**
+ * The number of subscriptions of the dummy device.
+ */
+ private static final int DUMMY_SUBSCRIPTIONS = 0;
+
+ /**
+ * The no-args-constructor for a device-wrapper containing a dummy device.
+ */
+ public DeviceWrapper() {
+ this(DUMMY_ID, DUMMY_DEVICE, DUMMY_TYPE, DUMMY_SUBSCRIPTIONS);
+ }
+
+}
diff --git a/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ForgotPasswordRequest.java b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ForgotPasswordRequest.java
new file mode 100644
index 0000000..700fb08
--- /dev/null
+++ b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/ForgotPasswordRequest.java
@@ -0,0 +1,13 @@
+package org.psesquared.server.authentication.api.controller;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * A request for sending an email with a link for resetting a user's password to
+ * the user's {@link #email} address.
+ *
+ * @param email The email address
+ */
+public record ForgotPasswordRequest(
+ @JsonProperty(required = true) String email) {
+}
diff --git a/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/PasswordRequest.java b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/PasswordRequest.java
new file mode 100644
index 0000000..f772c7b
--- /dev/null
+++ b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/PasswordRequest.java
@@ -0,0 +1,13 @@
+package org.psesquared.server.authentication.api.controller;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * A request that contains a {@link #password}, which is either set as a new
+ * password or used for confirming the deletion of an account.
+ *
+ * @param password The password
+ */
+public record PasswordRequest(
+ @JsonProperty(required = true) String password) {
+}
diff --git a/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/UserInfoRequest.java b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/UserInfoRequest.java
new file mode 100644
index 0000000..9c112b1
--- /dev/null
+++ b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/UserInfoRequest.java
@@ -0,0 +1,16 @@
+package org.psesquared.server.authentication.api.controller;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * A request that contains the {@link #username}, {@link #email} address and
+ * {@link #password} for registering a new user.
+ *
+ * @param username The username
+ * @param email The email
+ * @param password The password
+ */
+public record UserInfoRequest(@JsonProperty(required = true) String username,
+ @JsonProperty(required = true) String email,
+ @JsonProperty(required = true) String password) {
+}
diff --git a/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/package-info.java b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/package-info.java
new file mode 100644
index 0000000..79ae33d
--- /dev/null
+++ b/pse-server/src/main/java/org/psesquared/server/authentication/api/controller/package-info.java
@@ -0,0 +1,13 @@
+/**
+ * This package represents the highest logical layer of the authentication API
+ * ({@link org.psesquared.server.authentication.api}) - the controller layer.
+ * <br>
+ * It contains the
+ * {@link
+ * org.psesquared.server.authentication.api.controller.AuthenticationController}
+ * along with a series of wrapper classes for JSON request and response bodies.
+ *
+ * @author PSE-Squared Team
+ * @version 1.0
+ */
+package org.psesquared.server.authentication.api.controller;