diff --git a/src/main/java/dev/cloudeko/zenei/application/web/model/request/SignupRequest.java b/src/main/java/dev/cloudeko/zenei/application/web/model/request/SignupRequest.java index 3e26d9f..e5dbb4e 100644 --- a/src/main/java/dev/cloudeko/zenei/application/web/model/request/SignupRequest.java +++ b/src/main/java/dev/cloudeko/zenei/application/web/model/request/SignupRequest.java @@ -29,7 +29,7 @@ public class SignupRequest { @FormParam("redirect_to") private URI redirectTo; - public CreateUserInput toCreateUserInput() { - return new CreateUserInput(username, email, strategy.equals(Strategy.PASSWORD), password); + public CreateUserInput toCreateUserInput(boolean autoconfirm) { + return new CreateUserInput(username, email, autoconfirm, strategy.equals(Strategy.PASSWORD), password); } } diff --git a/src/main/java/dev/cloudeko/zenei/application/web/resource/OAuthResource.java b/src/main/java/dev/cloudeko/zenei/application/web/resource/OAuthResource.java new file mode 100644 index 0000000..0bf6d24 --- /dev/null +++ b/src/main/java/dev/cloudeko/zenei/application/web/resource/OAuthResource.java @@ -0,0 +1,56 @@ +package dev.cloudeko.zenei.application.web.resource; + +import dev.cloudeko.zenei.infrastructure.config.ApplicationConfig; +import dev.cloudeko.zenei.infrastructure.config.OAuthProviderConfig; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import lombok.AllArgsConstructor; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +@Path("/oauth") +@AllArgsConstructor +@Tag(name = "OAuth Service", description = "OAuth service used for authentication") +public class OAuthResource { + + private final ApplicationConfig config; + + @GET + @Path("/login/{provider}") + public Response login(@PathParam("provider") String provider) { + final var providerConfig = getProviderConfig(provider); + + if (providerConfig == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + + final var uriBuilder = UriBuilder.fromUri(providerConfig.authorizationUri()) + .queryParam("client_id", providerConfig.clientId()) + .queryParam("redirect_uri", providerConfig.redirectUri()) + .queryParam("response_type", "code") + .queryParam("scope", "openid profile email"); + + return Response.temporaryRedirect(uriBuilder.build()).build(); + } + + @GET + @Path("/callback/{provider}") + public Response callback(@PathParam("provider") String provider, + @QueryParam("code") String code, + @QueryParam("state") String state) { + final var providerConfig = getProviderConfig(provider); + + if (providerConfig == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + + return Response.ok("Received auth code: " + code).build(); + } + + private OAuthProviderConfig getProviderConfig(String provider) { + return config.getOAuthProvidersConfig().providers().get(provider); + } +} diff --git a/src/main/java/dev/cloudeko/zenei/application/web/resource/UserResource.java b/src/main/java/dev/cloudeko/zenei/application/web/resource/UserResource.java index c473251..56be75b 100644 --- a/src/main/java/dev/cloudeko/zenei/application/web/resource/UserResource.java +++ b/src/main/java/dev/cloudeko/zenei/application/web/resource/UserResource.java @@ -8,6 +8,7 @@ import dev.cloudeko.zenei.domain.model.email.VerifyMagicLinkInput; import dev.cloudeko.zenei.domain.model.token.LoginPasswordInput; import dev.cloudeko.zenei.domain.model.token.RefreshTokenInput; +import dev.cloudeko.zenei.infrastructure.config.ApplicationConfig; import io.quarkus.security.Authenticated; import jakarta.transaction.Transactional; import jakarta.validation.Valid; @@ -26,6 +27,8 @@ @Tag(name = "User Service", description = "API for user authentication") public class UserResource { + private final ApplicationConfig applicationConfig; + private final CreateUser createUser; private final FindUserByIdentifier findUserByIdentifier; @@ -49,10 +52,14 @@ public Response getCurrentUserInfo(@Context SecurityContext securityContext) { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response signup(@BeanParam @Valid SignupRequest request) { - final var user = createUser.handle(request.toCreateUserInput()); + if (!applicationConfig.getSignUpEnabled()) { + return Response.status(Response.Status.FORBIDDEN).build(); + } + + final var user = createUser.handle(request.toCreateUserInput(applicationConfig.getAutoConfirm())); final var emailAddress = user.getPrimaryEmailAddress(); - if (!emailAddress.getEmailVerified() && emailAddress.getEmailVerificationToken() != null){ + if (!emailAddress.getEmailVerified() && emailAddress.getEmailVerificationToken() != null) { sendMagicLinkVerifyEmail.handle(new EmailAddressInput(emailAddress)); } diff --git a/src/main/java/dev/cloudeko/zenei/domain/feature/CreateDefaultUser.java b/src/main/java/dev/cloudeko/zenei/domain/feature/CreateDefaultUser.java index 73abbcd..5d1b55f 100644 --- a/src/main/java/dev/cloudeko/zenei/domain/feature/CreateDefaultUser.java +++ b/src/main/java/dev/cloudeko/zenei/domain/feature/CreateDefaultUser.java @@ -2,6 +2,21 @@ import dev.cloudeko.zenei.infrastructure.config.DefaultUserConfig; +/** + * The {@code CreateDefaultUser} interface represents a contract for creating a default user based on a configuration. It + * defines a single method {@code handle} which takes a {@link DefaultUserConfig} object as a parameter and performs the + * necessary operations to create a default user based on the provided configuration. + *
+ * Implementations of this interface should handle the validation and creation of a default user based on the given + * configuration. If a user with the same email or username already exists, no action should be taken. + */ public interface CreateDefaultUser { + /** + * The {@code handle} method is used to create a default user based on a given configuration. + * + * @param config The {@link DefaultUserConfig} object that contains the user configuration, including email, username, + * password, and an optional role. + * @apiNote If a user with the same email or username already exists, no action should be taken. + */ void handle(DefaultUserConfig config); } diff --git a/src/main/java/dev/cloudeko/zenei/domain/feature/FindUserByIdentifier.java b/src/main/java/dev/cloudeko/zenei/domain/feature/FindUserByIdentifier.java index ea599b6..9ba4bcd 100644 --- a/src/main/java/dev/cloudeko/zenei/domain/feature/FindUserByIdentifier.java +++ b/src/main/java/dev/cloudeko/zenei/domain/feature/FindUserByIdentifier.java @@ -2,6 +2,24 @@ import dev.cloudeko.zenei.domain.model.user.User; +/** + * The {@code FindUserByIdentifier} interface represents a contract for finding a User object based on a provided identifier. It + * defines a single method {@code handle} which takes a {@link Long} identifier as a parameter and returns the corresponding + * {@link User} object if found. + * + *
Implementations of this interface should handle the retrieval of a User object based on the given identifier. If a user + * with the provided identifier is found, the corresponding + * User object should be returned. Otherwise, an appropriate exception should be thrown.
+ * + * @see User + */ public interface FindUserByIdentifier { - User handle(Long identifier); + /** + * The {@code handle} method handles the retrieval of a User object based on the provided identifier. It takes a Long + * identifier as a parameter and returns the corresponding User object if found. + * + * @param identifier the identifier of the user + * @return the User object corresponding to the provided identifier + */ + User handle(long identifier); } diff --git a/src/main/java/dev/cloudeko/zenei/domain/feature/ListUsers.java b/src/main/java/dev/cloudeko/zenei/domain/feature/ListUsers.java index 6995df6..f2768b2 100644 --- a/src/main/java/dev/cloudeko/zenei/domain/feature/ListUsers.java +++ b/src/main/java/dev/cloudeko/zenei/domain/feature/ListUsers.java @@ -4,6 +4,23 @@ import java.util.List; +/** + * The {@code ListUsers} interface represents a contract for retrieving a list of users based on the provided offset and limit. + * It defines a single method {@code listUsers} which takes the offset and limit as parameters and returns a list of User + * objects. + *
+ * Implementations of this interface should handle the logic for retrieving the users from a data source and returning the
+ * list.
+ *
+ * @see User
+ */
public interface ListUsers {
+ /**
+ * Retrieves a list of users based on the provided offset and limit.
+ *
+ * @param offset the starting position of the users in the list (inclusive)
+ * @param limit the maximum number of users to retrieve
+ * @return a {@code List} of {@link User} objects representing the users in the specified range
+ */
List
+ * Implementations of this interface should handle the authentication process by verifying the user's password. If the password
+ * is valid, a token should be generated and returned . Otherwise, an appropriate exception should be thrown.
+ * Implementations of this method should handle the authentication process by verifying the user's password. If the
+ * password
+ * is valid, a token should be generated and returned. Otherwise, an appropriate exception should be thrown. Implementations of this interface should handle the validation and generation of a new access token based on the provided
+ * refresh token. If the refresh token is invalid or expired, an appropriate exception should be thrown. Otherwise, a new access
+ * token should be generated and returned. Implementations of this method should handle the validation and generation of a new access token based on the provided
+ * refresh token.
+ * If the refresh token is invalid or expired, an appropriate exception should be thrown. Otherwise, a new access token
+ * should be generated and returned.