Skip to content

Commit

Permalink
feat(docs): added docs
Browse files Browse the repository at this point in the history
  • Loading branch information
zZHorizonZz committed Aug 19, 2024
1 parent ae4fe08 commit b9da6a1
Show file tree
Hide file tree
Showing 76 changed files with 12,663 additions and 98 deletions.
4 changes: 4 additions & 0 deletions .github/project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name: Cloudeko Zenei
release:
current-version: 0.0.1
next-version: 0.0.1-SNAPSHOT
33 changes: 33 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Deploy Docs to GitHub Pages

on:
push:
branches: [ main ]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout your repository using git
uses: actions/checkout@v4
- name: Install, build, and upload your site
uses: withastro/action@v2
with:
path: ./docs

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ It's built with [Quarkus](https://quarkus.io) and provides authentication via OA

- [x] Basic user authentication
- [ ] Admin user management
- [ ] OAuth2 support (Github, Google, Facebook, etc.)
- [X] OAuth2 support (Github, Google, Facebook, etc.)
- [ ] Organizations management
- [ ] API/Access token generation

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.cloudeko.zenei.application.web.model.request;

import jakarta.ws.rs.FormParam;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginRequest {
@FormParam("identifier")
private String identifier;
@FormParam("password")
private String password;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SignupRequest {
public class RegisterRequest {

@FormParam("username")
private String username;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.cloudeko.zenei.application.web.resource;
package dev.cloudeko.zenei.application.web.resource.backend;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.cloudeko.zenei.application.web.resource;
package dev.cloudeko.zenei.application.web.resource.backend;

import dev.cloudeko.zenei.application.web.model.response.ExternalAccessTokenResponse;
import dev.cloudeko.zenei.application.web.model.response.ExternalAccessTokensResponse;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
package dev.cloudeko.zenei.application.web.resource;
package dev.cloudeko.zenei.application.web.resource.frontend;

import dev.cloudeko.zenei.application.web.model.request.SignupRequest;
import dev.cloudeko.zenei.application.web.model.request.LoginRequest;
import dev.cloudeko.zenei.application.web.model.request.RegisterRequest;
import dev.cloudeko.zenei.application.web.model.response.PrivateUserResponse;
import dev.cloudeko.zenei.application.web.model.response.TokenResponse;
import dev.cloudeko.zenei.domain.feature.*;
import dev.cloudeko.zenei.domain.model.email.EmailAddressInput;
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;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;
import lombok.AllArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

import java.net.URI;

@Path("/user")
@Path("/frontend")
@AllArgsConstructor
@Tag(name = "User Service", description = "API for user authentication")
public class UserResource {
@Tag(name = "Email Password Service", description = "API for user authentication with email and password")
public class EmailPasswordResource {

private final ApplicationConfig applicationConfig;

Expand All @@ -39,21 +36,12 @@ public class UserResource {
private final LoginUserWithPassword loginUserWithPassword;
private final LoginUserWithAuthorizationCode loginUserWithAuthorizationCode;

@GET
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Response getCurrentUserInfo(@Context SecurityContext securityContext) {
final var userId = Long.parseLong(securityContext.getUserPrincipal().getName());
final var user = findUserByIdentifier.handle(userId);

return Response.ok(new PrivateUserResponse(user)).build();
}

@POST
@Transactional
@Path("/register")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response signup(@BeanParam @Valid SignupRequest request) {
public Response register(@BeanParam @Valid RegisterRequest request) {
if (!applicationConfig.getSignUpEnabled()) {
return Response.status(Response.Status.FORBIDDEN).build();
}
Expand All @@ -72,6 +60,15 @@ public Response signup(@BeanParam @Valid SignupRequest request) {
return Response.ok(new PrivateUserResponse(user)).build();
}

@POST
@Transactional
@Path("/login")
@Produces(MediaType.APPLICATION_JSON)
public Response login(@BeanParam @Valid LoginRequest request) {
final var token = loginUserWithPassword.handle(new LoginPasswordInput(request.getIdentifier(), request.getPassword()));
return Response.ok(new TokenResponse(token)).build();
}

@POST
@Transactional
@Path("/verify-email")
Expand All @@ -84,25 +81,4 @@ public Response verifyEmail(@QueryParam("token") String token, @QueryParam("redi

return Response.noContent().build();
}

@POST
@Transactional
@Path("/token")
@Produces(MediaType.APPLICATION_JSON)
public Response token(@QueryParam("grant_type") String grantType,
@QueryParam("username") String username,
@QueryParam("password") String password,
@QueryParam("refresh_token") String refreshToken) {
return switch (grantType) {
case "password" -> {
final var token = loginUserWithPassword.handle(new LoginPasswordInput(username, password));
yield Response.ok(new TokenResponse(token)).build();
}
case "refresh_token" -> {
final var token = refreshAccessToken.handle(new RefreshTokenInput(refreshToken));
yield Response.ok(new TokenResponse(token)).build();
}
default -> Response.status(Response.Status.BAD_REQUEST).build();
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.cloudeko.zenei.application.web.resource;
package dev.cloudeko.zenei.application.web.resource.frontend;

import dev.cloudeko.zenei.application.web.model.response.TokenResponse;
import dev.cloudeko.zenei.domain.feature.LoginUserWithAuthorizationCode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dev.cloudeko.zenei.application.web.resource.frontend;

import dev.cloudeko.zenei.application.web.model.response.TokenResponse;
import dev.cloudeko.zenei.domain.feature.*;
import dev.cloudeko.zenei.domain.model.token.LoginPasswordInput;
import dev.cloudeko.zenei.domain.model.token.RefreshTokenInput;
import dev.cloudeko.zenei.infrastructure.config.ApplicationConfig;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.AllArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

@Path("/frontend/session")
@AllArgsConstructor
@Tag(name = "Email Password Service", description = "API for user authentication with email and password")
public class SessionResource {

private final ApplicationConfig applicationConfig;
private final RefreshAccessToken refreshAccessToken;

@POST
@Transactional
@Path("/refresh")
@Produces(MediaType.APPLICATION_JSON)
public Response token(@QueryParam("refresh_token") String refreshToken) {
final var token = refreshAccessToken.handle(new RefreshTokenInput(refreshToken));
return Response.ok(new TokenResponse(token)).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.cloudeko.zenei.application.web.resource.frontend;

import dev.cloudeko.zenei.application.web.model.request.RegisterRequest;
import dev.cloudeko.zenei.application.web.model.response.PrivateUserResponse;
import dev.cloudeko.zenei.application.web.model.response.TokenResponse;
import dev.cloudeko.zenei.domain.feature.*;
import dev.cloudeko.zenei.domain.model.email.EmailAddressInput;
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;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;
import lombok.AllArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

import java.net.URI;

@Path("/frontend/user")
@AllArgsConstructor
@Tag(name = "User Service", description = "API for user authentication")
public class UserResource {

private final ApplicationConfig applicationConfig;
private final FindUserByIdentifier findUserByIdentifier;

@GET
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Response getCurrentUserInfo(@Context SecurityContext securityContext) {
final var userId = Long.parseLong(securityContext.getUserPrincipal().getName());
final var user = findUserByIdentifier.handle(userId);

return Response.ok(new PrivateUserResponse(user)).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class SendMagicLinkVerifyEmailImpl implements SendMagicLinkVerifyEmail {
@Override
public void handle(EmailAddressInput input) {
final var content = mailTemplateProvider.defaultCreateConfirmationMailTemplate(
"http://localhost:8080/user/verify-email",
"http://localhost:8080/frontend/verify-email",
input.getEmailAddress().getEmailVerificationToken());
final var mail = Mail.withHtml(input.getEmailAddress().getEmail(), "Welcome to Zenei", content);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import dev.cloudeko.zenei.profile.DefaultAdminUserProfile;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
Expand All @@ -23,10 +24,9 @@ public class AuthenticationFlowWithAdminUserTest {
@DisplayName("Retrieve all users (GET /admin/users) should return (200 OK)")
void testGetUserInfo() {
final var token = given()
.queryParam("grant_type", "password")
.queryParam("username", "[email protected]")
.queryParam("password", "test")
.post("/user/token")
.formParam("identifier", "[email protected]")
.formParam("password", "test")
.post("/frontend/login")
.then()
.statusCode(Response.Status.OK.getStatusCode())
.extract().as(TokenResponse.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ static void setup() {
@DisplayName("Create user via email and password (POST /user) should return (200 OK)")
void testCreateUser() {
given()
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.formParam("username", "test-user2")
.formParam("email", "[email protected]")
.formParam("password", "test-password")
.formParam("strategy", "PASSWORD")
.post("/user")
.post("/frontend/register")
.then()
.statusCode(Response.Status.OK.getStatusCode())
.body(
Expand All @@ -55,17 +54,16 @@ void testCreateUser() {
@DisplayName("Retrieve user information (GET /user) should return (200 OK)")
void testGetUserInfo() {
final var token = given()
.queryParam("grant_type", "password")
.queryParam("username", "[email protected]")
.queryParam("password", "test-password")
.post("/user/token")
.formParam("identifier", "[email protected]")
.formParam("password", "test-password")
.post("/frontend/login")
.then()
.statusCode(Response.Status.OK.getStatusCode())
.extract().as(TokenResponse.class);

given()
.header("Authorization", "Bearer " + token.getAccessToken())
.get("/user")
.get("/frontend/user")
.then()
.statusCode(Response.Status.OK.getStatusCode())
.body(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ static void setup() {
@DisplayName("Create user via email and password (POST /user) should return (403 FORBIDDEN)")
void testCreateUser() {
given()
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.formParam("username", "test-user2")
.formParam("email", "[email protected]")
.formParam("password", "test-password")
.formParam("strategy", "PASSWORD")
.post("/user")
.post("/frontend/register")
.then()
.statusCode(Response.Status.FORBIDDEN.getStatusCode());
}
Expand Down
Loading

0 comments on commit b9da6a1

Please sign in to comment.